@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.
Files changed (63) hide show
  1. package/dist/dto/ConfirmEmailInput.d.ts +1 -1
  2. package/dist/dto/ConfirmEmailInput.js +3 -3
  3. package/dist/dto/ForgotPasswordInput.d.ts +1 -1
  4. package/dist/dto/ForgotPasswordInput.js +3 -3
  5. package/dist/dto/LoginInput.d.ts +2 -2
  6. package/dist/dto/LoginInput.js +38 -5
  7. package/dist/dto/PasswordInput.d.ts +1 -1
  8. package/dist/dto/PasswordInput.js +3 -3
  9. package/dist/dto/RecoverPasswordInput.d.ts +2 -2
  10. package/dist/dto/RecoverPasswordInput.js +38 -5
  11. package/dist/dto/RegisterInput.d.ts +2 -2
  12. package/dist/dto/RegisterInput.js +38 -5
  13. package/dist/dto/SendTokenPayload.d.ts +2 -2
  14. package/dist/dto/SendTokenPayload.js +3 -4
  15. package/dist/dto/StatusPayload.d.ts +1 -1
  16. package/dist/dto/StatusPayload.js +2 -3
  17. package/dist/dto/UpdateEmailInput.d.ts +1 -1
  18. package/dist/dto/UpdateEmailInput.js +3 -3
  19. package/dist/dto/UpdatePasswordInput.d.ts +2 -2
  20. package/dist/dto/UpdatePasswordInput.js +38 -5
  21. package/dist/dto/UpdateProfileInput.d.ts +1 -1
  22. package/dist/dto/UpdateProfileInput.js +3 -3
  23. package/dist/dto/index.d.ts +11 -11
  24. package/dist/dto/index.js +42 -24
  25. package/dist/email-templates/base/content.pug +32 -0
  26. package/dist/email-templates/base/header.pug +28 -0
  27. package/dist/email-templates/base/layout.pug +34 -0
  28. package/dist/email-templates/base/mixins.pug +16 -0
  29. package/dist/email-templates/confirmEmail.pug +16 -0
  30. package/dist/email-templates/forgotPassword.pug +16 -0
  31. package/dist/email-templates/notification.pug +14 -0
  32. package/dist/email-templates/tempPassword.pug +14 -0
  33. package/dist/resolvers/AuthResolver.d.ts +2 -25
  34. package/dist/resolvers/AuthResolver.js +28 -15
  35. package/dist/resolvers/ProfileResolver.d.ts +2 -19
  36. package/dist/resolvers/ProfileResolver.js +18 -5
  37. package/dist/services/ProfileEmailService.js +2 -2
  38. package/dist/types/index.d.ts +1 -1
  39. package/package.json +3 -3
  40. package/src/dto/ConfirmEmailInput.ts +1 -1
  41. package/src/dto/ForgotPasswordInput.ts +1 -1
  42. package/src/dto/LoginInput.ts +2 -2
  43. package/src/dto/PasswordInput.ts +1 -1
  44. package/src/dto/RecoverPasswordInput.ts +2 -2
  45. package/src/dto/RegisterInput.ts +2 -2
  46. package/src/dto/SendTokenPayload.ts +2 -2
  47. package/src/dto/StatusPayload.ts +1 -1
  48. package/src/dto/UpdateEmailInput.ts +1 -1
  49. package/src/dto/UpdatePasswordInput.ts +2 -2
  50. package/src/dto/UpdateProfileInput.ts +1 -1
  51. package/src/dto/index.ts +11 -11
  52. package/src/email-templates/base/content.pug +32 -0
  53. package/src/email-templates/base/header.pug +28 -0
  54. package/src/email-templates/base/layout.pug +34 -0
  55. package/src/email-templates/base/mixins.pug +16 -0
  56. package/src/email-templates/confirmEmail.pug +16 -0
  57. package/src/email-templates/forgotPassword.pug +16 -0
  58. package/src/email-templates/notification.pug +14 -0
  59. package/src/email-templates/tempPassword.pug +14 -0
  60. package/src/resolvers/AuthResolver.ts +94 -38
  61. package/src/resolvers/ProfileResolver.ts +125 -91
  62. package/src/services/ProfileEmailService.ts +147 -147
  63. 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 { ProfileAuthService, CONFIRMATION_REDIS_PREFIX, RECOVERY_REDIS_PREFIX } from '../services/ProfileAuthService';
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, registerTransformers, registerValidators,
15
- LoginInput, loginTransformers, loginValidators,
12
+ RegisterInput,
13
+ registerTransformers,
14
+ registerValidators,
15
+ LoginInput,
16
+ loginTransformers,
17
+ loginValidators,
16
18
  ConfirmEmailInput,
