@skroz/profile-api 1.0.5

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 (65) hide show
  1. package/LICENCE.md +21 -0
  2. package/dist/adapters/TypeOrmProfileAdapter.d.ts +13 -0
  3. package/dist/adapters/TypeOrmProfileAdapter.js +54 -0
  4. package/dist/dto/ConfirmEmailInput.d.ts +6 -0
  5. package/dist/dto/ConfirmEmailInput.js +27 -0
  6. package/dist/dto/ForgotPasswordInput.d.ts +9 -0
  7. package/dist/dto/ForgotPasswordInput.js +31 -0
  8. package/dist/dto/LoginInput.d.ts +12 -0
  9. package/dist/dto/LoginInput.js +31 -0
  10. package/dist/dto/PasswordInput.d.ts +9 -0
  11. package/dist/dto/PasswordInput.js +31 -0
  12. package/dist/dto/RecoverPasswordInput.d.ts +12 -0
  13. package/dist/dto/RecoverPasswordInput.js +28 -0
  14. package/dist/dto/RegisterInput.d.ts +16 -0
  15. package/dist/dto/RegisterInput.js +40 -0
  16. package/dist/dto/SendTokenPayload.d.ts +4 -0
  17. package/dist/dto/SendTokenPayload.js +27 -0
  18. package/dist/dto/StatusPayload.d.ts +3 -0
  19. package/dist/dto/StatusPayload.js +23 -0
  20. package/dist/dto/UpdateEmailInput.d.ts +9 -0
  21. package/dist/dto/UpdateEmailInput.js +31 -0
  22. package/dist/dto/UpdatePasswordInput.d.ts +12 -0
  23. package/dist/dto/UpdatePasswordInput.js +28 -0
  24. package/dist/dto/UpdateProfileInput.d.ts +9 -0
  25. package/dist/dto/UpdateProfileInput.js +31 -0
  26. package/dist/dto/index.d.ts +11 -0
  27. package/dist/dto/index.js +27 -0
  28. package/dist/entities/TypeOrmBaseUser.d.ts +25 -0
  29. package/dist/entities/TypeOrmBaseUser.js +117 -0
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.js +24 -0
  32. package/dist/resolvers/AuthResolver.d.ts +38 -0
  33. package/dist/resolvers/AuthResolver.js +219 -0
  34. package/dist/resolvers/ProfileResolver.d.ts +24 -0
  35. package/dist/resolvers/ProfileResolver.js +145 -0
  36. package/dist/services/ProfileAuthService.d.ts +24 -0
  37. package/dist/services/ProfileAuthService.js +94 -0
  38. package/dist/services/ProfileEmailService.d.ts +15 -0
  39. package/dist/services/ProfileEmailService.js +105 -0
  40. package/dist/types/index.d.ts +98 -0
  41. package/dist/types/index.js +10 -0
  42. package/dist/validators/isTrue.d.ts +3 -0
  43. package/dist/validators/isTrue.js +7 -0
  44. package/package.json +45 -0
  45. package/src/adapters/TypeOrmProfileAdapter.ts +40 -0
  46. package/src/dto/ConfirmEmailInput.ts +12 -0
  47. package/src/dto/ForgotPasswordInput.ts +17 -0
  48. package/src/dto/LoginInput.ts +20 -0
  49. package/src/dto/PasswordInput.ts +17 -0
  50. package/src/dto/RecoverPasswordInput.ts +20 -0
  51. package/src/dto/RegisterInput.ts +29 -0
  52. package/src/dto/SendTokenPayload.ts +10 -0
  53. package/src/dto/StatusPayload.ts +7 -0
  54. package/src/dto/UpdateEmailInput.ts +17 -0
  55. package/src/dto/UpdatePasswordInput.ts +20 -0
  56. package/src/dto/UpdateProfileInput.ts +17 -0
  57. package/src/dto/index.ts +11 -0
  58. package/src/entities/TypeOrmBaseUser.ts +87 -0
  59. package/src/index.ts +8 -0
  60. package/src/resolvers/AuthResolver.ts +195 -0
  61. package/src/resolvers/ProfileResolver.ts +107 -0
  62. package/src/services/ProfileAuthService.ts +122 -0
  63. package/src/services/ProfileEmailService.ts +158 -0
  64. package/src/types/index.ts +102 -0
  65. package/src/validators/isTrue.ts +8 -0
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ProfileAuthService = exports.LAST_SENT_AT_REDIS_POSTFIX = exports.TOKEN_REDIS_POSTFIX = exports.RECOVERY_REDIS_PREFIX = exports.CONFIRMATION_REDIS_PREFIX = void 0;
16
+ const argon2_1 = __importDefault(require("argon2"));
17
+ exports.CONFIRMATION_REDIS_PREFIX = 'conf';
18
+ exports.RECOVERY_REDIS_PREFIX = 'rec';
19
+ exports.TOKEN_REDIS_POSTFIX = 'token';
20
+ exports.LAST_SENT_AT_REDIS_POSTFIX = 'lastSentAt';
21
+ class ProfileAuthService {
22
+ constructor(db, email, redis, config) {
23
+ this.db = db;
24
+ this.email = email;
25
+ this.redis = redis;
26
+ this.config = config;
27
+ }
28
+ hashPassword(password) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ return argon2_1.default.hash(password, { type: argon2_1.default.argon2id });
31
+ });
32
+ }
33
+ verifyPassword(hash, password) {
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ return argon2_1.default.verify(hash, password);
36
+ });
37
+ }
38
+ getLimitMs() {
39
+ return this.config.resendEmailLimitSeconds * 1000;
40
+ }
41
+ setTokenToRedis(prefix, user, token, ttlMinutes) {
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ const exSeconds = ttlMinutes * 60;
44
+ // token -> userId
45
+ yield this.redis.setex(`${prefix}:${token}`, exSeconds, user.id.toString());
46
+ // userId -> token
47
+ yield this.redis.setex(`${prefix}:${user.id}:${exports.TOKEN_REDIS_POSTFIX}`, exSeconds, token);
48
+ // lastSentAt
49
+ yield this.redis.psetex(`${prefix}:${user.id}:${exports.LAST_SENT_AT_REDIS_POSTFIX}`, this.getLimitMs(), Date.now().toString());
50
+ });
51
+ }
52
+ sendLink(user, type) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ const prefix = type === 'confirmation'
55
+ ? exports.CONFIRMATION_REDIS_PREFIX
56
+ : exports.RECOVERY_REDIS_PREFIX;
57
+ const ttlMinutes = type === 'confirmation'
58
+ ? this.config.confirmationTokenLifetimeMinutes
59
+ : this.config.recoveryTokenLifetimeMinutes;
60
+ // Check limit
61
+ const lastSentAtStr = yield this.redis.get(`${prefix}:${user.id}:${exports.LAST_SENT_AT_REDIS_POSTFIX}`);
62
+ const lastSentAt = Number(lastSentAtStr) || 0;
63
+ const expiresAt = lastSentAt + this.getLimitMs();
64
+ const expiresIn = expiresAt - Date.now();
65
+ if (expiresIn > 0)
66
+ return { ok: false, limitExpiresAt: expiresAt };
67
+ // Generate token (6 digits as in original code)
68
+ let token = '';
69
+ for (let i = 0; i < 6; i++) {
70
+ token += Math.floor(Math.random() * 10);
71
+ }
72
+ yield this.setTokenToRedis(prefix, user, token, ttlMinutes);
73
+ const ok = yield this.email.sendToken(user, type, token);
74
+ return { ok, limitExpiresAt: Date.now() + this.getLimitMs() };
75
+ });
76
+ }
77
+ getUserByToken(prefix, token) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ const userIdStr = yield this.redis.get(`${prefix}:${token}`);
80
+ if (!userIdStr)
81
+ return null;
82
+ const userId = Number(userIdStr);
83
+ if (isNaN(userId))
84
+ return null;
85
+ return this.db.findUserById(userId);
86
+ });
87
+ }
88
+ removeTokenFromRedis(prefix, user, token) {
89
+ return __awaiter(this, void 0, void 0, function* () {
90
+ yield this.redis.del(`${prefix}:${token}`, `${prefix}:${user.id}:${exports.TOKEN_REDIS_POSTFIX}`, `${prefix}:${user.id}:${exports.LAST_SENT_AT_REDIS_POSTFIX}`);
91
+ });
92
+ }
93
+ }
94
+ exports.ProfileAuthService = ProfileAuthService;
@@ -0,0 +1,15 @@
1
+ import { AuthUser, EmailConfig, ProfileLocales } from '../types';
2
+ export type EmailType = 'auth' | 'activity' | 'info' | 'admin' | 'other';
3
+ export declare class ProfileEmailService {
4
+ private ses;
5
+ private config;
6
+ private locales;
7
+ constructor(config: EmailConfig, locales: ProfileLocales);
8
+ private getWebsiteUrl;
9
+ private render;
10
+ private getFrom;
11
+ send(to: string, subject: string, template: string, vars: any, type?: EmailType): Promise<boolean>;
12
+ sendToken(user: AuthUser, type: 'confirmation' | 'recovery', token: string): Promise<boolean>;
13
+ sendTemporaryPassword(user: AuthUser, tempPassword: string): Promise<boolean>;
14
+ sendGenericEmail(user: AuthUser, subject: string, text: string, type?: EmailType, vars?: any): Promise<boolean>;
15
+ }
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ProfileEmailService = void 0;
16
+ const aws_ses_1 = __importDefault(require("@os-team/aws-ses"));
17
+ const pug_1 = __importDefault(require("pug"));
18
+ const path_1 = __importDefault(require("path"));
19
+ const types_1 = require("../types");
20
+ class ProfileEmailService {
21
+ constructor(config, locales) {
22
+ this.ses = new aws_ses_1.default({ region: 'eu-west-1' });
23
+ this.config = config;
24
+ this.locales = locales;
25
+ }
26
+ getWebsiteUrl() {
27
+ return this.config.websiteUrl || `https://${this.config.domain}`;
28
+ }
29
+ render(templateName, vars) {
30
+ const templatePath = path_1.default.resolve(this.config.templateDir, `${templateName}.pug`);
31
+ return pug_1.default.renderFile(templatePath, Object.assign({ domain: this.config.domain, websiteUrl: this.getWebsiteUrl(), primaryBrandColor: this.config.primaryBrandColor, logoUrl: this.config.logoUrl || '' }, vars));
32
+ }
33
+ getFrom(type) {
34
+ const { domain } = this.config;
35
+ const username = this.config.fromEmailUsername;
36
+ switch (type) {
37
+ case 'auth':
38
+ return `${domain} <${username}@auth.${domain}>`;
39
+ case 'activity':
40
+ return `${domain} <${username}@a.${domain}>`;
41
+ case 'info':
42
+ return `${domain} <${username}@i.${domain}>`;
43
+ case 'admin':
44
+ return `${domain} <${username}@admin.${domain}>`;
45
+ default:
46
+ return `${domain} <${username}@o.${domain}>`;
47
+ }
48
+ }
49
+ send(to_1, subject_1, template_1, vars_1) {
50
+ return __awaiter(this, arguments, void 0, function* (to, subject, template, vars, type = 'other') {
51
+ const html = this.render(template, vars);
52
+ const from = this.getFrom(type);
53
+ return this.ses.send({
54
+ Destination: { ToAddresses: [to] },
55
+ Message: {
56
+ Subject: { Data: subject, Charset: 'UTF-8' },
57
+ Body: { Html: { Data: html, Charset: 'UTF-8' } },
58
+ },
59
+ Source: from,
60
+ });
61
+ });
62
+ }
63
+ sendToken(user, type, token) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ if (!user.email)
66
+ return false;
67
+ const isConfirm = type === 'confirmation';
68
+ const emailLocales = isConfirm
69
+ ? this.locales.email.confirmEmail
70
+ : this.locales.email.forgotPassword;
71
+ const template = isConfirm
72
+ ? types_1.ProfileEmailTemplate.CONFIRM_EMAIL
73
+ : types_1.ProfileEmailTemplate.FORGOT_PASSWORD;
74
+ return this.send(user.email, emailLocales.subject, template, {
75
+ header: emailLocales.header,
76
+ text: emailLocales.text,
77
+ linkTitle: emailLocales.linkTitle,
78
+ linkHref: `${this.getWebsiteUrl()}/${type}?token=${token}&email=${user.email}`,
79
+ confirmationToken: token,
80
+ }, 'auth');
81
+ });
82
+ }
83
+ sendTemporaryPassword(user, tempPassword) {
84
+ return __awaiter(this, void 0, void 0, function* () {
85
+ if (!user.email)
86
+ return false;
87
+ const emailLocales = this.locales.email.tempPassword;
88
+ return this.send(user.email, emailLocales.subject, types_1.ProfileEmailTemplate.TEMP_PASSWORD, {
89
+ header: emailLocales.header.replace('{{tempPassword}}', tempPassword),
90
+ text: emailLocales.text,
91
+ linkTitle: emailLocales.linkTitle,
92
+ linkHref: this.getWebsiteUrl(),
93
+ }, 'auth');
94
+ });
95
+ }
96
+ /* Generic method for any custom project emails */
97
+ sendGenericEmail(user_1, subject_1, text_1) {
98
+ return __awaiter(this, arguments, void 0, function* (user, subject, text, type = 'info', vars = {}) {
99
+ if (!user.email)
100
+ return false;
101
+ return this.send(user.email, subject, types_1.ProfileEmailTemplate.GENERIC_NOTIFICATION, Object.assign({ header: subject, text, linkTitle: 'Go to Website', linkHref: this.getWebsiteUrl() }, vars), type);
102
+ });
103
+ }
104
+ }
105
+ exports.ProfileEmailService = ProfileEmailService;
@@ -0,0 +1,98 @@
1
+ export interface AuthUser {
2
+ id: number;
3
+ email: string | null;
4
+ name?: string | null;
5
+ avatar?: string | null;
6
+ password?: string;
7
+ isEmailConfirmed: boolean;
8
+ isBanned: boolean;
9
+ isTempPassword: boolean;
10
+ urlSlug?: string | null;
11
+ telegramId?: string | null;
12
+ isEmailNotificationEnabled: boolean;
13
+ isTelegramNotificationEnabled: boolean;
14
+ lastSeenAt: Date;
15
+ save(): Promise<this>;
16
+ }
17
+ export interface ProfileDbAdapter {
18
+ findUserByEmail(email: string): Promise<AuthUser | null>;
19
+ findUserById(id: number): Promise<AuthUser | null>;
20
+ findUserByTelegramId(telegramId: string): Promise<AuthUser | null>;
21
+ createUser(data: {
22
+ email: string;
23
+ passwordHash: string;
24
+ }): Promise<AuthUser>;
25
+ isEmailTaken(email: string, excludeUserId?: number): Promise<boolean>;
26
+ }
27
+ export interface ProfileAuthConfig {
28
+ resendEmailLimitSeconds: number;
29
+ confirmationTokenLifetimeMinutes: number;
30
+ recoveryTokenLifetimeMinutes: number;
31
+ }
32
+ export interface EmailConfig extends ProfileAuthConfig {
33
+ domain: string;
34
+ websiteUrl?: string;
35
+ primaryBrandColor: string;
36
+ logoUrl?: string;
37
+ fromEmailUsername?: string;
38
+ templateDir: string;
39
+ isOnlineSeconds: number;
40
+ isOnlineRecentlySeconds: number;
41
+ }
42
+ export declare enum ProfileEmailTemplate {
43
+ CONFIRM_EMAIL = "confirmEmail",
44
+ FORGOT_PASSWORD = "forgotPassword",
45
+ TEMP_PASSWORD = "tempPassword",
46
+ GENERIC_NOTIFICATION = "notification"
47
+ }
48
+ export interface ProfileLocales {
49
+ auth: {
50
+ emailConfirmed: string;
51
+ emailExists: string;
52
+ };
53
+ login: {
54
+ wrongPassword: string;
55
+ };
56
+ user: {
57
+ notFound: string;
58
+ banned: string;
59
+ };
60
+ error: {
61
+ wrongCode: string;
62
+ };
63
+ forgot: {
64
+ errors: {
65
+ notRegistered: string;
66
+ };
67
+ };
68
+ updatePassword: {
69
+ noPassword: string;
70
+ wrongOldPassword: string;
71
+ };
72
+ email: {
73
+ confirmEmail: {
74
+ subject: string;
75
+ header: string;
76
+ text: string;
77
+ linkTitle: string;
78
+ };
79
+ forgotPassword: {
80
+ subject: string;
81
+ header: string;
82
+ text: string;
83
+ linkTitle: string;
84
+ };
85
+ tempPassword: {
86
+ subject: string;
87
+ header: string;
88
+ text: string;
89
+ linkTitle: string;
90
+ };
91
+ };
92
+ }
93
+ export interface ProfileContext<TUser = AuthUser> {
94
+ user?: TUser;
95
+ req: any;
96
+ res: any;
97
+ t: (key: string, options?: any) => string;
98
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProfileEmailTemplate = void 0;
4
+ var ProfileEmailTemplate;
5
+ (function (ProfileEmailTemplate) {
6
+ ProfileEmailTemplate["CONFIRM_EMAIL"] = "confirmEmail";
7
+ ProfileEmailTemplate["FORGOT_PASSWORD"] = "forgotPassword";
8
+ ProfileEmailTemplate["TEMP_PASSWORD"] = "tempPassword";
9
+ ProfileEmailTemplate["GENERIC_NOTIFICATION"] = "notification";
10
+ })(ProfileEmailTemplate || (exports.ProfileEmailTemplate = ProfileEmailTemplate = {}));
@@ -0,0 +1,3 @@
1
+ import { Validator } from '@os-team/graphql-validators';
2
+ declare const isTrue: Validator;
3
+ export default isTrue;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const isTrue = {
4
+ name: 'isTrue',
5
+ validate: (value) => value === true,
6
+ };
7
+ exports.default = isTrue;
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@skroz/profile-api",
3
+ "version": "1.0.5",
4
+ "license": "MIT",
5
+ "repository": "git@gitlab.com:skroz/libs/utils.git",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "src",
11
+ "!**/*.test.ts"
12
+ ],
13
+ "scripts": {
14
+ "clean": "rimraf dist",
15
+ "build": "yarn clean && tsc --outDir dist",
16
+ "ncu": "ncu -u"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "dependencies": {
22
+ "@os-team/aws-ses": "1.0.27",
23
+ "@os-team/graphql-transformers": "1.0.15",
24
+ "@os-team/graphql-utils": "1.1.61",
25
+ "@os-team/graphql-validators": "1.0.26",
26
+ "@os-team/session": "1.0.32",
27
+ "argon2": "0.30.2",
28
+ "class-validator": "0.13.2",
29
+ "nanoid": "3.3.4",
30
+ "pug": "3.0.2",
31
+ "type-graphql": "2.0.0-beta.1",
32
+ "typeorm": "0.2.45"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "18.0.6",
36
+ "@types/pug": "2.0.6",
37
+ "ioredis": "5.2.1",
38
+ "rimraf": "3.0.2",
39
+ "typescript": "^5.0.0"
40
+ },
41
+ "peerDependencies": {
42
+ "reflect-metadata": "0.1.13"
43
+ },
44
+ "gitHead": "fbc3bff05af003470fe79b7ea7d11bc29caa9ae3"
45
+ }
@@ -0,0 +1,40 @@
1
+ import { Repository, getConnection, Not } from 'typeorm';
2
+ import { AuthUser, ProfileDbAdapter } from '../types';
3
+
4
+ export class TypeOrmProfileAdapter implements ProfileDbAdapter {
5
+ private repository: Repository<any>;
6
+
7
+ constructor(userEntity: any) {
8
+ this.repository = getConnection().getRepository(userEntity);
9
+ }
10
+
11
+ async findUserByEmail(email: string): Promise<AuthUser | null> {
12
+ return this.repository.findOne({ where: { email: email.toLowerCase() } }) as Promise<AuthUser | null>;
13
+ }
14
+
15
+ async findUserById(id: number): Promise<AuthUser | null> {
16
+ return this.repository.findOne(id) as Promise<AuthUser | null>;
17
+ }
18
+
19
+ async findUserByTelegramId(telegramId: string): Promise<AuthUser | null> {
20
+ return this.repository.findOne({ where: { telegramId } }) as Promise<AuthUser | null>;
21
+ }
22
+
23
+ async createUser(data: { email: string; passwordHash: string }): Promise<AuthUser> {
24
+ const user = this.repository.create({
25
+ email: data.email.toLowerCase(),
26
+ password: data.passwordHash,
27
+ isEmailConfirmed: false,
28
+ });
29
+ return (await this.repository.save(user)) as AuthUser;
30
+ }
31
+
32
+ async isEmailTaken(email: string, excludeUserId?: number): Promise<boolean> {
33
+ const where: any = { email: email.toLowerCase() };
34
+ if (excludeUserId) {
35
+ where.id = Not(excludeUserId);
36
+ }
37
+ const count = await this.repository.count({ where });
38
+ return count > 0;
39
+ }
40
+ }
@@ -0,0 +1,12 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isNotEmpty } from '@os-team/graphql-validators';
3
+
4
+ @InputType()
5
+ export class ConfirmEmailInput {
6
+ @Field()
7
+ token!: string;
8
+ }
9
+
10
+ export const confirmEmailValidators = {
11
+ token: [isNotEmpty],
12
+ };
@@ -0,0 +1,17 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isEmail } from '@os-team/graphql-validators';
3
+ import { toLowerCase, trim } from '@os-team/graphql-transformers';
4
+
5
+ @InputType()
6
+ export class ForgotPasswordInput {
7
+ @Field()
8
+ email!: string;
9
+ }
10
+
11
+ export const forgotPasswordValidators = {
12
+ email: [isEmail],
13
+ };
14
+
15
+ export const forgotPasswordTransformers = {
16
+ email: [trim, toLowerCase],
17
+ };
@@ -0,0 +1,20 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isEmail, isNotEmpty } from '@os-team/graphql-validators';
3
+ import { toLowerCase, trim } from '@os-team/graphql-transformers';
4
+ import { PasswordInput, passwordTransformers } from './PasswordInput';
5
+
6
+ @InputType()
7
+ export class LoginInput extends PasswordInput {
8
+ @Field()
9
+ email!: string;
10
+ }
11
+
12
+ export const loginValidators = {
13
+ email: [isEmail],
14
+ password: [isNotEmpty],
15
+ };
16
+
17
+ export const loginTransformers = {
18
+ email: [trim, toLowerCase],
19
+ ...passwordTransformers,
20
+ };
@@ -0,0 +1,17 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { maxLength, minLength } from '@os-team/graphql-validators';
3
+ import { trim } from '@os-team/graphql-transformers';
4
+
5
+ @InputType({ isAbstract: true })
6
+ export class PasswordInput {
7
+ @Field()
8
+ password!: string;
9
+ }
10
+
11
+ export const passwordValidators = {
12
+ password: [minLength(6), maxLength(64)],
13
+ };
14
+
15
+ export const passwordTransformers = {
16
+ password: [trim],
17
+ };
@@ -0,0 +1,20 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isNotEmpty } from '@os-team/graphql-validators';
3
+ import { trim } from '@os-team/graphql-transformers';
4
+ import { PasswordInput, passwordTransformers, passwordValidators } from './PasswordInput';
5
+
6
+ @InputType()
7
+ export class RecoverPasswordInput extends PasswordInput {
8
+ @Field()
9
+ token!: string;
10
+ }
11
+
12
+ export const recoverPasswordTransformers = {
13
+ ...passwordTransformers,
14
+ token: [trim],
15
+ };
16
+
17
+ export const recoverPasswordValidators = {
18
+ ...passwordValidators,
19
+ token: [isNotEmpty],
20
+ };
@@ -0,0 +1,29 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isEmail } from '@os-team/graphql-validators';
3
+ import { toLowerCase, trim } from '@os-team/graphql-transformers';
4
+ import { PasswordInput, passwordTransformers, passwordValidators } from './PasswordInput';
5
+ import isTrue from '../validators/isTrue';
6
+
7
+ @InputType()
8
+ export class RegisterInput extends PasswordInput {
9
+ @Field()
10
+ email!: string;
11
+
12
+ @Field(() => Boolean, { nullable: true })
13
+ isUserAgreementAgree?: boolean;
14
+
15
+ @Field(() => Boolean, { nullable: true })
16
+ isPrivacyPolicyAgree?: boolean;
17
+ }
18
+
19
+ export const registerValidators = {
20
+ email: [isEmail],
21
+ isUserAgreementAgree: [isTrue],
22
+ isPrivacyPolicyAgree: [isTrue],
23
+ ...passwordValidators,
24
+ };
25
+
26
+ export const registerTransformers = {
27
+ email: [trim, toLowerCase],
28
+ ...passwordTransformers,
29
+ };
@@ -0,0 +1,10 @@
1
+ import { Field, ObjectType } from 'type-graphql';
2
+
3
+ @ObjectType()
4
+ export class SendTokenPayload {
5
+ @Field()
6
+ confirmationLinkIsSent!: boolean;
7
+
8
+ @Field(() => Number, { nullable: true })
9
+ limitExpiresAt?: number;
10
+ }
@@ -0,0 +1,7 @@
1
+ import { Field, ObjectType } from 'type-graphql';
2
+
3
+ @ObjectType()
4
+ export class StatusPayload {
5
+ @Field()
6
+ ok!: boolean;
7
+ }
@@ -0,0 +1,17 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isEmail, isNotEmpty } from '@os-team/graphql-validators';
3
+ import { toLowerCase, trim } from '@os-team/graphql-transformers';
4
+
5
+ @InputType()
6
+ export class UpdateEmailInput {
7
+ @Field()
8
+ email!: string;
9
+ }
10
+
11
+ export const updateEmailTransformers = {
12
+ email: [trim, toLowerCase],
13
+ };
14
+
15
+ export const updateEmailValidators = {
16
+ email: [isNotEmpty, isEmail],
17
+ };
@@ -0,0 +1,20 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isNotEmpty } from '@os-team/graphql-validators';
3
+ import { trim } from '@os-team/graphql-transformers';
4
+ import { PasswordInput, passwordTransformers, passwordValidators } from './PasswordInput';
5
+
6
+ @InputType()
7
+ export class UpdatePasswordInput extends PasswordInput {
8
+ @Field()
9
+ oldPassword!: string;
10
+ }
11
+
12
+ export const updatePasswordTransformers = {
13
+ ...passwordTransformers,
14
+ oldPassword: [trim],
15
+ };
16
+
17
+ export const updatePasswordValidators = {
18
+ ...passwordValidators,
19
+ oldPassword: [isNotEmpty],
20
+ };
@@ -0,0 +1,17 @@
1
+ import { Field, InputType } from 'type-graphql';
2
+ import { isNotEmpty } from '@os-team/graphql-validators';
3
+ import { trim } from '@os-team/graphql-transformers';
4
+
5
+ @InputType()
6
+ export class UpdateProfileInput {
7
+ @Field()
8
+ name!: string;
9
+ }
10
+
11
+ export const updateProfileTransformers = {
12
+ name: [trim],
13
+ };
14
+
15
+ export const updateProfileValidators = {
16
+ name: [isNotEmpty],
17
+ };
@@ -0,0 +1,11 @@
1
+ export * from './PasswordInput';
2
+ export * from './RegisterInput';
3
+ export * from './LoginInput';
4
+ export * from './ForgotPasswordInput';
5
+ export * from './ConfirmEmailInput';
6
+ export * from './StatusPayload';
7
+ export * from './SendTokenPayload';
8
+ export * from './UpdatePasswordInput';
9
+ export * from './UpdateEmailInput';
10
+ export * from './UpdateProfileInput';
11
+ export * from './RecoverPasswordInput';