@skroz/profile-api 1.0.21 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +197 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# @skroz/profile-api
|
|
2
|
+
|
|
3
|
+
GraphQL-резолверы и сервисы для аутентификации пользователей. Построена на type-graphql + TypeORM + Redis.
|
|
4
|
+
|
|
5
|
+
## Архитектура
|
|
6
|
+
|
|
7
|
+
Библиотека предоставляет **фабрики резолверов** — функции, которые принимают зависимости и возвращают класс резолвера для регистрации в GraphQL-схеме. Это позволяет переиспользовать логику в разных приложениях с разными User-сущностями.
|
|
8
|
+
|
|
9
|
+
## Билд
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd libs/utils/packages/profile-api
|
|
13
|
+
yarn build
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Экспорты
|
|
17
|
+
|
|
18
|
+
### Типы
|
|
19
|
+
|
|
20
|
+
- **`AuthUser`** — интерфейс пользователя: `id`, `email`, `name`, `avatar`, `password`, `isEmailConfirmed`, `isBanned`, `isTempPassword`, `urlSlug`, `telegramId`, `isEmailNotificationEnabled`, `isTelegramNotificationEnabled`, `lastSeenAt`, `save()`
|
|
21
|
+
- **`ProfileDbAdapter`** — интерфейс БД-адаптера, который нужно реализовать: `findUserByEmail`, `findUserById`, `findUserByTelegramId`, `createUser`, `isEmailTaken`, `findUserByProviderId`, `updateUserProviderId`
|
|
22
|
+
- **`ProfileAuthConfig`** — конфиг токенов/лимитов: `resendEmailLimitSeconds`, `confirmationTokenLifetimeMinutes`, `recoveryTokenLifetimeMinutes`
|
|
23
|
+
- **`EmailConfig`** — extends ProfileAuthConfig + настройки email: `domain`, `websiteUrl`, `primaryBrandColor`, `logoUrl`, `fromEmailUsername`, `templateDir`, `isOnlineSeconds`, `isOnlineRecentlySeconds`
|
|
24
|
+
- **`ProfileEmailTemplate`** — enum шаблонов писем: `CONFIRM_EMAIL`, `FORGOT_PASSWORD`, `TEMP_PASSWORD`, `GENERIC_NOTIFICATION`
|
|
25
|
+
- **`ProfileLocales`** — структура переводов для auth-ошибок и email-шаблонов
|
|
26
|
+
- **`ProfileContext<TUser>`** — GraphQL-контекст: `user?`, `req`, `res`, `t`
|
|
27
|
+
|
|
28
|
+
### TypeOrmProfileAdapter
|
|
29
|
+
|
|
30
|
+
Готовая реализация `ProfileDbAdapter` поверх TypeORM.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { TypeOrmProfileAdapter } from '@skroz/profile-api';
|
|
34
|
+
|
|
35
|
+
const db = new TypeOrmProfileAdapter(() =>
|
|
36
|
+
getConnection().getRepository(User)
|
|
37
|
+
);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Провайдер-ID ищет по полю `${provider}Id` (т.е. `googleId`, `vkId`, `telegramId` и т.д.) — эти поля должны быть в сущности User.
|
|
41
|
+
|
|
42
|
+
### TypeOrmBaseUser
|
|
43
|
+
|
|
44
|
+
Базовая TypeORM-сущность со всеми полями `AuthUser`. Можно наследовать:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { TypeOrmBaseUser } from '@skroz/profile-api';
|
|
48
|
+
|
|
49
|
+
@Entity()
|
|
50
|
+
export class User extends TypeOrmBaseUser {
|
|
51
|
+
// дополнительные поля
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### ProfileAuthService
|
|
56
|
+
|
|
57
|
+
Основной сервис. Принимает `(db, email, redis, config)`.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { ProfileAuthService } from '@skroz/profile-api';
|
|
61
|
+
|
|
62
|
+
const authService = new ProfileAuthService(db, emailService, redis, {
|
|
63
|
+
resendEmailLimitSeconds: 60,
|
|
64
|
+
confirmationTokenLifetimeMinutes: 60,
|
|
65
|
+
recoveryTokenLifetimeMinutes: 30,
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Публичные поля: `db`, `email`, `redis`, `config`.
|
|
70
|
+
Методы: `hashPassword`, `verifyPassword`, `setTokenToRedis`, `removeTokenFromRedis`, `getUserByToken`, `sendLink`.
|
|
71
|
+
|
|
72
|
+
### ProfileEmailService
|
|
73
|
+
|
|
74
|
+
Отправка писем (confirm email, forgot password, temp password). Передаётся в `ProfileAuthService`.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Резолверы
|
|
79
|
+
|
|
80
|
+
Все резолверы создаются через фабричные функции. `authService` в deps может быть как экземпляром, так и функцией `(ctx) => ProfileAuthService` (для per-request сервисов).
|
|
81
|
+
|
|
82
|
+
### createAuthResolver(deps)
|
|
83
|
+
|
|
84
|
+
Auth-мутации: `register`, `login`, `logout`, `confirmEmail`, `sendToken`, `recoverPassword`.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { createAuthResolver } from '@skroz/profile-api';
|
|
88
|
+
|
|
89
|
+
const AuthResolver = createAuthResolver({
|
|
90
|
+
authService, // ProfileAuthService | (ctx) => ProfileAuthService
|
|
91
|
+
userType: User, // GraphQL ObjectType
|
|
92
|
+
onUserCreated: async (user, ctx) => { /* ... */ },
|
|
93
|
+
onLogin: async (user, ctx) => { /* ... */ },
|
|
94
|
+
onLogout: async (user, ctx) => { /* ... */ },
|
|
95
|
+
onEmailConfirmed: async (user, ctx) => { /* ... */ },
|
|
96
|
+
onPasswordRecovered: async (user, ctx) => { /* ... */ },
|
|
97
|
+
logTelegramBot: { sendError: async (msg) => { /* ... */ } },
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### createProfileResolver(deps)
|
|
102
|
+
|
|
103
|
+
Мутации профиля (только для авторизованных): `updateEmail`, `updatePassword`, `updateProfile`, `toggleEmailNotification`.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { createProfileResolver } from '@skroz/profile-api';
|
|
107
|
+
|
|
108
|
+
const ProfileResolver = createProfileResolver({ authService, userType: User });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### createOauthResolver(deps)
|
|
112
|
+
|
|
113
|
+
OAuth-авторизация и вход через Telegram-бот.
|
|
114
|
+
|
|
115
|
+
Мутации:
|
|
116
|
+
- `oauthLogin(input: OauthLoginInput)` — вход через Google/VK/Яндекс/Mail/Apple/Telegram-виджет
|
|
117
|
+
- `generateTelegramAuthToken` → `{ token, url }` — генерирует токен и deep link для бота
|
|
118
|
+
- `confirmTelegramAuthToken(token)` → `{ confirmed, expired }` — polling со стороны фронта
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { createOauthResolver, GoogleOauth, VKOauth } from '@skroz/profile-api';
|
|
122
|
+
|
|
123
|
+
const OauthResolver = createOauthResolver({
|
|
124
|
+
authService,
|
|
125
|
+
userType: User,
|
|
126
|
+
providers: {
|
|
127
|
+
google: new GoogleOauth(clientId, clientSecret, redirectUri),
|
|
128
|
+
vk: new VKOauth(clientId, clientSecret, redirectUri),
|
|
129
|
+
// ya, mail, apple — аналогично
|
|
130
|
+
},
|
|
131
|
+
telegramBotToken: process.env.TELEGRAM_BOT_TOKEN, // для Telegram Login Widget
|
|
132
|
+
telegramBotName: 'MyBot', // имя бота без @, для deep link авторизации
|
|
133
|
+
redis: getRedis(), // клиент с методами get/setex/del
|
|
134
|
+
onUserCreated: async (user, ctx) => { /* ... */ },
|
|
135
|
+
onLogin: async (user, ctx) => { /* ... */ },
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## confirmTelegramBotAuth
|
|
142
|
+
|
|
143
|
+
Вспомогательная функция для **Telegram-бота** (не для GraphQL-резолвера).
|
|
144
|
+
|
|
145
|
+
Когда пользователь переходит по deep link `/start tgauth_<token>`, бот вызывает эту функцию — она записывает `telegramUserId` в Redis, и фронт получает `confirmed: true` при следующем polling.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { confirmTelegramBotAuth } from '@skroz/profile-api';
|
|
149
|
+
|
|
150
|
+
// В обработчике команды /start в Telegram-боте:
|
|
151
|
+
if (text.startsWith('/start tgauth_')) {
|
|
152
|
+
const token = text.replace('/start tgauth_', '');
|
|
153
|
+
const confirmed = await confirmTelegramBotAuth(redis, token, user.id);
|
|
154
|
+
if (confirmed) {
|
|
155
|
+
// отправить пользователю сообщение об успешной авторизации
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Возвращает `true` если токен найден и подтверждён, `false` если истёк или не существует.
|
|
161
|
+
Использует тот же Redis-префикс (`skroz:profile:tgbotauth`) и TTL (300 сек), что и `generateTelegramAuthToken`.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## OAuth-провайдеры
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { GoogleOauth, VKOauth, YandexOauth, MailOauth, AppleOauth } from '@skroz/profile-api';
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Каждый провайдер реализует интерфейс `OAuthProvider`:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
interface OAuthProvider {
|
|
175
|
+
exchangeCode(code: string): Promise<OAuthProfile>;
|
|
176
|
+
readonly trustedEmail: boolean; // true = email верифицирован провайдером
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
interface OAuthProfile {
|
|
180
|
+
providerId: string;
|
|
181
|
+
email?: string | null;
|
|
182
|
+
name?: string | null;
|
|
183
|
+
avatarUrl?: string | null;
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`trustedEmail: true` у Google, VK, Яндекс, Mail, Apple — при входе через них `isEmailConfirmed` и `isEmailNotificationEnabled` автоматически выставляются в `true`.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## DTO
|
|
192
|
+
|
|
193
|
+
GraphQL input/output классы для использования в схеме:
|
|
194
|
+
|
|
195
|
+
`AuthInput`, `UpdateEmailInput`, `UpdatePasswordInput`, `UpdateProfileInput`,
|
|
196
|
+
`ConfirmEmailInput`, `RecoverPasswordInput`, `SendTokenInput`, `SendTokenPayload`,
|
|
197
|
+
`OauthLoginInput`, `TelegramAuthData`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skroz/profile-api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": "git@gitlab.com:skroz/libs/utils.git",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"type-graphql": "^1.1.1",
|
|
45
45
|
"typeorm": "^0.2.45"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "b76953b60d5790c28dd02476788130a62c255a94"
|
|
48
48
|
}
|