17
- ForgotPasswordInput, forgotPasswordTransformers, forgotPasswordValidators,
18
- RecoverPasswordInput, recoverPasswordTransformers, recoverPasswordValidators,
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<TContext extends ProfileContext = ProfileContext> {
24
- authService: ProfileAuthService;
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<TContext extends ProfileContext = ProfileContext>(deps: AuthResolverDependencies<TContext>) {
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
- if (await authService.db.isEmailTaken(input.email)) {
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 authService.hashPassword(input.password);
58
- const user = await authService.db.createUser({
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) await logTelegramBot.sendError(`Новый пользователь ${input.email}`);
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 user = await authService.db.findUserByEmail(input.email);
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 authService.verifyPassword(user.password!, input.password);
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, { arg: 'input', tKey: 'confirmEmail' })
142
+ @ValidateArgs(confirmEmailValidators, {
143
+ arg: 'input',
144
+ tKey: 'confirmEmail',
145
+ })
122
146
  @Mutation(() => StatusPayload)
123
- async confirmEmail(@Arg('input') input: ConfirmEmailInput, @Ctx() ctx: TContext) {
124
- const user = await authService.getUserByToken(CONFIRMATION_REDIS_PREFIX, input.token);
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 authService.removeTokenFromRedis(CONFIRMATION_REDIS_PREFIX, user, input.token);
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(`${user.email || user.urlSlug} подтвердил(а) email`);
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(@Arg('input') input: ForgotPasswordInput, @Ctx() ctx: TContext) {
186
+ async forgotPassword(
187
+ @Arg('input') input: ForgotPasswordInput,
188
+ @Ctx() ctx: TContext
189
+ ) {
150
190
  const { t } = ctx;
151
- const user = await authService.db.findUserByEmail(input.email);
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 authService.sendLink(user, 'recovery');
195
+ const res = await service.sendLink(user, 'recovery');
155
196
 
156
197
  if (logTelegramBot) {
157
- await logTelegramBot.sendError(`${user.email || user.urlSlug} запросил(а) восстановление пароля`);
198
+ await logTelegramBot.sendError(
199
+ `${user.email || user.urlSlug} запросил(а) восстановление пароля`
200
+ );
158
201
  }
159
202
 
160
203
  return {
161
- confirmationLinkIsSent: res.ok,
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(@Arg('input') input: RecoverPasswordInput, @Ctx() ctx: TContext) {
170
- const user = await authService.getUserByToken(RECOVERY_REDIS_PREFIX, input.token);
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 authService.hashPassword(input.password);
223
+ user.password = await service.hashPassword(input.password);
174
224
  user.isTempPassword = false;
175
225
  await user.save();
176
- await authService.removeTokenFromRedis(RECOVERY_REDIS_PREFIX, user, input.token);
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(`${user.email || user.urlSlug} восстановил(а) пароль`);
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
- Arg,
3
- Authorized,
4
- Ctx,
5
- Mutation,
6
- Resolver,
7
- UnauthorizedError,
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
- UpdateEmailInput, updateEmailTransformers, updateEmailValidators,
16
- UpdatePasswordInput, updatePasswordTransformers, updatePasswordValidators,
17
- UpdateProfileInput, updateProfileTransformers, updateProfileValidators,
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
- authService: ProfileAuthService;
22
- userType: any;
27
+ authService: ProfileAuthService | (() => ProfileAuthService);
28
+ userType: any;
23
29
  }
24
30
 
25
- export function createProfileResolver<TContext extends ProfileContext = ProfileContext>(deps: ProfileResolverDependencies) {
26
- const { authService, userType } = deps;
27
-
28
- @Resolver(() => userType)
29
- class ProfileResolver {
30
- @Authorized()
31
- @TransformArgs(updateEmailTransformers, { arg: 'input' })
32
- @ValidateArgs(updateEmailValidators, { arg: 'input', tKey: 'updateEmail' })
33
- @Mutation(() => StatusPayload)
34
- async updateEmail(@Arg('input') input: UpdateEmailInput, @Ctx() ctx: TContext) {
35
- const { user, t } = ctx;
36
- if (!user) throw new UnauthorizedError();
37
-
38
- if (await authService.db.isEmailTaken(input.email, user.id)) {
39
- throw new Error(t('validation:updateEmail.emailExists'));
40
- }
41
-
42
- user.email = input.email;
43
- user.isEmailConfirmed = false;
44
- await user.save();
45
-
46
- return { ok: true };
47
- }
48
-
49
- @Authorized()
50
- @TransformArgs(updatePasswordTransformers, { arg: 'input' })
51
- @ValidateArgs(updatePasswordValidators, { arg: 'input', tKey: 'updatePassword' })
52
- @Mutation(() => StatusPayload)
53
- async updatePassword(@Arg('input') input: UpdatePasswordInput, @Ctx() ctx: TContext) {
54
- const { user, t } = ctx;
55
- if (!user) throw new UnauthorizedError();
56
-
57
- const isOldPasswordOk = await authService.verifyPassword(user.password!, input.oldPassword);
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
- return ProfileResolver;
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
  }