@skroz/profile-api 1.0.6 → 1.0.8
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/dist/dto/ConfirmEmailInput.d.ts +1 -1
- package/dist/dto/ConfirmEmailInput.js +3 -3
- package/dist/dto/ForgotPasswordInput.d.ts +1 -1
- package/dist/dto/ForgotPasswordInput.js +3 -3
- package/dist/dto/LoginInput.d.ts +2 -2
- package/dist/dto/LoginInput.js +38 -5
- package/dist/dto/PasswordInput.d.ts +1 -1
- package/dist/dto/PasswordInput.js +3 -3
- package/dist/dto/RecoverPasswordInput.d.ts +2 -2
- package/dist/dto/RecoverPasswordInput.js +38 -5
- package/dist/dto/RegisterInput.d.ts +2 -2
- package/dist/dto/RegisterInput.js +38 -5
- package/dist/dto/SendTokenPayload.d.ts +2 -2
- package/dist/dto/SendTokenPayload.js +3 -4
- package/dist/dto/StatusPayload.d.ts +1 -1
- package/dist/dto/StatusPayload.js +2 -3
- package/dist/dto/UpdateEmailInput.d.ts +1 -1
- package/dist/dto/UpdateEmailInput.js +3 -3
- package/dist/dto/UpdatePasswordInput.d.ts +2 -2
- package/dist/dto/UpdatePasswordInput.js +38 -5
- package/dist/dto/UpdateProfileInput.d.ts +1 -1
- package/dist/dto/UpdateProfileInput.js +3 -3
- package/dist/dto/index.d.ts +11 -11
- package/dist/dto/index.js +42 -24
- package/dist/email-templates/base/content.pug +32 -0
- package/dist/email-templates/base/header.pug +28 -0
- package/dist/email-templates/base/layout.pug +34 -0
- package/dist/email-templates/base/mixins.pug +16 -0
- package/dist/email-templates/confirmEmail.pug +16 -0
- package/dist/email-templates/forgotPassword.pug +16 -0
- package/dist/email-templates/notification.pug +14 -0
- package/dist/email-templates/tempPassword.pug +14 -0
- package/dist/resolvers/AuthResolver.d.ts +2 -25
- package/dist/resolvers/AuthResolver.js +28 -15
- package/dist/resolvers/ProfileResolver.d.ts +2 -19
- package/dist/resolvers/ProfileResolver.js +18 -5
- package/dist/services/ProfileEmailService.js +2 -2
- package/dist/types/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/dto/ConfirmEmailInput.ts +1 -1
- package/src/dto/ForgotPasswordInput.ts +1 -1
- package/src/dto/LoginInput.ts +2 -2
- package/src/dto/PasswordInput.ts +1 -1
- package/src/dto/RecoverPasswordInput.ts +2 -2
- package/src/dto/RegisterInput.ts +2 -2
- package/src/dto/SendTokenPayload.ts +2 -2
- package/src/dto/StatusPayload.ts +1 -1
- package/src/dto/UpdateEmailInput.ts +1 -1
- package/src/dto/UpdatePasswordInput.ts +2 -2
- package/src/dto/UpdateProfileInput.ts +1 -1
- package/src/dto/index.ts +11 -11
- package/src/email-templates/base/content.pug +32 -0
- package/src/email-templates/base/header.pug +28 -0
- package/src/email-templates/base/layout.pug +34 -0
- package/src/email-templates/base/mixins.pug +16 -0
- package/src/email-templates/confirmEmail.pug +16 -0
- package/src/email-templates/forgotPassword.pug +16 -0
- package/src/email-templates/notification.pug +14 -0
- package/src/email-templates/tempPassword.pug +14 -0
- package/src/resolvers/AuthResolver.ts +94 -38
- package/src/resolvers/ProfileResolver.ts +125 -91
- package/src/services/ProfileEmailService.ts +147 -147
- package/src/types/index.ts +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
table(
|
|
2
|
+
width='100%'
|
|
3
|
+
bgcolor='#ff0066'
|
|
4
|
+
border='0'
|
|
5
|
+
cellspacing='0'
|
|
6
|
+
cellpadding='0'
|
|
7
|
+
style={
|
|
8
|
+
'border-radius': '25px 25px 0 0',
|
|
9
|
+
'-webkit-border-radius': '25px 25px 0 0',
|
|
10
|
+
'-moz-border-radius': '25px 25px 0 0'
|
|
11
|
+
}
|
|
12
|
+
)
|
|
13
|
+
tr
|
|
14
|
+
td(
|
|
15
|
+
align='center'
|
|
16
|
+
height='60px'
|
|
17
|
+
style={
|
|
18
|
+
'line-height': '1',
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
a(href=websiteUrl)
|
|
22
|
+
img(
|
|
23
|
+
src=logoUrl
|
|
24
|
+
height='26px'
|
|
25
|
+
style={
|
|
26
|
+
'margin-top': '2px',
|
|
27
|
+
}
|
|
28
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
doctype html
|
|
2
|
+
html
|
|
3
|
+
head
|
|
4
|
+
meta(http-equiv='Content-Type' content='text/html; charset=utf-8')
|
|
5
|
+
meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
|
6
|
+
|
|
7
|
+
body(
|
|
8
|
+
style={
|
|
9
|
+
'margin': '0',
|
|
10
|
+
'padding': '0',
|
|
11
|
+
'width': '100% !important',
|
|
12
|
+
'height': '100% !important',
|
|
13
|
+
'max-width': '100%',
|
|
14
|
+
'font-family': '"Helvetica Neue", Helvetica, Arial, sans-serif',
|
|
15
|
+
'font-size': '16px',
|
|
16
|
+
'line-height': '1.5',
|
|
17
|
+
'color': '#222',
|
|
18
|
+
'border-collapse': 'collapse',
|
|
19
|
+
'-webkit-font-smoothing': 'antialiased',
|
|
20
|
+
'-webkit-text-size-adjust': 'none',
|
|
21
|
+
'-ms-text-size-adjust': '100%',
|
|
22
|
+
'border-spacing': '0'
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
table(
|
|
26
|
+
width='100%'
|
|
27
|
+
align='center'
|
|
28
|
+
border='0'
|
|
29
|
+
cellspacing='0'
|
|
30
|
+
cellpadding='0'
|
|
31
|
+
)
|
|
32
|
+
tr
|
|
33
|
+
td(align='center' valign='top')
|
|
34
|
+
include content
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
mixin header(title)
|
|
2
|
+
h1(style={
|
|
3
|
+
'font-size': '26px',
|
|
4
|
+
'font-weight': '500',
|
|
5
|
+
'margin-top': '20px',
|
|
6
|
+
'margin-bottom': '15px',
|
|
7
|
+
})= title
|
|
8
|
+
|
|
9
|
+
mixin link(href, title)
|
|
10
|
+
if href
|
|
11
|
+
a(
|
|
12
|
+
href=href
|
|
13
|
+
style={
|
|
14
|
+
'color': '#1890ff'
|
|
15
|
+
}
|
|
16
|
+
)= title
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
extends base/layout
|
|
2
|
+
include base/mixins
|
|
3
|
+
|
|
4
|
+
block content
|
|
5
|
+
//+header(header)
|
|
6
|
+
//p #{' ' + text}
|
|
7
|
+
//if (link)
|
|
8
|
+
// +link(link.href, link.label)
|
|
9
|
+
//div(style='margin-top: 30px;')
|
|
10
|
+
// strong #{mistakeHeader}
|
|
11
|
+
// br
|
|
12
|
+
// | #{mistakeText}
|
|
13
|
+
div(style='margin-top: 30px; background-color: white; border-radius: 25px; padding: 20px; text-align: center; font-size: 30px ')
|
|
14
|
+
strong #{confirmationToken}
|
|
15
|
+
div(style='margin-top: 30px; text-align: center; font-size: 25px ')
|
|
16
|
+
p #{header}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
extends base/layout
|
|
2
|
+
include base/mixins
|
|
3
|
+
|
|
4
|
+
block content
|
|
5
|
+
//+header(header)
|
|
6
|
+
//p #{' ' + text}
|
|
7
|
+
//if (link)
|
|
8
|
+
// +link(link.href, link.label)
|
|
9
|
+
//div(style='margin-top: 30px;')
|
|
10
|
+
// strong #{mistakeHeader}
|
|
11
|
+
// br
|
|
12
|
+
// | #{mistakeText}
|
|
13
|
+
div(style='margin-top: 30px; background-color: white; border-radius: 25px; padding: 20px; text-align: center; font-size: 30px ')
|
|
14
|
+
strong #{confirmationToken}
|
|
15
|
+
div(style='margin-top: 30px; text-align: center; font-size: 25px ')
|
|
16
|
+
p #{header}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
extends base/layout
|
|
2
|
+
include base/mixins
|
|
3
|
+
|
|
4
|
+
block content
|
|
5
|
+
+header(header)
|
|
6
|
+
p #{' ' + text}
|
|
7
|
+
if (link)
|
|
8
|
+
+link(link.href, link.label)
|
|
9
|
+
div(style='margin-top: 30px;')
|
|
10
|
+
strong #{mistakeHeader}
|
|
11
|
+
a(href=unsubscribeLink)
|
|
12
|
+
strong Отписаться
|
|
13
|
+
br
|
|
14
|
+
| #{mistakeText}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
extends base/layout
|
|
2
|
+
include base/mixins
|
|
3
|
+
|
|
4
|
+
block content
|
|
5
|
+
+header(header)
|
|
6
|
+
p #{' ' + text}
|
|
7
|
+
if (link)
|
|
8
|
+
+link(link.href, link.label)
|
|
9
|
+
div(style='margin-top: 30px;')
|
|
10
|
+
strong #{mistakeHeader}
|
|
11
|
+
a(href=unsubscribeLink)
|
|
12
|
+
strong Отписаться
|
|
13
|
+
br
|
|
14
|
+
| #{mistakeText}
|
|
@@ -1,27 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Arg,
|
|
3
|
-
Authorized,
|
|
4
|
-
Ctx,
|
|
5
|
-
Mutation,
|
|
6
|
-
Resolver,
|
|
7
|
-
} from 'type-graphql';
|
|
1
|
+
import { Arg, Authorized, Ctx, Mutation, Resolver } from 'type-graphql';
|
|
8
2
|
import { StatusPayload } from '@os-team/graphql-utils';
|
|
9
3
|
import { TransformArgs } from '@os-team/graphql-transformers';
|
|
10
4
|
import { ValidateArgs } from '@os-team/graphql-validators';
|
|
11
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ProfileAuthService,
|
|
7
|
+
CONFIRMATION_REDIS_PREFIX,
|
|
8
|
+
RECOVERY_REDIS_PREFIX,
|
|
9
|
+
} from '../services/ProfileAuthService';
|
|
12
10
|
import { ProfileContext } from '../types';
|
|
13
11
|
import {
|
|
14
|
-
RegisterInput,
|
|
15
|
-
|
|
12
|
+
RegisterInput,
|
|
13
|
+
registerTransformers,
|
|
14
|
+
registerValidators,
|
|
15
|
+
LoginInput,
|
|
16
|
+
loginTransformers,
|
|
17
|
+
loginValidators,
|
|
16
18
|
ConfirmEmailInput,
|
|
17
|
-
ForgotPasswordInput,
|
|
18
|
-
|
|
19
|
+
ForgotPasswordInput,
|
|
20
|
+
forgotPasswordTransformers,
|
|
21
|
+
forgotPasswordValidators,
|
|
22
|
+
RecoverPasswordInput,
|
|
23
|
+
recoverPasswordTransformers,
|
|
24
|
+
recoverPasswordValidators,
|
|
19
25
|
SendTokenPayload,
|
|
20
|
-
confirmEmailValidators
|
|
26
|
+
confirmEmailValidators,
|
|
21
27
|
} from '../dto';
|
|
22
28
|
|
|
23
|
-
export interface AuthResolverDependencies<
|
|
24
|
-
|
|
29
|
+
export interface AuthResolverDependencies<
|
|
30
|
+
TContext extends ProfileContext = ProfileContext
|
|
31
|
+
> {
|
|
32
|
+
authService: ProfileAuthService | (() => ProfileAuthService);
|
|
25
33
|
userType: any;
|
|
26
34
|
onUserCreated?: (user: any, ctx: TContext) => Promise<void>;
|
|
27
35
|
onLogin?: (user: any, ctx: TContext) => Promise<void>;
|
|
@@ -31,7 +39,9 @@ export interface AuthResolverDependencies<TContext extends ProfileContext = Prof
|
|
|
31
39
|
logTelegramBot?: { sendError: (msg: string) => Promise<any> };
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
export function createAuthResolver<
|
|
42
|
+
export function createAuthResolver<
|
|
43
|
+
TContext extends ProfileContext = ProfileContext
|
|
44
|
+
>(deps: AuthResolverDependencies<TContext>): any {
|
|
35
45
|
const {
|
|
36
46
|
authService,
|
|
37
47
|
onUserCreated,
|
|
@@ -40,9 +50,14 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
40
50
|
onEmailConfirmed,
|
|
41
51
|
onPasswordRecovered,
|
|
42
52
|
logTelegramBot,
|
|
43
|
-
userType
|
|
53
|
+
userType,
|
|
44
54
|
} = deps;
|
|
45
55
|
|
|
56
|
+
const getAuthService = (): ProfileAuthService => {
|
|
57
|
+
if (typeof authService === 'function') return authService();
|
|
58
|
+
return authService;
|
|
59
|
+
};
|
|
60
|
+
|
|
46
61
|
@Resolver(() => userType)
|
|
47
62
|
class AuthResolver {
|
|
48
63
|
@TransformArgs(registerTransformers, { arg: 'input' })
|
|
@@ -50,18 +65,20 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
50
65
|
@Mutation(() => StatusPayload)
|
|
51
66
|
async register(@Arg('input') input: RegisterInput, @Ctx() ctx: TContext) {
|
|
52
67
|
const { t } = ctx;
|
|
53
|
-
|
|
68
|
+
const service = getAuthService();
|
|
69
|
+
if (await service.db.isEmailTaken(input.email)) {
|
|
54
70
|
throw new Error(t('validation:auth.emailExists'));
|
|
55
71
|
}
|
|
56
72
|
|
|
57
|
-
const passwordHash = await
|
|
58
|
-
const user = await
|
|
73
|
+
const passwordHash = await service.hashPassword(input.password);
|
|
74
|
+
const user = await service.db.createUser({
|
|
59
75
|
email: input.email,
|
|
60
76
|
passwordHash,
|
|
61
77
|
});
|
|
62
78
|
|
|
63
79
|
if (onUserCreated) await onUserCreated(user, ctx);
|
|
64
|
-
if (logTelegramBot)
|
|
80
|
+
if (logTelegramBot)
|
|
81
|
+
await logTelegramBot.sendError(`Новый пользователь ${input.email}`);
|
|
65
82
|
|
|
66
83
|
// Создаем сессию
|
|
67
84
|
const userAgent = decodeURI(ctx.req.get('user-agent') || '');
|
|
@@ -79,12 +96,16 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
79
96
|
@Mutation(() => StatusPayload)
|
|
80
97
|
async login(@Arg('input') input: LoginInput, @Ctx() ctx: TContext) {
|
|
81
98
|
const { t } = ctx;
|
|
82
|
-
const
|
|
99
|
+
const service = getAuthService();
|
|
100
|
+
const user = await service.db.findUserByEmail(input.email);
|
|
83
101
|
if (!user) throw new Error(t('validation:user.notFound'));
|
|
84
102
|
|
|
85
103
|
if (user.isBanned) throw new Error(t('validation:user.banned'));
|
|
86
104
|
|
|
87
|
-
const isPasswordOk = await
|
|
105
|
+
const isPasswordOk = await service.verifyPassword(
|
|
106
|
+
user.password!,
|
|
107
|
+
input.password
|
|
108
|
+
);
|
|
88
109
|
if (!isPasswordOk) throw new Error(t('validation:login.wrongPassword'));
|
|
89
110
|
|
|
90
111
|
const userAgent = decodeURI(ctx.req.get('user-agent') || '');
|
|
@@ -118,15 +139,29 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
118
139
|
return { ok: true };
|
|
119
140
|
}
|
|
120
141
|
|
|
121
|
-
@ValidateArgs(confirmEmailValidators, {
|
|
142
|
+
@ValidateArgs(confirmEmailValidators, {
|
|
143
|
+
arg: 'input',
|
|
144
|
+
tKey: 'confirmEmail',
|
|
145
|
+
})
|
|
122
146
|
@Mutation(() => StatusPayload)
|
|
123
|
-
async confirmEmail(
|
|
124
|
-
|
|
147
|
+
async confirmEmail(
|
|
148
|
+
@Arg('input') input: ConfirmEmailInput,
|
|
149
|
+
@Ctx() ctx: TContext
|
|
150
|
+
) {
|
|
151
|
+
const service = getAuthService();
|
|
152
|
+
const user = await service.getUserByToken(
|
|
153
|
+
CONFIRMATION_REDIS_PREFIX,
|
|
154
|
+
input.token
|
|
155
|
+
);
|
|
125
156
|
if (!user) throw new Error(ctx.t('validation:error.wrongCode'));
|
|
126
157
|
|
|
127
158
|
user.isEmailConfirmed = true;
|
|
128
159
|
await user.save();
|
|
129
|
-
await
|
|
160
|
+
await service.removeTokenFromRedis(
|
|
161
|
+
CONFIRMATION_REDIS_PREFIX,
|
|
162
|
+
user,
|
|
163
|
+
input.token
|
|
164
|
+
);
|
|
130
165
|
|
|
131
166
|
const userAgent = decodeURI(ctx.req.get('user-agent') || '');
|
|
132
167
|
await ctx.req.session.create({
|
|
@@ -137,7 +172,9 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
137
172
|
|
|
138
173
|
if (onEmailConfirmed) await onEmailConfirmed(user, ctx);
|
|
139
174
|
if (logTelegramBot) {
|
|
140
|
-
await logTelegramBot.sendError(
|
|
175
|
+
await logTelegramBot.sendError(
|
|
176
|
+
`${user.email || user.urlSlug} подтвердил(а) email`
|
|
177
|
+
);
|
|
141
178
|
}
|
|
142
179
|
|
|
143
180
|
return { ok: true };
|
|
@@ -146,19 +183,25 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
146
183
|
@TransformArgs(forgotPasswordTransformers, { arg: 'input' })
|
|
147
184
|
@ValidateArgs(forgotPasswordValidators, { arg: 'input', tKey: 'forgot' })
|
|
148
185
|
@Mutation(() => SendTokenPayload)
|
|
149
|
-
async forgotPassword(
|
|
186
|
+
async forgotPassword(
|
|
187
|
+
@Arg('input') input: ForgotPasswordInput,
|
|
188
|
+
@Ctx() ctx: TContext
|
|
189
|
+
) {
|
|
150
190
|
const { t } = ctx;
|
|
151
|
-
const
|
|
191
|
+
const service = getAuthService();
|
|
192
|
+
const user = await service.db.findUserByEmail(input.email);
|
|
152
193
|
if (!user) throw new Error(t('validation:forgot.errors.notRegistered'));
|
|
153
194
|
|
|
154
|
-
const res = await
|
|
195
|
+
const res = await service.sendLink(user, 'recovery');
|
|
155
196
|
|
|
156
197
|
if (logTelegramBot) {
|
|
157
|
-
await logTelegramBot.sendError(
|
|
198
|
+
await logTelegramBot.sendError(
|
|
199
|
+
`${user.email || user.urlSlug} запросил(а) восстановление пароля`
|
|
200
|
+
);
|
|
158
201
|
}
|
|
159
202
|
|
|
160
203
|
return {
|
|
161
|
-
|
|
204
|
+
recoveryLinkIsSent: res.ok,
|
|
162
205
|
limitExpiresAt: res.limitExpiresAt,
|
|
163
206
|
};
|
|
164
207
|
}
|
|
@@ -166,14 +209,25 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
166
209
|
@TransformArgs(recoverPasswordTransformers, { arg: 'input' })
|
|
167
210
|
@ValidateArgs(recoverPasswordValidators, { arg: 'input', tKey: 'recover' })
|
|
168
211
|
@Mutation(() => StatusPayload)
|
|
169
|
-
async recoverPassword(
|
|
170
|
-
|
|
212
|
+
async recoverPassword(
|
|
213
|
+
@Arg('input') input: RecoverPasswordInput,
|
|
214
|
+
@Ctx() ctx: TContext
|
|
215
|
+
) {
|
|
216
|
+
const service = getAuthService();
|
|
217
|
+
const user = await service.getUserByToken(
|
|
218
|
+
RECOVERY_REDIS_PREFIX,
|
|
219
|
+
input.token
|
|
220
|
+
);
|
|
171
221
|
if (!user) throw new Error(ctx.t('validation:error.wrongCode'));
|
|
172
222
|
|
|
173
|
-
user.password = await
|
|
223
|
+
user.password = await service.hashPassword(input.password);
|
|
174
224
|
user.isTempPassword = false;
|
|
175
225
|
await user.save();
|
|
176
|
-
await
|
|
226
|
+
await service.removeTokenFromRedis(
|
|
227
|
+
RECOVERY_REDIS_PREFIX,
|
|
228
|
+
user,
|
|
229
|
+
input.token
|
|
230
|
+
);
|
|
177
231
|
|
|
178
232
|
const userAgent = decodeURI(ctx.req.get('user-agent') || '');
|
|
179
233
|
await ctx.req.session.create({
|
|
@@ -184,7 +238,9 @@ export function createAuthResolver<TContext extends ProfileContext = ProfileCont
|
|
|
184
238
|
|
|
185
239
|
if (onPasswordRecovered) await onPasswordRecovered(user, ctx);
|
|
186
240
|
if (logTelegramBot) {
|
|
187
|
-
await logTelegramBot.sendError(
|
|
241
|
+
await logTelegramBot.sendError(
|
|
242
|
+
`${user.email || user.urlSlug} восстановил(а) пароль`
|
|
243
|
+
);
|
|
188
244
|
}
|
|
189
245
|
|
|
190
246
|
return { ok: true };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
Arg,
|
|
3
|
+
Authorized,
|
|
4
|
+
Ctx,
|
|
5
|
+
Mutation,
|
|
6
|
+
Resolver,
|
|
7
|
+
UnauthorizedError,
|
|
8
8
|
} from 'type-graphql';
|
|
9
9
|
import { StatusPayload } from '@os-team/graphql-utils';
|
|
10
10
|
import { TransformArgs } from '@os-team/graphql-transformers';
|
|
@@ -12,96 +12,130 @@ import { ValidateArgs } from '@os-team/graphql-validators';
|
|
|
12
12
|
import { ProfileAuthService } from '../services/ProfileAuthService';
|
|
13
13
|
import { ProfileContext } from '../types';
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
UpdateEmailInput,
|
|
16
|
+
updateEmailTransformers,
|
|
17
|
+
updateEmailValidators,
|
|
18
|
+
UpdatePasswordInput,
|
|
19
|
+
updatePasswordTransformers,
|
|
20
|
+
updatePasswordValidators,
|
|
21
|
+
UpdateProfileInput,
|
|
22
|
+
updateProfileTransformers,
|
|
23
|
+
updateProfileValidators,
|
|
18
24
|
} from '../dto';
|
|
19
25
|
|
|
20
26
|
export interface ProfileResolverDependencies {
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
authService: ProfileAuthService | (() => ProfileAuthService);
|
|
28
|
+
userType: any;
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
export function createProfileResolver<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!isOldPasswordOk) throw new Error(t('validation:updatePassword.wrongPassword'));
|
|
59
|
-
|
|
60
|
-
user.password = await authService.hashPassword(input.password);
|
|
61
|
-
user.isTempPassword = false;
|
|
62
|
-
await user.save();
|
|
63
|
-
|
|
64
|
-
return { ok: true };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
@Authorized()
|
|
68
|
-
@TransformArgs(updateProfileTransformers, { arg: 'input' })
|
|
69
|
-
@ValidateArgs(updateProfileValidators, { arg: 'input', tKey: 'updateProfile' })
|
|
70
|
-
@Mutation(() => userType)
|
|
71
|
-
async updateProfile(@Arg('input') input: UpdateProfileInput, @Ctx() ctx: TContext) {
|
|
72
|
-
const { user } = ctx;
|
|
73
|
-
if (!user) throw new UnauthorizedError();
|
|
74
|
-
|
|
75
|
-
user.name = input.name;
|
|
76
|
-
await user.save();
|
|
77
|
-
|
|
78
|
-
return user;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
@Authorized()
|
|
82
|
-
@Mutation(() => StatusPayload)
|
|
83
|
-
async toggleEmailNotification(@Ctx() ctx: TContext) {
|
|
84
|
-
const { user } = ctx;
|
|
85
|
-
if (!user) throw new UnauthorizedError();
|
|
86
|
-
|
|
87
|
-
user.isEmailNotificationEnabled = !user.isEmailNotificationEnabled;
|
|
88
|
-
await user.save();
|
|
89
|
-
|
|
90
|
-
return { ok: true };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
@Authorized()
|
|
94
|
-
@Mutation(() => StatusPayload)
|
|
95
|
-
async toggleTelegramNotification(@Ctx() ctx: TContext) {
|
|
96
|
-
const { user } = ctx;
|
|
97
|
-
if (!user) throw new UnauthorizedError();
|
|
98
|
-
|
|
99
|
-
user.isTelegramNotificationEnabled = !user.isTelegramNotificationEnabled;
|
|
100
|
-
await user.save();
|
|
101
|
-
|
|
102
|
-
return { ok: true };
|
|
103
|
-
}
|
|
31
|
+
export function createProfileResolver<
|
|
32
|
+
TContext extends ProfileContext = ProfileContext
|
|
33
|
+
>(deps: ProfileResolverDependencies): any {
|
|
34
|
+
const { authService, userType } = deps;
|
|
35
|
+
|
|
36
|
+
const getAuthService = (): ProfileAuthService => {
|
|
37
|
+
if (typeof authService === 'function') return authService();
|
|
38
|
+
return authService;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
@Resolver(() => userType)
|
|
42
|
+
class ProfileResolver {
|
|
43
|
+
@Authorized()
|
|
44
|
+
@TransformArgs(updateEmailTransformers, { arg: 'input' })
|
|
45
|
+
@ValidateArgs(updateEmailValidators, { arg: 'input', tKey: 'updateEmail' })
|
|
46
|
+
@Mutation(() => StatusPayload)
|
|
47
|
+
async updateEmail(
|
|
48
|
+
@Arg('input') input: UpdateEmailInput,
|
|
49
|
+
@Ctx() ctx: TContext
|
|
50
|
+
) {
|
|
51
|
+
const { user, t } = ctx;
|
|
52
|
+
if (!user) throw new UnauthorizedError();
|
|
53
|
+
|
|
54
|
+
const service = getAuthService();
|
|
55
|
+
if (await service.db.isEmailTaken(input.email, user.id)) {
|
|
56
|
+
throw new Error(t('validation:updateEmail.emailExists'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
user.email = input.email;
|
|
60
|
+
user.isEmailConfirmed = false;
|
|
61
|
+
await user.save();
|
|
62
|
+
|
|
63
|
+
return { ok: true };
|
|
104
64
|
}
|
|
105
65
|
|
|
106
|
-
|
|
66
|
+
@Authorized()
|
|
67
|
+
@TransformArgs(updatePasswordTransformers, { arg: 'input' })
|
|
68
|
+
@ValidateArgs(updatePasswordValidators, {
|
|
69
|
+
arg: 'input',
|
|
70
|
+
tKey: 'updatePassword',
|
|
71
|
+
})
|
|
72
|
+
@Mutation(() => StatusPayload)
|
|
73
|
+
async updatePassword(
|
|
74
|
+
@Arg('input') input: UpdatePasswordInput,
|
|
75
|
+
@Ctx() ctx: TContext
|
|
76
|
+
) {
|
|
77
|
+
const { user, t } = ctx;
|
|
78
|
+
if (!user) throw new UnauthorizedError();
|
|
79
|
+
|
|
80
|
+
const service = getAuthService();
|
|
81
|
+
const isOldPasswordOk = await service.verifyPassword(
|
|
82
|
+
user.password!,
|
|
83
|
+
input.oldPassword
|
|
84
|
+
);
|
|
85
|
+
if (!isOldPasswordOk)
|
|
86
|
+
throw new Error(t('validation:updatePassword.wrongPassword'));
|
|
87
|
+
|
|
88
|
+
user.password = await service.hashPassword(input.password);
|
|
89
|
+
user.isTempPassword = false;
|
|
90
|
+
await user.save();
|
|
91
|
+
|
|
92
|
+
return { ok: true };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Authorized()
|
|
96
|
+
@TransformArgs(updateProfileTransformers, { arg: 'input' })
|
|
97
|
+
@ValidateArgs(updateProfileValidators, {
|
|
98
|
+
arg: 'input',
|
|
99
|
+
tKey: 'updateProfile',
|
|
100
|
+
})
|
|
101
|
+
@Mutation(() => userType)
|
|
102
|
+
async updateProfile(
|
|
103
|
+
@Arg('input') input: UpdateProfileInput,
|
|
104
|
+
@Ctx() ctx: TContext
|
|
105
|
+
) {
|
|
106
|
+
const { user } = ctx;
|
|
107
|
+
if (!user) throw new UnauthorizedError();
|
|
108
|
+
|
|
109
|
+
user.name = input.name;
|
|
110
|
+
await user.save();
|
|
111
|
+
|
|
112
|
+
return user;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Authorized()
|
|
116
|
+
@Mutation(() => StatusPayload)
|
|
117
|
+
async toggleEmailNotification(@Ctx() ctx: TContext) {
|
|
118
|
+
const { user } = ctx;
|
|
119
|
+
if (!user) throw new UnauthorizedError();
|
|
120
|
+
|
|
121
|
+
user.isEmailNotificationEnabled = !user.isEmailNotificationEnabled;
|
|
122
|
+
await user.save();
|
|
123
|
+
|
|
124
|
+
return { ok: true };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@Authorized()
|
|
128
|
+
@Mutation(() => StatusPayload)
|
|
129
|
+
async toggleTelegramNotification(@Ctx() ctx: TContext) {
|
|
130
|
+
const { user } = ctx;
|
|
131
|
+
if (!user) throw new UnauthorizedError();
|
|
132
|
+
|
|
133
|
+
user.isTelegramNotificationEnabled = !user.isTelegramNotificationEnabled;
|
|
134
|
+
await user.save();
|
|
135
|
+
|
|
136
|
+
return { ok: true };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return ProfileResolver;
|
|
107
141
|
}
|