@driveflux/api-functions 1.0.77-next.0 → 1.0.77
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/auth/confirm.d.ts +5 -2
- package/dist/auth/confirm.d.ts.map +1 -1
- package/dist/auth/confirm.js +52 -36
- package/dist/auth/confirm.js.map +1 -1
- package/dist/auth/consent.d.ts +17 -0
- package/dist/auth/consent.d.ts.map +1 -0
- package/dist/auth/consent.js +58 -0
- package/dist/auth/consent.js.map +1 -0
- package/dist/auth/emails.js +13 -12
- package/dist/auth/formatter.js +5 -5
- package/dist/auth/otp.js +51 -67
- package/dist/auth/otp.js.map +1 -1
- package/dist/auth/register.d.ts +10 -5
- package/dist/auth/register.d.ts.map +1 -1
- package/dist/auth/register.js +77 -74
- package/dist/auth/register.js.map +1 -1
- package/dist/auth/tokens.js +55 -58
- package/dist/auth/verifications.js +52 -53
- package/dist/constants.js +1 -0
- package/dist/create-logger.js +2 -1
- package/dist/mailjet/calls/manage-contacts-in-list.js +6 -5
- package/dist/mailjet/calls/manage-subscription-status.js +5 -4
- package/dist/mailjet/calls/request-service.js +6 -7
- package/dist/mailjet/refresh-email-preferences.js +12 -11
- package/dist/mailjet/set-contact.js +12 -11
- package/dist/mailjet/types.js +2 -1
- package/dist/mailjet/utils/convert-to-array.js +6 -8
- package/dist/mailjet/utils/extract-email-preferences.js +15 -14
- package/dist/mailjet/utils/lists.js +8 -7
- package/dist/mailjet/utils/update-email-references.js +15 -16
- package/dist/notion/client.js +19 -22
- package/dist/notion/helpful.js +9 -6
- package/dist/notion/schemas/block.js +48 -42
- package/dist/notion/schemas/common.js +14 -9
- package/dist/notion/schemas/database.js +60 -62
- package/dist/notion/schemas/emoji.js +2 -1
- package/dist/notion/schemas/file.js +9 -9
- package/dist/notion/schemas/kb.js +6 -5
- package/dist/notion/schemas/page.js +61 -72
- package/dist/notion/schemas/parent.js +5 -4
- package/dist/notion/schemas/user.js +19 -18
- package/dist/reservation/agree.d.ts +2 -308
- package/dist/reservation/agree.d.ts.map +1 -1
- package/dist/reservation/agree.js +23 -21
- package/dist/reservation/agree.js.map +1 -1
- package/dist/reservation/checks.js +4 -3
- package/dist/reservation/display-vehicle.d.ts +36 -36
- package/dist/reservation/display-vehicle.js +83 -73
- package/dist/reservation/display-vehicle.js.map +1 -1
- package/dist/reservation/ensure-user-billing-address.js +11 -9
- package/dist/reservation/fetch-or-create.d.ts +1 -1
- package/dist/reservation/fetch-or-create.d.ts.map +1 -1
- package/dist/reservation/fetch-or-create.js +56 -51
- package/dist/reservation/fetch-or-create.js.map +1 -1
- package/dist/reservation/invoice.js +88 -77
- package/dist/reservation/payer.js +6 -5
- package/dist/reservation/payment-intent-sync.js +6 -4
- package/dist/reservation/reserve.d.ts.map +1 -1
- package/dist/reservation/reserve.js +9 -5
- package/dist/reservation/reserve.js.map +1 -1
- package/dist/reservation/types.d.ts +9 -1
- package/dist/reservation/types.d.ts.map +1 -1
- package/dist/reservation/types.js +2 -1
- package/dist/reservation/vehicle.js +16 -13
- package/dist/slack.js +29 -24
- package/dist/validation.js +79 -77
- package/dist/vehicle/vehicle-pricing/constants.js +19 -22
- package/dist/vehicle/vehicle-pricing/index.js +42 -28
- package/dist/vehicle/vehicle-pricing/types.js +2 -1
- package/package.json +10 -10
package/dist/auth/register.js
CHANGED
|
@@ -13,42 +13,40 @@ import { nanoid } from 'nanoid';
|
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { SALT_ROUNDS } from '../constants.js';
|
|
15
15
|
import { setContactInList } from '../mailjet/set-contact.js';
|
|
16
|
+
import { getConsentCreateData } from './consent.js';
|
|
16
17
|
import { clearToken, verifyToken } from './tokens.js';
|
|
17
18
|
import { sendVerificationEmail } from './verifications.js';
|
|
18
|
-
const
|
|
19
|
+
export const RegistrationValidation = z.object({
|
|
19
20
|
firstName: z.string(),
|
|
20
21
|
lastName: z.string(),
|
|
21
|
-
phoneNumber: z.string().transform((s)=>s.replace(/[\s-]/g, '')),
|
|
22
|
-
authMethod: z.enum([
|
|
23
|
-
'mobile',
|
|
24
|
-
'email',
|
|
25
|
-
'facebook',
|
|
26
|
-
'google',
|
|
27
|
-
'apple'
|
|
28
|
-
]),
|
|
22
|
+
phoneNumber: z.string().transform((s) => s.replace(/[\s-]/g, '')),
|
|
23
|
+
authMethod: z.enum(['mobile', 'email', 'facebook', 'google', 'apple']),
|
|
29
24
|
code: z.string().optional().nullable(),
|
|
30
25
|
keepCode: z.boolean().optional().default(false),
|
|
31
|
-
email: z.
|
|
32
|
-
dateOfBirth: dateValidation
|
|
26
|
+
email: z.email().transform((s) => s.toLowerCase().trim()),
|
|
27
|
+
dateOfBirth: dateValidation
|
|
28
|
+
.refine((d) => {
|
|
33
29
|
return isAfter(d, subYears(new Date(), 25));
|
|
34
30
|
}, {
|
|
35
|
-
path: [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}).optional(),
|
|
31
|
+
path: ['dateOfBirth'],
|
|
32
|
+
message: 'You must be at least 25 years old to signup for the service.',
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
40
35
|
password: z.string().min(6).optional(),
|
|
41
36
|
noMarketing: z.boolean().optional(),
|
|
42
37
|
pageSource: z.string().optional().nullable(),
|
|
43
|
-
skipPassword: z.boolean().optional().nullable()
|
|
38
|
+
skipPassword: z.boolean().optional().nullable(),
|
|
39
|
+
fingerprint: z.string().optional(),
|
|
40
|
+
termsVersion: z.string().optional(),
|
|
41
|
+
userAgent: z.string().optional(),
|
|
42
|
+
ipAddress: z.ipv4().or(z.ipv6()),
|
|
43
|
+
sessionToken: z.string().optional(),
|
|
44
|
+
sessionId: z.string().optional(),
|
|
44
45
|
});
|
|
45
|
-
export const handleRegister = async (
|
|
46
|
-
const { noMarketing, password, authMethod, phoneNumber, code, keepCode, pageSource, skipPassword, ...body } = Body.parse(b);
|
|
46
|
+
export const handleRegister = async ({ noMarketing, password, authMethod, phoneNumber, code, keepCode, pageSource, skipPassword, fingerprint, termsVersion, userAgent, ipAddress, sessionToken, sessionId, ...body }) => {
|
|
47
47
|
let phoneNumberVerified = false;
|
|
48
48
|
if (code) {
|
|
49
|
-
const tokenResult = await verifyToken(code, {
|
|
50
|
-
scope: 'verify-phone'
|
|
51
|
-
});
|
|
49
|
+
const tokenResult = await verifyToken(code, { scope: 'verify-phone' });
|
|
52
50
|
if (tokenResult.err) {
|
|
53
51
|
return new Err(makeProblem(PROBLEM_CONFLICT, 'Invalid OTP token'));
|
|
54
52
|
}
|
|
@@ -62,9 +60,7 @@ export const handleRegister = async (b, xForwardedFor)=>{
|
|
|
62
60
|
}
|
|
63
61
|
// Check if the user exists
|
|
64
62
|
const foundUser = await prisma.user.findFirst({
|
|
65
|
-
where: {
|
|
66
|
-
email: body.email
|
|
67
|
-
}
|
|
63
|
+
where: { email: body.email },
|
|
68
64
|
});
|
|
69
65
|
if (foundUser) {
|
|
70
66
|
return new Err(makeProblem(PROBLEM_CONFLICT, 'A user with this email address already exists. Did you mean to login?'));
|
|
@@ -72,65 +68,72 @@ export const handleRegister = async (b, xForwardedFor)=>{
|
|
|
72
68
|
const formattedPohoneNumber = cleanupPhoneNumber(phoneNumber);
|
|
73
69
|
const id = generateId('User');
|
|
74
70
|
const hashedPassword = await bcrypt.hash(password ?? nanoid(), SALT_ROUNDS);
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
71
|
+
const tokenId = generateId('Token');
|
|
72
|
+
const userCreateData = {
|
|
73
|
+
id,
|
|
74
|
+
password: hashedPassword,
|
|
75
|
+
...body,
|
|
76
|
+
phoneNumber: formattedPohoneNumber,
|
|
77
|
+
preferredCurrency: 'MYR',
|
|
78
|
+
preferredLocale: 'en',
|
|
79
|
+
phoneNumberVerified,
|
|
80
|
+
groups: ['member'],
|
|
81
|
+
signupParams: {
|
|
82
|
+
method: authMethod,
|
|
83
|
+
source: pageSource,
|
|
84
|
+
},
|
|
85
|
+
registrationComplete: true,
|
|
86
|
+
accounts: {
|
|
87
|
+
create: {
|
|
88
|
+
id: generateId('Account'),
|
|
89
|
+
object: 'account',
|
|
90
|
+
type: 'default',
|
|
91
|
+
provider: 'credentials',
|
|
92
|
+
providerAccountId: id,
|
|
95
93
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
},
|
|
95
|
+
tokens: {
|
|
96
|
+
create: {
|
|
97
|
+
id: tokenId,
|
|
98
|
+
expiresAt: addDays(new Date(), 365),
|
|
99
|
+
scope: 'all',
|
|
99
100
|
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const { user: _removeDueToNestedCreate, ...consentCreate } = getConsentCreateData({
|
|
104
|
+
user: userCreateData,
|
|
105
|
+
fingerprint: fingerprint,
|
|
106
|
+
termsVersion: termsVersion,
|
|
107
|
+
ipAddress: ipAddress,
|
|
108
|
+
userAgent: userAgent,
|
|
109
|
+
sessionToken: tokenId,
|
|
110
|
+
consentType: 'terms',
|
|
111
111
|
});
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
const finalCreateData = {
|
|
113
|
+
...userCreateData,
|
|
114
|
+
consents: {
|
|
115
|
+
create: consentCreate,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
const user = await prisma.user.create({
|
|
119
|
+
data: finalCreateData,
|
|
120
|
+
include: {
|
|
121
|
+
tokens: {
|
|
122
|
+
where: {
|
|
123
|
+
id: tokenId,
|
|
124
|
+
},
|
|
119
125
|
},
|
|
120
|
-
|
|
121
|
-
scope: 'all'
|
|
122
|
-
}
|
|
126
|
+
},
|
|
123
127
|
});
|
|
124
128
|
// We don't want this to be a task. It should be instant
|
|
125
129
|
await sendVerificationEmail(user.id);
|
|
126
130
|
if (!noMarketing) {
|
|
127
|
-
await setContactInList(user.id, {
|
|
128
|
-
generalMarketing: true
|
|
129
|
-
});
|
|
131
|
+
await setContactInList(user.id, { generalMarketing: true });
|
|
130
132
|
}
|
|
131
133
|
await refreshEmailPreferences(user.id);
|
|
132
134
|
return new Ok({
|
|
133
|
-
token,
|
|
134
|
-
user
|
|
135
|
+
token: user.tokens[0],
|
|
136
|
+
user,
|
|
135
137
|
});
|
|
136
138
|
};
|
|
139
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/auth/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,4DAA4D,CAAA;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/auth/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,4DAA4D,CAAA;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACpE,OAAO,EAAe,MAAM,EAAyB,MAAM,eAAe,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAuB,MAAM,mBAAmB,CAAA;AAChE,OAAO,MAAM,MAAM,UAAU,CAAA;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAS1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACtC,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC/C,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACzD,WAAW,EAAE,cAAc;SACzB,MAAM,CACN,CAAC,CAAC,EAAE,EAAE;QACL,OAAO,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;IAC5C,CAAC,EACD;QACC,IAAI,EAAE,CAAC,aAAa,CAAC;QACrB,OAAO,EAAE,8DAA8D;KACvE,CACD;SACA,QAAQ,EAAE;IACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtC,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACnC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC/C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAChC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,EACpC,WAAW,EACX,QAAQ,EACR,UAAU,EACV,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACT,YAAY,EACZ,SAAS,EACT,GAAG,IAAI,EACQ,EAAuC,EAAE;IACxD,IAAI,mBAAmB,GAAG,KAAK,CAAA;IAC/B,IAAI,IAAI,EAAE,CAAC;QACV,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAEtE,IAAI,WAAW,CAAC,GAAG,EAAE,CAAC;YACrB,OAAO,IAAI,GAAG,CAAC,WAAW,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,CAAC,CAAA;QACnE,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrC,CAAC;QAED,mBAAmB,GAAG,IAAI,CAAA;IAC3B,CAAC;IAED,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;QAChC,OAAO,IAAI,GAAG,CAAC,WAAW,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,CAAC,CAAA;IACrE,CAAC;IAED,2BAA2B;IAC3B,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QAC7C,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;KAC5B,CAAC,CAAA;IAEF,IAAI,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,GAAG,CACb,WAAW,CACV,gBAAgB,EAChB,uEAAuE,CACvE,CACD,CAAA;IACF,CAAC;IACD,MAAM,qBAAqB,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAA;IAE7D,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA;IAC7B,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,MAAM,EAAE,EAAE,WAAW,CAAC,CAAA;IAE3E,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;IAEnC,MAAM,cAAc,GAAG;QACtB,EAAE;QACF,QAAQ,EAAE,cAAc;QACxB,GAAG,IAAI;QACP,WAAW,EAAE,qBAAqB;QAClC,iBAAiB,EAAE,KAAK;QACxB,eAAe,EAAE,IAAI;QACrB,mBAAmB;QACnB,MAAM,EAAE,CAAC,QAAQ,CAAC;QAClB,YAAY,EAAE;YACb,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,UAAU;SAClB;QACD,oBAAoB,EAAE,IAAI;QAC1B,QAAQ,EAAE;YACT,MAAM,EAAE;gBACP,EAAE,EAAE,UAAU,CAAC,SAAS,CAAC;gBACzB,MAAM,EAAE,SAAS;gBACjB,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,aAAa;gBACvB,iBAAiB,EAAE,EAAE;aACrB;SACD;QACD,MAAM,EAAE;YACP,MAAM,EAAE;gBACP,EAAE,EAAE,OAAO;gBACX,SAAS,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC;gBACnC,KAAK,EAAE,KAAK;aACZ;SACD;KACgC,CAAA;IAElC,MAAM,EAAE,IAAI,EAAE,wBAAwB,EAAE,GAAG,aAAa,EAAE,GACzD,oBAAoB,CAAC;QACpB,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,WAAW;QACxB,YAAY,EAAE,YAAY;QAC1B,SAAS,EAAE,SAAS;QACpB,SAAS,EAAE,SAAS;QACpB,YAAY,EAAE,OAAO;QACrB,WAAW,EAAE,OAAO;KACpB,CAAC,CAAA;IAEH,MAAM,eAAe,GAA2B;QAC/C,GAAG,cAAc;QACjB,QAAQ,EAAE;YACT,MAAM,EAAE,aAAa;SACrB;KACD,CAAA;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACrC,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE;YACR,MAAM,EAAE;gBACP,KAAK,EAAE;oBACN,EAAE,EAAE,OAAO;iBACX;aACD;SACD;KACD,CAAC,CAAA;IAEF,wDAAwD;IACxD,MAAM,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEpC,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,gBAAgB,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC;IACD,MAAM,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEtC,OAAO,IAAI,EAAE,CAAC;QACb,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAU;QAC9B,IAAI;KACJ,CAAC,CAAA;AACH,CAAC,CAAA"}
|
package/dist/auth/tokens.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { prisma } from '@driveflux/db';
|
|
2
2
|
import { generateId } from '@driveflux/db/id';
|
|
3
|
-
import { makeProblem, PROBLEM_CORRUPT, PROBLEM_EXPIRED, PROBLEM_INVALID_DATA, PROBLEM_NOT_FOUND } from '@driveflux/problem';
|
|
3
|
+
import { makeProblem, PROBLEM_CORRUPT, PROBLEM_EXPIRED, PROBLEM_INVALID_DATA, PROBLEM_NOT_FOUND, } from '@driveflux/problem';
|
|
4
4
|
import { Err, Ok } from '@driveflux/result';
|
|
5
5
|
import { addDays } from 'date-fns/addDays';
|
|
6
6
|
import { addMinutes } from 'date-fns/addMinutes';
|
|
7
7
|
import { customAlphabet } from 'nanoid';
|
|
8
|
-
export const createToken = async (userId, type, value, providedExpiresAt)=>{
|
|
8
|
+
export const createToken = async (userId, type, value, providedExpiresAt) => {
|
|
9
9
|
// Generate 6 digits
|
|
10
10
|
const alphabet = '0123456789';
|
|
11
11
|
const nanoid = customAlphabet(alphabet, 6);
|
|
12
12
|
const identifier = !userId ? value : null;
|
|
13
13
|
const scope = type === 'email' ? 'verify-email' : 'verify-phone';
|
|
14
|
-
const expiresAt = providedExpiresAt ||
|
|
14
|
+
const expiresAt = providedExpiresAt ||
|
|
15
|
+
(type === 'email' ? addDays(new Date(), 1) : addMinutes(new Date(), 30));
|
|
15
16
|
if (!userId && !identifier) {
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
@@ -19,69 +20,72 @@ export const createToken = async (userId, type, value, providedExpiresAt)=>{
|
|
|
19
20
|
data: {
|
|
20
21
|
id: generateId('Token'),
|
|
21
22
|
value: nanoid(),
|
|
22
|
-
...userId
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
...(userId
|
|
24
|
+
? {
|
|
25
|
+
user: {
|
|
26
|
+
connect: {
|
|
27
|
+
id: userId,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
27
30
|
}
|
|
28
|
-
|
|
29
|
-
...identifier ? {
|
|
30
|
-
identifier
|
|
31
|
-
} : {},
|
|
31
|
+
: {}),
|
|
32
|
+
...(identifier ? { identifier } : {}),
|
|
32
33
|
expiresAt,
|
|
33
34
|
scope,
|
|
34
35
|
metadata: {
|
|
35
|
-
[type]: value
|
|
36
|
-
}
|
|
37
|
-
}
|
|
36
|
+
[type]: value,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
38
39
|
});
|
|
39
40
|
};
|
|
40
|
-
export const createEmailToken = async (userId, email)=>{
|
|
41
|
+
export const createEmailToken = async (userId, email) => {
|
|
41
42
|
return await createToken(userId, 'email', email);
|
|
42
43
|
};
|
|
43
|
-
export const createSMSToken = async (userId, phoneNumber)=>{
|
|
44
|
+
export const createSMSToken = async (userId, phoneNumber) => {
|
|
44
45
|
return await createToken(userId, 'phoneNumber', phoneNumber);
|
|
45
46
|
};
|
|
46
|
-
export const verifyToken = async (tokenIdOrValue, verifications, option)=>{
|
|
47
|
-
const token = typeof tokenIdOrValue === 'object'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
...option?.includeUser ? {
|
|
59
|
-
include: {
|
|
60
|
-
user: true
|
|
61
|
-
}
|
|
62
|
-
} : {}
|
|
63
|
-
});
|
|
47
|
+
export const verifyToken = async (tokenIdOrValue, verifications, option) => {
|
|
48
|
+
const token = typeof tokenIdOrValue === 'object'
|
|
49
|
+
? tokenIdOrValue
|
|
50
|
+
: (await prisma.token.findFirst({
|
|
51
|
+
where: {
|
|
52
|
+
OR: [{ id: tokenIdOrValue }, { value: tokenIdOrValue }],
|
|
53
|
+
},
|
|
54
|
+
...(option?.includeUser ? { include: { user: true } } : {}),
|
|
55
|
+
}));
|
|
64
56
|
if (!token) {
|
|
65
57
|
return new Err(makeProblem(PROBLEM_NOT_FOUND, 'Token not found'));
|
|
66
58
|
}
|
|
67
59
|
if (token.expiresAt && token.expiresAt.getTime() < Date.now()) {
|
|
68
60
|
return new Err(makeProblem(PROBLEM_EXPIRED, 'This token has expired'));
|
|
69
61
|
}
|
|
70
|
-
if (typeof verifications?.scope !== 'undefined' &&
|
|
62
|
+
if (typeof verifications?.scope !== 'undefined' &&
|
|
63
|
+
token.scope !== verifications.scope) {
|
|
71
64
|
return new Err(makeProblem(PROBLEM_INVALID_DATA, 'Invalid token scope'));
|
|
72
65
|
}
|
|
73
66
|
if (typeof verifications?.metadata !== 'undefined') {
|
|
74
67
|
if (typeof verifications.metadata.phoneNumber !== 'undefined') {
|
|
75
|
-
const tokenPhoneNumber = token?.metadata &&
|
|
76
|
-
|
|
68
|
+
const tokenPhoneNumber = token?.metadata &&
|
|
69
|
+
typeof token.metadata === 'object' &&
|
|
70
|
+
'phoneNumber' in token.metadata
|
|
71
|
+
? token.metadata.phoneNumber
|
|
72
|
+
: undefined;
|
|
73
|
+
const formattedTokenPhoneNumber = `+${(tokenPhoneNumber)?.replace(/[^0-9]/g, '')}`;
|
|
77
74
|
const phoneNumber = verifications.metadata?.phoneNumber;
|
|
78
|
-
if (verifications.scope === 'verify-phone' &&
|
|
75
|
+
if (verifications.scope === 'verify-phone' &&
|
|
76
|
+
phoneNumber !== formattedTokenPhoneNumber &&
|
|
77
|
+
phoneNumber !== tokenPhoneNumber) {
|
|
79
78
|
return new Err(makeProblem(PROBLEM_INVALID_DATA, 'Invalid token data'));
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
if (typeof verifications.metadata.email !== 'undefined') {
|
|
83
|
-
const tokenEmail = token?.metadata &&
|
|
84
|
-
|
|
82
|
+
const tokenEmail = token?.metadata &&
|
|
83
|
+
typeof token.metadata === 'object' &&
|
|
84
|
+
'email' in token.metadata
|
|
85
|
+
? token.metadata.email
|
|
86
|
+
: undefined;
|
|
87
|
+
if (verifications.scope === 'verify-email' &&
|
|
88
|
+
tokenEmail !== verifications.metadata.email) {
|
|
85
89
|
return new Err(makeProblem(PROBLEM_INVALID_DATA, 'Invalid token data'));
|
|
86
90
|
}
|
|
87
91
|
}
|
|
@@ -103,30 +107,23 @@ export const verifyToken = async (tokenIdOrValue, verifications, option)=>{
|
|
|
103
107
|
}
|
|
104
108
|
return new Ok(token);
|
|
105
109
|
};
|
|
106
|
-
export const clearToken = async (tokenId)=>{
|
|
110
|
+
export const clearToken = async (tokenId) => {
|
|
107
111
|
try {
|
|
108
112
|
await prisma.token.delete({
|
|
109
113
|
where: {
|
|
110
|
-
id: tokenId
|
|
111
|
-
}
|
|
114
|
+
id: tokenId,
|
|
115
|
+
},
|
|
112
116
|
});
|
|
113
|
-
}
|
|
114
|
-
|
|
117
|
+
}
|
|
118
|
+
catch (_e) {
|
|
119
|
+
// Nothing to for now
|
|
115
120
|
}
|
|
116
121
|
};
|
|
117
|
-
export const clearExpiredTokens = async ()=>{
|
|
122
|
+
export const clearExpiredTokens = async () => {
|
|
118
123
|
await prisma.token.deleteMany({
|
|
119
124
|
where: {
|
|
120
|
-
OR: [
|
|
121
|
-
|
|
122
|
-
expiresAt: {
|
|
123
|
-
lte: new Date()
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
invalid: true
|
|
128
|
-
}
|
|
129
|
-
]
|
|
130
|
-
}
|
|
125
|
+
OR: [{ expiresAt: { lte: new Date() } }, { invalid: true }],
|
|
126
|
+
},
|
|
131
127
|
});
|
|
132
128
|
};
|
|
129
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -3,21 +3,19 @@ import { prisma } from '@driveflux/db';
|
|
|
3
3
|
import { send } from '@driveflux/email';
|
|
4
4
|
import { verificationEmail } from '@driveflux/email-templates/flux/verification';
|
|
5
5
|
import { enhancedFetch } from '@driveflux/fetch';
|
|
6
|
-
import { makeProblem, PROBLEM_CONFLICT, PROBLEM_EXTERNAL, PROBLEM_INTERNAL, PROBLEM_INVALID_DATA, PROBLEM_NOT_FOUND, PROBLEM_NOT_IMPLEMENTED } from '@driveflux/problem';
|
|
6
|
+
import { makeProblem, PROBLEM_CONFLICT, PROBLEM_EXTERNAL, PROBLEM_INTERNAL, PROBLEM_INVALID_DATA, PROBLEM_NOT_FOUND, PROBLEM_NOT_IMPLEMENTED, } from '@driveflux/problem';
|
|
7
7
|
import { reportProblem } from '@driveflux/reporter';
|
|
8
8
|
import { Err, Ok } from '@driveflux/result';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import { clearToken, createEmailToken, createSMSToken } from './tokens.js';
|
|
11
11
|
const CheckDuplicationBody = z.object({
|
|
12
12
|
phoneNumber: z.string(),
|
|
13
|
-
email: z.string().transform((email)=>email.toLowerCase().trim())
|
|
13
|
+
email: z.string().transform((email) => email.toLowerCase().trim()),
|
|
14
14
|
});
|
|
15
|
-
export const sendVerificationEmail = async (userId, temporaryEmail, next)=>{
|
|
16
|
-
const user = typeof userId === 'string'
|
|
17
|
-
where: {
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
}) : userId;
|
|
15
|
+
export const sendVerificationEmail = async (userId, temporaryEmail, next) => {
|
|
16
|
+
const user = typeof userId === 'string'
|
|
17
|
+
? await prisma.user.findUnique({ where: { id: userId } })
|
|
18
|
+
: userId;
|
|
21
19
|
if (!user) {
|
|
22
20
|
return new Err(makeProblem(PROBLEM_NOT_FOUND, `User ${typeof userId === 'string' ? userId : 'unknown'} not found.`));
|
|
23
21
|
}
|
|
@@ -34,18 +32,18 @@ export const sendVerificationEmail = async (userId, temporaryEmail, next)=>{
|
|
|
34
32
|
}
|
|
35
33
|
const updatedUser = await prisma.user.update({
|
|
36
34
|
where: {
|
|
37
|
-
id: user.id
|
|
35
|
+
id: user.id,
|
|
38
36
|
},
|
|
39
37
|
data: {
|
|
40
|
-
temporaryEmail: destinationEmail
|
|
38
|
+
temporaryEmail: destinationEmail,
|
|
41
39
|
},
|
|
42
40
|
include: {
|
|
43
|
-
tokens: true
|
|
44
|
-
}
|
|
41
|
+
tokens: true,
|
|
42
|
+
},
|
|
45
43
|
});
|
|
46
44
|
const tokens = updatedUser?.tokens;
|
|
47
45
|
if (tokens) {
|
|
48
|
-
await Promise.allSettled(tokens.map(async (userToken)=>{
|
|
46
|
+
await Promise.allSettled(tokens.map(async (userToken) => {
|
|
49
47
|
if (userToken.scope === 'verify-email' && userToken.id !== token.id) {
|
|
50
48
|
return await clearToken(userToken.id);
|
|
51
49
|
}
|
|
@@ -56,13 +54,15 @@ export const sendVerificationEmail = async (userId, temporaryEmail, next)=>{
|
|
|
56
54
|
const query = new URLSearchParams({
|
|
57
55
|
email: destinationEmail,
|
|
58
56
|
token: token.id,
|
|
59
|
-
code: token.value
|
|
57
|
+
code: token.value,
|
|
60
58
|
});
|
|
61
59
|
if (next) {
|
|
62
60
|
query.append('next', next);
|
|
63
61
|
}
|
|
64
62
|
const link = `${config.appUrl}/verify?${query}`;
|
|
65
|
-
const title = temporaryEmail
|
|
63
|
+
const title = temporaryEmail
|
|
64
|
+
? 'Confirm email change'
|
|
65
|
+
: 'Verify your email address';
|
|
66
66
|
try {
|
|
67
67
|
// Generate html
|
|
68
68
|
const html = verificationEmail({
|
|
@@ -70,122 +70,119 @@ export const sendVerificationEmail = async (userId, temporaryEmail, next)=>{
|
|
|
70
70
|
link,
|
|
71
71
|
title: `${title}`,
|
|
72
72
|
newEmail: temporaryEmail,
|
|
73
|
-
isChanging: !!temporaryEmail
|
|
73
|
+
isChanging: !!temporaryEmail,
|
|
74
74
|
});
|
|
75
75
|
// send email
|
|
76
76
|
const result = await send({
|
|
77
77
|
to: {
|
|
78
78
|
name: user.firstName || '',
|
|
79
|
-
address: destinationEmail
|
|
79
|
+
address: destinationEmail,
|
|
80
80
|
},
|
|
81
81
|
subject: `${title}`,
|
|
82
|
-
html
|
|
82
|
+
html,
|
|
83
83
|
});
|
|
84
84
|
if (result.err) {
|
|
85
85
|
console.error('Error sending email:', result.val);
|
|
86
86
|
return new Err(makeProblem('email_not_sent', 'Unable to send the verification email'));
|
|
87
87
|
}
|
|
88
|
-
return new Ok({
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
} catch (e) {
|
|
88
|
+
return new Ok({ success: true, link });
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
93
91
|
return new Err(makeProblem('email_not_sent', e.message));
|
|
94
92
|
}
|
|
95
93
|
};
|
|
96
|
-
export const verifyEmailUsage = async (newEmail)=>{
|
|
94
|
+
export const verifyEmailUsage = async (newEmail) => {
|
|
97
95
|
// TODO: add email regex checking
|
|
98
96
|
if (newEmail.length < 3) {
|
|
99
97
|
return new Err(makeProblem(PROBLEM_INVALID_DATA, 'Email address needs minimum 3 characters'));
|
|
100
98
|
}
|
|
101
99
|
const existingEmail = await prisma.user.findFirst({
|
|
102
100
|
where: {
|
|
103
|
-
email: newEmail
|
|
104
|
-
}
|
|
101
|
+
email: newEmail,
|
|
102
|
+
},
|
|
105
103
|
});
|
|
106
104
|
if (existingEmail) {
|
|
107
105
|
return new Err(makeProblem(PROBLEM_INVALID_DATA, 'The email address is already in use'));
|
|
108
106
|
}
|
|
109
107
|
return new Ok({});
|
|
110
108
|
};
|
|
111
|
-
export const sendVerificationSMS = async (userId, phoneNumber)=>{
|
|
109
|
+
export const sendVerificationSMS = async (userId, phoneNumber) => {
|
|
112
110
|
const token = await createSMSToken(userId, phoneNumber);
|
|
113
111
|
if (!token) {
|
|
114
112
|
return new Err(makeProblem(PROBLEM_NOT_IMPLEMENTED, 'Token was not created'));
|
|
115
113
|
}
|
|
116
114
|
return await sendSms(phoneNumber, token.value);
|
|
117
115
|
};
|
|
118
|
-
const formatPhoneNumber = (phoneNumber)=>{
|
|
116
|
+
const formatPhoneNumber = (phoneNumber) => {
|
|
119
117
|
return phoneNumber.replace(/[^0-9]/g, '');
|
|
120
118
|
};
|
|
121
|
-
const sendSms = async (phoneNumber, code)=>{
|
|
119
|
+
const sendSms = async (phoneNumber, code) => {
|
|
122
120
|
const isInternational = !formatPhoneNumber(phoneNumber).startsWith('60');
|
|
123
121
|
try {
|
|
124
|
-
const sendSmsResult = isInternational
|
|
122
|
+
const sendSmsResult = isInternational
|
|
123
|
+
? await sendVonageSms(phoneNumber, code)
|
|
124
|
+
: await sendEsmsSms(phoneNumber, code);
|
|
125
125
|
if (sendSmsResult.err) {
|
|
126
126
|
return new Err(await reportProblem(makeProblem({
|
|
127
127
|
type: 'public',
|
|
128
128
|
code: PROBLEM_EXTERNAL,
|
|
129
129
|
message: 'Unable to send sms verification',
|
|
130
130
|
privateMetadata: {
|
|
131
|
-
error: sendSmsResult.val
|
|
132
|
-
}
|
|
131
|
+
error: sendSmsResult.val,
|
|
132
|
+
},
|
|
133
133
|
})));
|
|
134
134
|
}
|
|
135
|
-
return new Ok({
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
139
|
-
} catch (e) {
|
|
135
|
+
return new Ok({ success: true, code });
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
140
138
|
return new Err(await reportProblem(makeProblem({
|
|
141
139
|
type: 'public',
|
|
142
140
|
code: PROBLEM_INTERNAL,
|
|
143
141
|
message: 'Unable to send sms verification',
|
|
144
142
|
privateMetadata: {
|
|
145
|
-
error: e
|
|
146
|
-
}
|
|
143
|
+
error: e,
|
|
144
|
+
},
|
|
147
145
|
})));
|
|
148
146
|
}
|
|
149
147
|
};
|
|
150
|
-
export const handleCheckDuplication = async (b)=>{
|
|
148
|
+
export const handleCheckDuplication = async (b) => {
|
|
151
149
|
const { phoneNumber, email } = CheckDuplicationBody.parse(b);
|
|
152
150
|
// Check if the user exists
|
|
153
151
|
const checkForEmail = await prisma.user.findFirst({
|
|
154
152
|
where: {
|
|
155
|
-
email: email.toLowerCase().trim()
|
|
156
|
-
}
|
|
153
|
+
email: email.toLowerCase().trim(),
|
|
154
|
+
},
|
|
157
155
|
});
|
|
158
156
|
if (checkForEmail) {
|
|
159
157
|
return new Err(makeProblem(PROBLEM_CONFLICT, 'This email is already registered. Are you trying to sign in?'));
|
|
160
158
|
}
|
|
161
159
|
const checkForMobile = await prisma.user.findFirst({
|
|
162
160
|
where: {
|
|
163
|
-
phoneNumber: phoneNumber.replace(/[\s-]/g, '')
|
|
164
|
-
}
|
|
161
|
+
phoneNumber: phoneNumber.replace(/[\s-]/g, ''),
|
|
162
|
+
},
|
|
165
163
|
});
|
|
166
164
|
if (checkForMobile) {
|
|
167
165
|
return new Err(makeProblem(PROBLEM_CONFLICT, 'The phone number is already in use. Please try a different one.'));
|
|
168
166
|
}
|
|
169
|
-
return new Ok({
|
|
170
|
-
notDuplicate: true
|
|
171
|
-
});
|
|
167
|
+
return new Ok({ notDuplicate: true });
|
|
172
168
|
};
|
|
173
169
|
/**
|
|
174
170
|
* This function runs only for foreign numbers and sends an SMS containing the OTP code to the foreign number
|
|
175
171
|
* @param phoneNumber user's phone number
|
|
176
172
|
* @param code OTP code
|
|
177
173
|
* @returns result of sending SMS to user
|
|
178
|
-
*/
|
|
174
|
+
*/
|
|
175
|
+
const sendVonageSms = async (phoneNumber, code) => {
|
|
179
176
|
const body = {
|
|
180
177
|
from: 'FLUX',
|
|
181
178
|
to: formatPhoneNumber(phoneNumber),
|
|
182
179
|
text: `FLUX verification code: ${code}`,
|
|
183
180
|
api_key: config.vonage.apiKey,
|
|
184
|
-
api_secret: config.vonage.apiSecret
|
|
181
|
+
api_secret: config.vonage.apiSecret,
|
|
185
182
|
};
|
|
186
183
|
const result = await enhancedFetch('https://rest.nexmo.com/sms/json', {
|
|
187
184
|
method: 'POST',
|
|
188
|
-
body: JSON.stringify(body)
|
|
185
|
+
body: JSON.stringify(body),
|
|
189
186
|
});
|
|
190
187
|
return result;
|
|
191
188
|
};
|
|
@@ -194,17 +191,19 @@ export const handleCheckDuplication = async (b)=>{
|
|
|
194
191
|
* @param phoneNumber user's phone number
|
|
195
192
|
* @param code OTP code
|
|
196
193
|
* @returns result of sending SMS to user
|
|
197
|
-
*/
|
|
194
|
+
*/
|
|
195
|
+
const sendEsmsSms = async (phoneNumber, code) => {
|
|
198
196
|
const body = {
|
|
199
197
|
user: config.esms.apiKey,
|
|
200
198
|
pass: config.esms.apiSecret,
|
|
201
199
|
to: formatPhoneNumber(phoneNumber),
|
|
202
200
|
msg: `RM0.00 FLUX verification code: ${code}`,
|
|
203
|
-
sender: 'FLUX'
|
|
201
|
+
sender: 'FLUX',
|
|
204
202
|
};
|
|
205
203
|
const result = await enhancedFetch('https://api.esms.com.my/sms/send', {
|
|
206
204
|
method: 'POST',
|
|
207
|
-
body: JSON.stringify(body)
|
|
205
|
+
body: JSON.stringify(body),
|
|
208
206
|
});
|
|
209
207
|
return result;
|
|
210
208
|
};
|
|
209
|
+
//# sourceMappingURL=verifications.js.map
|
package/dist/constants.js
CHANGED
package/dist/create-logger.js
CHANGED