@rpcbase/auth 0.165.0 → 0.167.0
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/handler-0nKR-P5B.js +84 -0
- package/dist/handler-0nKR-P5B.js.map +1 -0
- package/dist/handler-B8jPw9zh.js +74 -0
- package/dist/handler-B8jPw9zh.js.map +1 -0
- package/dist/handler-BGNM_dz1.js +19 -0
- package/dist/handler-BGNM_dz1.js.map +1 -0
- package/dist/handler-BoWTfKWi.js +56 -0
- package/dist/handler-BoWTfKWi.js.map +1 -0
- package/dist/handler-ClyVm_Us.js +92 -0
- package/dist/handler-ClyVm_Us.js.map +1 -0
- package/dist/handler-DNxEmrtP.js +67 -0
- package/dist/{handler-B_lItxOC.js.map → handler-DNxEmrtP.js.map} +1 -1
- package/dist/handler-DsPS6JXV.js +70 -0
- package/dist/handler-DsPS6JXV.js.map +1 -0
- package/dist/handler-GKdEwnBO.js +75 -0
- package/dist/handler-GKdEwnBO.js.map +1 -0
- package/dist/handler-IBGVDW5w.js +125 -0
- package/dist/handler-IBGVDW5w.js.map +1 -0
- package/dist/handler-J2OwFTyf.js +51 -0
- package/dist/handler-J2OwFTyf.js.map +1 -0
- package/dist/index.js +770 -756
- package/dist/index.js.map +1 -1
- package/dist/middleware-BT3Uput1.js +18 -0
- package/dist/middleware-BT3Uput1.js.map +1 -0
- package/dist/oauth/index.js +636 -761
- package/dist/oauth/index.js.map +1 -1
- package/dist/routes.js +19 -9
- package/dist/routes.js.map +1 -1
- package/dist/schemas-BgOpQqUv.js +3988 -0
- package/dist/schemas-BgOpQqUv.js.map +1 -0
- package/dist/session-BQ06Yo3C.js +20 -0
- package/dist/session-BQ06Yo3C.js.map +1 -0
- package/dist/sign-in-BG2ND3cO.js +18 -0
- package/dist/sign-in-BG2ND3cO.js.map +1 -0
- package/dist/sign-up-BCVi8UC9.js +18 -0
- package/dist/sign-up-BCVi8UC9.js.map +1 -0
- package/package.json +1 -1
- package/dist/handler-BBSeVt7x.js +0 -153
- package/dist/handler-BBSeVt7x.js.map +0 -1
- package/dist/handler-BKSik1j0.js +0 -83
- package/dist/handler-BKSik1j0.js.map +0 -1
- package/dist/handler-BXvh_Nx4.js +0 -95
- package/dist/handler-BXvh_Nx4.js.map +0 -1
- package/dist/handler-B_lItxOC.js +0 -66
- package/dist/handler-BuwKXMOu.js +0 -24
- package/dist/handler-BuwKXMOu.js.map +0 -1
- package/dist/handler-C-hPxnHG.js +0 -68
- package/dist/handler-C-hPxnHG.js.map +0 -1
- package/dist/handler-CoHpAhui.js +0 -81
- package/dist/handler-CoHpAhui.js.map +0 -1
- package/dist/handler-DfZwgoA4.js +0 -94
- package/dist/handler-DfZwgoA4.js.map +0 -1
- package/dist/handler-Eyjkxj9l.js +0 -58
- package/dist/handler-Eyjkxj9l.js.map +0 -1
- package/dist/handler-U3df_2Al.js +0 -59
- package/dist/handler-U3df_2Al.js.map +0 -1
- package/dist/index-1_ZKABa_.js +0 -20
- package/dist/index-1_ZKABa_.js.map +0 -1
- package/dist/index-D94-1Zwh.js +0 -20
- package/dist/index-D94-1Zwh.js.map +0 -1
- package/dist/middleware-8IfSkEEy.js +0 -25
- package/dist/middleware-8IfSkEEy.js.map +0 -1
- package/dist/schemas--f80aXK7.js +0 -4329
- package/dist/schemas--f80aXK7.js.map +0 -1
- package/dist/session-DoTNxRRA.js +0 -20
- package/dist/session-DoTNxRRA.js.map +0 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { n as requestSchema, t as Route } from "./sign-up-BCVi8UC9.js";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { models } from "@rpcbase/db";
|
|
4
|
+
import { hashPasswordForStorage, sendEmail } from "@rpcbase/server";
|
|
5
|
+
//#region src/api/sign-up/handler.ts
|
|
6
|
+
var signUp = async (payload, ctx) => {
|
|
7
|
+
const User = await models.getGlobal("RBUser", ctx);
|
|
8
|
+
const Tenant = await models.getGlobal("RBTenant", ctx);
|
|
9
|
+
const parsed = requestSchema.safeParse(payload);
|
|
10
|
+
if (!parsed.success) {
|
|
11
|
+
ctx.res.status(400);
|
|
12
|
+
return {
|
|
13
|
+
success: false,
|
|
14
|
+
error: "invalid_payload"
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const { email, password, rememberMe: _rememberMe } = parsed.data;
|
|
18
|
+
if (await User.findOne({ email })) {
|
|
19
|
+
console.log("user with email already exists", email);
|
|
20
|
+
ctx.res.status(409);
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: "user_exists"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const hashedPassword = await hashPasswordForStorage(password);
|
|
27
|
+
const tenantId = crypto.randomUUID();
|
|
28
|
+
const emailVerificationCode = crypto.randomInt(0, 1e6).toString().padStart(6, "0");
|
|
29
|
+
const emailVerificationExpiresAt = new Date(Date.now() + 600 * 1e3);
|
|
30
|
+
const user = new User({
|
|
31
|
+
email,
|
|
32
|
+
password: hashedPassword,
|
|
33
|
+
tenants: [tenantId],
|
|
34
|
+
tenantRoles: { [tenantId]: ["owner"] },
|
|
35
|
+
emailVerificationCode,
|
|
36
|
+
emailVerificationExpiresAt
|
|
37
|
+
});
|
|
38
|
+
await user.save();
|
|
39
|
+
try {
|
|
40
|
+
await sendEmail({
|
|
41
|
+
to: email,
|
|
42
|
+
subject: `Verify your email: ${emailVerificationCode}`,
|
|
43
|
+
html: `
|
|
44
|
+
<p>Welcome to rpcbase!</p>
|
|
45
|
+
<p>Your verification code is <strong>${emailVerificationCode}</strong>. It expires in 10 minutes.</p>
|
|
46
|
+
<p>If you didn't request this, you can ignore this message.</p>
|
|
47
|
+
`,
|
|
48
|
+
text: `Welcome to rpcbase! Your verification code is ${emailVerificationCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error("failed to send sign-up email", err);
|
|
52
|
+
try {
|
|
53
|
+
await User.deleteOne({ _id: user._id });
|
|
54
|
+
} catch (rollbackErr) {
|
|
55
|
+
console.error("failed to roll back user after sign-up email failure", rollbackErr);
|
|
56
|
+
}
|
|
57
|
+
ctx.res.status(500);
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: "email_delivery_failed"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
await Tenant.create({
|
|
65
|
+
tenantId,
|
|
66
|
+
name: email
|
|
67
|
+
});
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.warn("failed to create tenant for user", err);
|
|
70
|
+
}
|
|
71
|
+
console.log("created new user", user._id.toString());
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
userId: user._id.toString(),
|
|
75
|
+
tenantId
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
var handler_default = (api) => {
|
|
79
|
+
api.post(Route, signUp);
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { handler_default as default, signUp };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=handler-0nKR-P5B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-0nKR-P5B.js","names":["crypto","Api","Ctx","ApiHandler","models","hashPasswordForStorage","sendEmail","AuthSessionUser","SignUp","signUp","RequestPayload","ResponsePayload","payload","ctx","Promise","User","getGlobal","Tenant","parsed","requestSchema","safeParse","success","res","status","error","email","password","rememberMe","_rememberMe","data","existingUser","findOne","console","log","hashedPassword","tenantId","randomUUID","emailVerificationCode","randomInt","toString","padStart","emailVerificationExpiresAt","Date","now","user","tenants","tenantRoles","save","to","subject","html","text","err","deleteOne","_id","rollbackErr","create","name","warn","userId","api","post","Route"],"sources":["../src/api/sign-up/handler.ts"],"sourcesContent":["import crypto from \"crypto\"\n\nimport { Api, Ctx, ApiHandler } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { hashPasswordForStorage, sendEmail } from \"@rpcbase/server\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SignUp from \"./index\"\n\n\nexport const signUp: ApiHandler<SignUp.RequestPayload, SignUp.ResponsePayload, AuthSessionUser> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SignUp.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n const Tenant = await models.getGlobal(\"RBTenant\", ctx)\n\n const parsed = SignUp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { email, password, rememberMe: _rememberMe } = parsed.data\n\n const existingUser = await User.findOne({ email })\n\n if (existingUser) {\n console.log(\"user with email already exists\", email)\n // For security, we don't want to reveal if an email is registered.\n // But for this implementation, we'll make it simple.\n ctx.res.status(409)\n return { success: false, error: \"user_exists\" }\n }\n\n const hashedPassword = await hashPasswordForStorage(password)\n\n const tenantId = crypto.randomUUID()\n\n const emailVerificationCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const emailVerificationExpiresAt = new Date(Date.now() + 10 * 60 * 1000)\n\n const user = new User({\n email,\n password: hashedPassword,\n tenants: [tenantId],\n tenantRoles: {\n [tenantId]: [\"owner\"],\n },\n emailVerificationCode,\n emailVerificationExpiresAt,\n })\n await user.save()\n\n try {\n await sendEmail({\n to: email,\n subject: `Verify your email: ${emailVerificationCode}`,\n html: `\n <p>Welcome to rpcbase!</p>\n <p>Your verification code is <strong>${emailVerificationCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Welcome to rpcbase! Your verification code is ${emailVerificationCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.error(\"failed to send sign-up email\", err)\n\n try {\n await User.deleteOne({ _id: user._id })\n } catch (rollbackErr) {\n console.error(\"failed to roll back user after sign-up email failure\", rollbackErr)\n }\n\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n\n try {\n await Tenant.create({\n tenantId,\n name: email,\n })\n } catch (err) {\n // best-effort tenant creation; log and continue\n console.warn(\"failed to create tenant for user\", err)\n }\n\n console.log(\"created new user\", user._id.toString())\n\n return { success: true, userId: user._id.toString(), tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(SignUp.Route, signUp)\n}\n"],"mappings":";;;;;AAWA,IAAaS,SAAqF,OAChGG,SACAC,QACoC;CACpC,MAAME,OAAO,MAAMX,OAAOY,UAAU,UAAUH,GAAG;CACjD,MAAMI,SAAS,MAAMb,OAAOY,UAAU,YAAYH,GAAG;CAErD,MAAMK,SAAAA,cAA8BE,UAAUR,OAAO;CAErD,IAAI,CAACM,OAAOG,SAAS;EACnBR,IAAIS,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAGA,MAAM,EAAEC,OAAOC,UAAUC,YAAYC,gBAAgBV,OAAOW;CAI5D,IAAIC,MAFuBf,KAAKgB,QAAQ,EAAEN,MAAM,CAAC,GAE/B;EAChBO,QAAQC,IAAI,kCAAkCR,KAAK;EAGnDZ,IAAIS,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAc;CAChD;CAEA,MAAMU,iBAAiB,MAAM7B,uBAAuBqB,QAAQ;CAE5D,MAAMS,WAAWnC,OAAOoC,WAAW;CAEnC,MAAMC,wBAAwBrC,OAAOsC,UAAU,GAAG,GAAS,CAAC,CAACC,SAAS,CAAC,CAACC,SAAS,GAAG,GAAG;CACvF,MAAMC,6BAA6B,IAAIC,KAAKA,KAAKC,IAAI,IAAI,MAAU,GAAI;CAEvE,MAAMC,OAAO,IAAI7B,KAAK;EACpBU;EACAC,UAAUQ;EACVW,SAAS,CAACV,QAAQ;EAClBW,aAAa,GACVX,WAAW,CAAC,OAAO,EACtB;EACAE;EACAI;CACF,CAAC;CACD,MAAMG,KAAKG,KAAK;CAEhB,IAAI;EACF,MAAMzC,UAAU;GACd0C,IAAIvB;GACJwB,SAAS,sBAAsBZ;GAC/Ba,MAAM;;+CAEmCb,sBAAqB;;;GAG9Dc,MAAM,iDAAiDd,sBAAqB;EAC9E,CAAC;CACH,SAASe,KAAK;EACZpB,QAAQR,MAAM,gCAAgC4B,GAAG;EAEjD,IAAI;GACF,MAAMrC,KAAKsC,UAAU,EAAEC,KAAKV,KAAKU,IAAI,CAAC;EACxC,SAASC,aAAa;GACpBvB,QAAQR,MAAM,wDAAwD+B,WAAW;EACnF;EAEA1C,IAAIS,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAwB;CAC1D;CAEA,IAAI;EACF,MAAMP,OAAOuC,OAAO;GAClBrB;GACAsB,MAAMhC;EACR,CAAC;CACH,SAAS2B,KAAK;EAEZpB,QAAQ0B,KAAK,oCAAoCN,GAAG;CACtD;CAEApB,QAAQC,IAAI,oBAAoBW,KAAKU,IAAIf,SAAS,CAAC;CAEnD,OAAO;EAAElB,SAAS;EAAMsC,QAAQf,KAAKU,IAAIf,SAAS;EAAGJ;CAAS;AAChE;AAEA,IAAA,mBAAgByB,QAA8B;CAC5CA,IAAIC,KAAKrD,OAAcC,MAAM;AAC/B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { n as requestSchema, t as Route } from "./sign-in-BG2ND3cO.js";
|
|
2
|
+
import { n as completeAuthSignIn } from "./session-BQ06Yo3C.js";
|
|
3
|
+
import { models } from "@rpcbase/db";
|
|
4
|
+
import { verifyPasswordFromStorage } from "@rpcbase/server";
|
|
5
|
+
//#region src/api/sign-in/handler.ts
|
|
6
|
+
var signIn = async (payload, ctx) => {
|
|
7
|
+
const User = await models.getGlobal("RBUser", ctx);
|
|
8
|
+
const parsed = requestSchema.safeParse(payload);
|
|
9
|
+
if (!parsed.success) {
|
|
10
|
+
ctx.res.status(400);
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
error: "invalid_payload"
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const { email, password } = parsed.data;
|
|
17
|
+
const user = await User.findOne({ email }, {
|
|
18
|
+
password: 1,
|
|
19
|
+
tenants: 1,
|
|
20
|
+
tenantRoles: 1
|
|
21
|
+
});
|
|
22
|
+
if (!user?.password) {
|
|
23
|
+
ctx.res.status(401);
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
error: "invalid_credentials"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const stored = String(user.password);
|
|
30
|
+
if (!await verifyPasswordFromStorage(password, stored)) {
|
|
31
|
+
if (!stored.startsWith("$scrypt$")) console.warn("auth::sign-in invalid stored password format", user._id.toString());
|
|
32
|
+
ctx.res.status(401);
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: "invalid_credentials"
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const signedInTenants = (user.tenants || []).map(String);
|
|
39
|
+
const defaultTenantId = signedInTenants[0];
|
|
40
|
+
if (!defaultTenantId) {
|
|
41
|
+
ctx.res.status(500);
|
|
42
|
+
return {
|
|
43
|
+
success: false,
|
|
44
|
+
error: "tenant_unavailable"
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const tenantId = defaultTenantId;
|
|
48
|
+
const tenantRolesMap = user.get("tenantRoles");
|
|
49
|
+
const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : void 0;
|
|
50
|
+
if (!completeAuthSignIn(ctx, {
|
|
51
|
+
userId: user._id.toString(),
|
|
52
|
+
currentTenantId: tenantId,
|
|
53
|
+
signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],
|
|
54
|
+
tenantRoles
|
|
55
|
+
})) {
|
|
56
|
+
ctx.res.status(500);
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: "session_unavailable"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
userId: user._id.toString(),
|
|
65
|
+
tenantId
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
var handler_default = (api) => {
|
|
69
|
+
api.post(Route, signIn);
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { handler_default as default };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=handler-B8jPw9zh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-B8jPw9zh.js","names":["Api","ApiHandler","Ctx","models","verifyPasswordFromStorage","completeAuthSignIn","AuthSessionUser","SignIn","signIn","RequestPayload","ResponsePayload","payload","ctx","Promise","User","getGlobal","parsed","requestSchema","safeParse","success","res","status","error","email","password","data","user","findOne","tenants","tenantRoles","stored","String","passwordMatches","startsWith","console","warn","_id","toString","signedInTenants","map","defaultTenantId","tenantId","tenantRolesMap","get","Map","Object","fromEntries","entries","undefined","userId","currentTenantId","length","api","post","Route"],"sources":["../src/api/sign-in/handler.ts"],"sourcesContent":["import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { verifyPasswordFromStorage } from \"@rpcbase/server\"\n\nimport { completeAuthSignIn } from \"../../session\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SignIn from \"./index\"\n\n\nconst signIn: ApiHandler<SignIn.RequestPayload, SignIn.ResponsePayload, AuthSessionUser> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SignIn.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = SignIn.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email, password } = parsed.data\n\n const user = await User.findOne({ email }, { password: 1, tenants: 1, tenantRoles: 1 })\n\n if (!user?.password) {\n ctx.res.status(401)\n return { success: false, error: \"invalid_credentials\" }\n }\n\n const stored = String(user.password)\n const passwordMatches = await verifyPasswordFromStorage(password, stored)\n\n if (!passwordMatches) {\n if (!stored.startsWith(\"$scrypt$\")) {\n console.warn(\"auth::sign-in invalid stored password format\", user._id.toString())\n }\n ctx.res.status(401)\n return { success: false, error: \"invalid_credentials\" }\n }\n\n const signedInTenants = (user.tenants || []).map(String)\n const defaultTenantId = signedInTenants[0]\n if (!defaultTenantId) {\n ctx.res.status(500)\n return { success: false, error: \"tenant_unavailable\" }\n }\n const tenantId = defaultTenantId\n const tenantRolesMap = user.get(\"tenantRoles\") as Map<string, string[]> | undefined\n const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : undefined\n\n if (!completeAuthSignIn(ctx, {\n userId: user._id.toString(),\n currentTenantId: tenantId,\n signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],\n tenantRoles,\n })) {\n ctx.res.status(500)\n return { success: false, error: \"session_unavailable\" }\n }\n\n return { success: true, userId: user._id.toString(), tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(SignIn.Route, signIn)\n}\n"],"mappings":";;;;;AAUA,IAAMQ,SAAqF,OACzFG,SACAC,QACoC;CACpC,MAAME,OAAO,MAAMX,OAAOY,UAAU,UAAUH,GAAG;CAEjD,MAAMI,SAAAA,cAA8BE,UAAUP,OAAO;CAErD,IAAI,CAACK,OAAOG,SAAS;EACnBP,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAEA,MAAM,EAAEC,OAAOC,aAAaR,OAAOS;CAEnC,MAAMC,OAAO,MAAMZ,KAAKa,QAAQ,EAAEJ,MAAM,GAAG;EAAEC,UAAU;EAAGI,SAAS;EAAGC,aAAa;CAAE,CAAC;CAEtF,IAAI,CAACH,MAAMF,UAAU;EACnBZ,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAsB;CACxD;CAEA,MAAMQ,SAASC,OAAOL,KAAKF,QAAQ;CAGnC,IAAI,CAACQ,MAFyB5B,0BAA0BoB,UAAUM,MAAM,GAElD;EACpB,IAAI,CAACA,OAAOG,WAAW,UAAU,GAC/BC,QAAQC,KAAK,gDAAgDT,KAAKU,IAAIC,SAAS,CAAC;EAElFzB,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAsB;CACxD;CAEA,MAAMgB,mBAAmBZ,KAAKE,WAAW,CAAA,EAAA,CAAIW,IAAIR,MAAM;CACvD,MAAMS,kBAAkBF,gBAAgB;CACxC,IAAI,CAACE,iBAAiB;EACpB5B,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAqB;CACvD;CACA,MAAMmB,WAAWD;CACjB,MAAME,iBAAiBhB,KAAKiB,IAAI,aAAa;CAC7C,MAAMd,cAAca,iBAAiBG,OAAOC,YAAYJ,eAAeK,QAAQ,CAAC,IAAIC,KAAAA;CAEpF,IAAI,CAAC3C,mBAAmBO,KAAK;EAC3BqC,QAAQvB,KAAKU,IAAIC,SAAS;EAC1Ba,iBAAiBT;EACjBH,iBAAiBA,gBAAgBa,SAASb,kBAAkB,CAACG,QAAQ;EACrEZ;CACF,CAAC,GAAG;EACFjB,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAsB;CACxD;CAEA,OAAO;EAAEH,SAAS;EAAM8B,QAAQvB,KAAKU,IAAIC,SAAS;EAAGI;CAAS;AAChE;AAEA,IAAA,mBAAgBW,QAA8B;CAC5CA,IAAIC,KAAK9C,OAAcC,MAAM;AAC/B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { i as object, n as boolean } from "./schemas-BgOpQqUv.js";
|
|
2
|
+
//#region src/api/sign-out/index.ts
|
|
3
|
+
var Route = "/api/rb/auth/sign-out";
|
|
4
|
+
object({});
|
|
5
|
+
object({ success: boolean() });
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/api/sign-out/handler.ts
|
|
8
|
+
var handleSignOut = async (_, ctx) => {
|
|
9
|
+
if (!ctx.req.session) return { success: true };
|
|
10
|
+
await new Promise((resolve) => ctx.req.session.destroy(() => resolve()));
|
|
11
|
+
return { success: true };
|
|
12
|
+
};
|
|
13
|
+
var handler_default = (api) => {
|
|
14
|
+
api.post(Route, handleSignOut);
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { handler_default as default };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=handler-BGNM_dz1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-BGNM_dz1.js","names":["z","Route","requestSchema","object","RequestPayload","infer","responseSchema","success","boolean","ResponsePayload","Api","Ctx","SignOut","handleSignOut","_","RequestPayload","ctx","Promise","ResponsePayload","req","session","success","resolve","destroy","api","post","Route"],"sources":["../src/api/sign-out/index.ts","../src/api/sign-out/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/sign-out\"\n\n// No request payload needed for signout\nexport const requestSchema = z.object({})\n\nexport type RequestPayload = z.infer<typeof requestSchema>;\n\nexport const responseSchema = z.object({\n success: z.boolean()\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>;\n","import { Api, Ctx } from \"@rpcbase/api\"\n\nimport * as SignOut from \"./index\"\n\n\nconst handleSignOut = async(_: SignOut.RequestPayload, ctx: Ctx): Promise<SignOut.ResponsePayload> => {\n // Destroy the session\n if (!ctx.req.session) {\n return { success: true }\n }\n\n await new Promise<void>((resolve) => ctx.req.session!.destroy(() => resolve()))\n\n return {\n success: true\n }\n}\n\nexport default (api: Api) => {\n api.post(SignOut.Route, handleSignOut)\n}\n"],"mappings":";;AAGA,IAAaC,QAAQ;AAGQD,OAAS,CAAC,CAAC;AAIVA,OAAS,EACrCO,SAASP,QAAU,EACrB,CAAC;;;ACPD,IAAMa,gBAAgB,OAAMC,GAA2BE,QAA+C;CAEpG,IAAI,CAACA,IAAIG,IAAIC,SACX,OAAO,EAAEC,SAAS,KAAK;CAGzB,MAAM,IAAIJ,SAAeK,YAAYN,IAAIG,IAAIC,QAASG,cAAcD,QAAQ,CAAC,CAAC;CAE9E,OAAO,EACLD,SAAS,KACX;AACF;AAEA,IAAA,mBAAgBG,QAAa;CAC3BA,IAAIC,KAAKb,OAAeC,aAAa;AACvC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { a as string, i as object, n as boolean } from "./schemas-BgOpQqUv.js";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { models } from "@rpcbase/db";
|
|
4
|
+
//#region src/api/verify-password-reset-otp/index.ts
|
|
5
|
+
var Route = "/api/rb/auth/verify-password-reset-otp";
|
|
6
|
+
var requestSchema = object({
|
|
7
|
+
email: string().email(),
|
|
8
|
+
code: string().length(6, "Code must be 6 digits")
|
|
9
|
+
});
|
|
10
|
+
object({
|
|
11
|
+
success: boolean(),
|
|
12
|
+
error: string().optional(),
|
|
13
|
+
resetToken: string().optional()
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/api/verify-password-reset-otp/handler.ts
|
|
17
|
+
var verifyPasswordResetOtp = async (payload, ctx) => {
|
|
18
|
+
const User = await models.getGlobal("RBUser", ctx);
|
|
19
|
+
const parsed = requestSchema.safeParse(payload);
|
|
20
|
+
if (!parsed.success) {
|
|
21
|
+
ctx.res.status(400);
|
|
22
|
+
return {
|
|
23
|
+
success: false,
|
|
24
|
+
error: "invalid_payload"
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const { email, code } = parsed.data;
|
|
28
|
+
const user = await User.findOne({ email });
|
|
29
|
+
const storedCode = user?.passwordResetCode;
|
|
30
|
+
const expiresAt = user?.passwordResetCodeExpiresAt;
|
|
31
|
+
const isExpired = expiresAt instanceof Date && expiresAt.getTime() < Date.now();
|
|
32
|
+
if (!user || !storedCode || storedCode !== code || isExpired) {
|
|
33
|
+
ctx.res.status(400);
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
error: "invalid_code"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const resetToken = crypto.randomBytes(24).toString("hex");
|
|
40
|
+
user.passwordResetCode = void 0;
|
|
41
|
+
user.passwordResetCodeExpiresAt = void 0;
|
|
42
|
+
user.passwordResetToken = resetToken;
|
|
43
|
+
user.passwordResetTokenExpiresAt = new Date(Date.now() + 900 * 1e3);
|
|
44
|
+
await user.save();
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
resetToken
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
var handler_default = (api) => {
|
|
51
|
+
api.post(Route, verifyPasswordResetOtp);
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { handler_default as default };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=handler-BoWTfKWi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-BoWTfKWi.js","names":["z","Route","requestSchema","object","email","string","code","length","RequestPayload","infer","responseSchema","success","boolean","error","optional","resetToken","ResponsePayload","crypto","Api","ApiHandler","Ctx","models","AuthSessionUser","VerifyPasswordResetOtp","verifyPasswordResetOtp","RequestPayload","ResponsePayload","payload","ctx","Promise","User","getGlobal","parsed","requestSchema","safeParse","success","res","status","error","email","code","data","user","findOne","storedCode","passwordResetCode","expiresAt","passwordResetCodeExpiresAt","isExpired","Date","getTime","now","resetToken","randomBytes","toString","undefined","passwordResetToken","passwordResetTokenExpiresAt","save","api","post","Route"],"sources":["../src/api/verify-password-reset-otp/index.ts","../src/api/verify-password-reset-otp/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/verify-password-reset-otp\"\n\nexport const requestSchema = z.object({\n email: z.string().email(),\n code: z.string().length(6, \"Code must be 6 digits\"),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n resetToken: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as VerifyPasswordResetOtp from \"./index\"\n\n\nconst verifyPasswordResetOtp: ApiHandler<\n VerifyPasswordResetOtp.RequestPayload,\n VerifyPasswordResetOtp.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<VerifyPasswordResetOtp.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = VerifyPasswordResetOtp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email, code } = parsed.data\n const user = await User.findOne({ email })\n\n const storedCode = user?.passwordResetCode\n const expiresAt = user?.passwordResetCodeExpiresAt\n const isExpired = expiresAt instanceof Date && expiresAt.getTime() < Date.now()\n\n if (!user || !storedCode || storedCode !== code || isExpired) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_code\" }\n }\n\n const resetToken = crypto.randomBytes(24).toString(\"hex\")\n\n user.passwordResetCode = undefined\n user.passwordResetCodeExpiresAt = undefined\n user.passwordResetToken = resetToken\n user.passwordResetTokenExpiresAt = new Date(Date.now() + 15 * 60 * 1000)\n await user.save()\n\n return { success: true, resetToken }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(VerifyPasswordResetOtp.Route, verifyPasswordResetOtp)\n}\n"],"mappings":";;;;AAGA,IAAaC,QAAQ;AAErB,IAAaC,gBAAgBF,OAAS;CACpCI,OAAOJ,OAAS,CAAC,CAACI,MAAM;CACxBE,MAAMN,OAAS,CAAC,CAACO,OAAO,GAAG,uBAAuB;AACpD,CAAC;AAI6BP,OAAS;CACrCW,SAASX,QAAU;CACnBa,OAAOb,OAAS,CAAC,CAACc,SAAS;CAC3BC,YAAYf,OAAS,CAAC,CAACc,SAAS;AAClC,CAAC;;;ACND,IAAMU,yBAIF,OACFG,SACAC,QACoD;CACpD,MAAME,OAAO,MAAMT,OAAOU,UAAU,UAAUH,GAAG;CAEjD,MAAMI,SAAAA,cAA8CE,UAAUP,OAAO;CAErE,IAAI,CAACK,OAAOG,SAAS;EACnBP,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAEA,MAAM,EAAEC,OAAOC,SAASR,OAAOS;CAC/B,MAAMC,OAAO,MAAMZ,KAAKa,QAAQ,EAAEJ,MAAM,CAAC;CAEzC,MAAMK,aAAaF,MAAMG;CACzB,MAAMC,YAAYJ,MAAMK;CACxB,MAAMC,YAAYF,qBAAqBG,QAAQH,UAAUI,QAAQ,IAAID,KAAKE,IAAI;CAE9E,IAAI,CAACT,QAAQ,CAACE,cAAcA,eAAeJ,QAAQQ,WAAW;EAC5DpB,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAe;CACjD;CAEA,MAAMc,aAAanC,OAAOoC,YAAY,EAAE,CAAC,CAACC,SAAS,KAAK;CAExDZ,KAAKG,oBAAoBU,KAAAA;CACzBb,KAAKK,6BAA6BQ,KAAAA;CAClCb,KAAKc,qBAAqBJ;CAC1BV,KAAKe,8BAA8B,IAAIR,KAAKA,KAAKE,IAAI,IAAI,MAAU,GAAI;CACvE,MAAMT,KAAKgB,KAAK;CAEhB,OAAO;EAAEvB,SAAS;EAAMiB;CAAW;AACrC;AAEA,IAAA,mBAAgBO,QAA8B;CAC5CA,IAAIC,KAAKrC,OAA8BC,sBAAsB;AAC/D"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { a as string, i as object, n as boolean } from "./schemas-BgOpQqUv.js";
|
|
2
|
+
import { n as completeAuthSignIn } from "./session-BQ06Yo3C.js";
|
|
3
|
+
import { models } from "@rpcbase/db";
|
|
4
|
+
//#region src/api/verify-otp/index.ts
|
|
5
|
+
var Route = "/api/rb/auth/verify-otp";
|
|
6
|
+
var requestSchema = object({
|
|
7
|
+
email: string().email(),
|
|
8
|
+
code: string().length(6, "Code must be 6 digits"),
|
|
9
|
+
rememberMe: boolean().default(true)
|
|
10
|
+
});
|
|
11
|
+
object({
|
|
12
|
+
success: boolean(),
|
|
13
|
+
error: string().optional(),
|
|
14
|
+
userId: string().optional(),
|
|
15
|
+
tenantId: string().optional()
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/api/verify-otp/handler.ts
|
|
19
|
+
var verifyOtp = async (payload, ctx) => {
|
|
20
|
+
const User = await models.getGlobal("RBUser", ctx);
|
|
21
|
+
const parsed = requestSchema.safeParse(payload);
|
|
22
|
+
if (!parsed.success) {
|
|
23
|
+
ctx.res.status(400);
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
error: "invalid_payload"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const { email, code } = parsed.data;
|
|
30
|
+
const user = await User.findOne({ email }, {
|
|
31
|
+
emailVerificationCode: 1,
|
|
32
|
+
emailVerificationExpiresAt: 1,
|
|
33
|
+
tenants: 1,
|
|
34
|
+
tenantRoles: 1
|
|
35
|
+
});
|
|
36
|
+
if (!user) {
|
|
37
|
+
ctx.res.status(404);
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: "user_not_found"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const storedCode = user.emailVerificationCode;
|
|
44
|
+
const expiresAt = user.emailVerificationExpiresAt;
|
|
45
|
+
const isExpired = expiresAt instanceof Date && expiresAt.getTime() < Date.now();
|
|
46
|
+
if (!storedCode || storedCode !== code || isExpired) {
|
|
47
|
+
ctx.res.status(400);
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: "invalid_code"
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
user.emailVerificationCode = void 0;
|
|
54
|
+
user.emailVerificationExpiresAt = void 0;
|
|
55
|
+
await user.save();
|
|
56
|
+
const signedInTenants = (user.tenants || []).map(String);
|
|
57
|
+
const defaultTenantId = signedInTenants[0];
|
|
58
|
+
if (!defaultTenantId) {
|
|
59
|
+
ctx.res.status(500);
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
error: "tenant_unavailable"
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const tenantId = defaultTenantId;
|
|
66
|
+
const tenantRolesMap = user.get("tenantRoles");
|
|
67
|
+
const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : void 0;
|
|
68
|
+
if (!completeAuthSignIn(ctx, {
|
|
69
|
+
userId: user._id.toString(),
|
|
70
|
+
currentTenantId: tenantId,
|
|
71
|
+
signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],
|
|
72
|
+
tenantRoles
|
|
73
|
+
})) {
|
|
74
|
+
ctx.res.status(500);
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: "session_unavailable"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
userId: user._id.toString(),
|
|
83
|
+
tenantId
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
var handler_default = (api) => {
|
|
87
|
+
api.post(Route, verifyOtp);
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
export { handler_default as default };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=handler-ClyVm_Us.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-ClyVm_Us.js","names":["z","Route","requestSchema","object","email","string","code","length","rememberMe","boolean","default","RequestPayload","infer","responseSchema","success","error","optional","userId","tenantId","ResponsePayload","Api","ApiHandler","Ctx","models","completeAuthSignIn","AuthSessionUser","VerifyOtp","verifyOtp","RequestPayload","ResponsePayload","payload","ctx","Promise","User","getGlobal","parsed","requestSchema","safeParse","success","res","status","error","email","code","data","user","findOne","emailVerificationCode","emailVerificationExpiresAt","tenants","tenantRoles","storedCode","expiresAt","isExpired","Date","getTime","now","undefined","save","signedInTenants","map","String","defaultTenantId","tenantId","tenantRolesMap","get","Map","Object","fromEntries","entries","userId","_id","toString","currentTenantId","length","api","post","Route"],"sources":["../src/api/verify-otp/index.ts","../src/api/verify-otp/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/verify-otp\"\n\nexport const requestSchema = z.object({\n email: z.string().email(),\n code: z.string().length(6, \"Code must be 6 digits\"),\n rememberMe: z.boolean().default(true),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n userId: z.string().optional(),\n tenantId: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\nimport { completeAuthSignIn } from \"../../session\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as VerifyOtp from \"./index\"\n\n\nconst verifyOtp: ApiHandler<VerifyOtp.RequestPayload, VerifyOtp.ResponsePayload, AuthSessionUser> = async (\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<VerifyOtp.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = VerifyOtp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email, code } = parsed.data\n\n const user = await User.findOne({ email }, { emailVerificationCode: 1, emailVerificationExpiresAt: 1, tenants: 1, tenantRoles: 1 })\n\n if (!user) {\n ctx.res.status(404)\n return { success: false, error: \"user_not_found\" }\n }\n\n const storedCode = user.emailVerificationCode\n const expiresAt = user.emailVerificationExpiresAt\n\n const isExpired = expiresAt instanceof Date && expiresAt.getTime() < Date.now()\n if (!storedCode || storedCode !== code || isExpired) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_code\" }\n }\n\n user.emailVerificationCode = undefined\n user.emailVerificationExpiresAt = undefined\n await user.save()\n\n const signedInTenants = (user.tenants || []).map(String)\n const defaultTenantId = signedInTenants[0]\n if (!defaultTenantId) {\n ctx.res.status(500)\n return { success: false, error: \"tenant_unavailable\" }\n }\n const tenantId = defaultTenantId\n const tenantRolesMap = user.get(\"tenantRoles\") as Map<string, string[]> | undefined\n const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : undefined\n\n if (!completeAuthSignIn(ctx, {\n userId: user._id.toString(),\n currentTenantId: tenantId,\n signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],\n tenantRoles,\n })) {\n ctx.res.status(500)\n return { success: false, error: \"session_unavailable\" }\n }\n\n return { success: true, userId: user._id.toString(), tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(VerifyOtp.Route, verifyOtp)\n}\n"],"mappings":";;;;AAGA,IAAaC,QAAQ;AAErB,IAAaC,gBAAgBF,OAAS;CACpCI,OAAOJ,OAAS,CAAC,CAACI,MAAM;CACxBE,MAAMN,OAAS,CAAC,CAACO,OAAO,GAAG,uBAAuB;CAClDC,YAAYR,QAAU,CAAC,CAACU,QAAQ,IAAI;AACtC,CAAC;AAI6BV,OAAS;CACrCc,SAASd,QAAU;CACnBe,OAAOf,OAAS,CAAC,CAACgB,SAAS;CAC3BC,QAAQjB,OAAS,CAAC,CAACgB,SAAS;CAC5BE,UAAUlB,OAAS,CAAC,CAACgB,SAAS;AAChC,CAAC;;;ACTD,IAAMW,YAA8F,OAClGG,SACAC,QACuC;CACvC,MAAME,OAAO,MAAMV,OAAOW,UAAU,UAAUH,GAAG;CAEjD,MAAMI,SAAAA,cAAiCE,UAAUP,OAAO;CAExD,IAAI,CAACK,OAAOG,SAAS;EACnBP,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAEA,MAAM,EAAEC,OAAOC,SAASR,OAAOS;CAE/B,MAAMC,OAAO,MAAMZ,KAAKa,QAAQ,EAAEJ,MAAM,GAAG;EAAEK,uBAAuB;EAAGC,4BAA4B;EAAGC,SAAS;EAAGC,aAAa;CAAE,CAAC;CAElI,IAAI,CAACL,MAAM;EACTd,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAiB;CACnD;CAEA,MAAMU,aAAaN,KAAKE;CACxB,MAAMK,YAAYP,KAAKG;CAEvB,MAAMK,YAAYD,qBAAqBE,QAAQF,UAAUG,QAAQ,IAAID,KAAKE,IAAI;CAC9E,IAAI,CAACL,cAAcA,eAAeR,QAAQU,WAAW;EACnDtB,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAe;CACjD;CAEAI,KAAKE,wBAAwBU,KAAAA;CAC7BZ,KAAKG,6BAA6BS,KAAAA;CAClC,MAAMZ,KAAKa,KAAK;CAEhB,MAAMC,mBAAmBd,KAAKI,WAAW,CAAA,EAAA,CAAIW,IAAIC,MAAM;CACvD,MAAMC,kBAAkBH,gBAAgB;CACxC,IAAI,CAACG,iBAAiB;EACpB/B,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAqB;CACvD;CACA,MAAMsB,WAAWD;CACjB,MAAME,iBAAiBnB,KAAKoB,IAAI,aAAa;CAC7C,MAAMf,cAAcc,iBAAiBG,OAAOC,YAAYJ,eAAeK,QAAQ,CAAC,IAAIZ,KAAAA;CAEpF,IAAI,CAACjC,mBAAmBO,KAAK;EAC3BuC,QAAQzB,KAAK0B,IAAIC,SAAS;EAC1BC,iBAAiBV;EACjBJ,iBAAiBA,gBAAgBe,SAASf,kBAAkB,CAACI,QAAQ;EACrEb;CACF,CAAC,GAAG;EACFnB,IAAIQ,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAsB;CACxD;CAEA,OAAO;EAAEH,SAAS;EAAMgC,QAAQzB,KAAK0B,IAAIC,SAAS;EAAGT;CAAS;AAChE;AAEA,IAAA,mBAAgBY,QAA8B;CAC5CA,IAAIC,KAAKlD,OAAiBC,SAAS;AACrC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { a as string, i as object, n as boolean } from "./schemas-BgOpQqUv.js";
|
|
2
|
+
import { r as restrictSessionMiddleware } from "./middleware-BT3Uput1.js";
|
|
3
|
+
import { t as AUTHENTICATED_TENANT_ID_HEADER } from "./session-BQ06Yo3C.js";
|
|
4
|
+
//#region src/api/set-active-account/index.ts
|
|
5
|
+
var Route = "/api/rb/auth/set-active-account";
|
|
6
|
+
var requestSchema = object({ tenantId: string().min(1, "Tenant is required") });
|
|
7
|
+
object({
|
|
8
|
+
success: boolean(),
|
|
9
|
+
error: string().optional(),
|
|
10
|
+
tenantId: string().optional()
|
|
11
|
+
});
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/api/set-active-account/handler.ts
|
|
14
|
+
var setActiveAccount = async (payload, ctx) => {
|
|
15
|
+
const parsed = requestSchema.safeParse(payload);
|
|
16
|
+
if (!parsed.success) {
|
|
17
|
+
ctx.res.status(400);
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
error: "invalid_payload"
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const sessionUser = ctx.req.session?.user;
|
|
24
|
+
if (!sessionUser?.id) {
|
|
25
|
+
ctx.res.status(401);
|
|
26
|
+
return {
|
|
27
|
+
success: false,
|
|
28
|
+
error: "unauthorized"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const tenantId = parsed.data.tenantId.trim();
|
|
32
|
+
const signedInTenants = Array.isArray(sessionUser.signedInTenants) ? sessionUser.signedInTenants.map(String) : [];
|
|
33
|
+
if (signedInTenants.length > 0 && !signedInTenants.includes(tenantId)) {
|
|
34
|
+
ctx.res.status(403);
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
error: "invalid_account"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (signedInTenants.length === 0) {
|
|
41
|
+
const currentTenantId = typeof sessionUser.currentTenantId === "string" ? sessionUser.currentTenantId.trim() : "";
|
|
42
|
+
if (!currentTenantId || currentTenantId !== tenantId) {
|
|
43
|
+
ctx.res.status(403);
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
error: "invalid_account"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
ctx.req.session.user = {
|
|
51
|
+
...sessionUser,
|
|
52
|
+
currentTenantId: tenantId
|
|
53
|
+
};
|
|
54
|
+
ctx.res.setHeader(AUTHENTICATED_TENANT_ID_HEADER, tenantId);
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
tenantId
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
var handler_default = (api) => {
|
|
61
|
+
api.use(Route, restrictSessionMiddleware);
|
|
62
|
+
api.post(Route, setActiveAccount);
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { handler_default as default };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=handler-DNxEmrtP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-
|
|
1
|
+
{"version":3,"file":"handler-DNxEmrtP.js","names":["z","Route","requestSchema","object","tenantId","string","min","RequestPayload","infer","responseSchema","success","boolean","error","optional","ResponsePayload","Api","ApiHandler","Ctx","restrictSessionMiddleware","AUTHENTICATED_TENANT_ID_HEADER","AuthSessionUser","SetActiveAccount","setActiveAccount","RequestPayload","ResponsePayload","payload","ctx","Promise","parsed","requestSchema","safeParse","success","res","status","error","sessionUser","req","session","user","id","tenantId","data","trim","signedInTenants","Array","isArray","map","String","length","includes","currentTenantId","setHeader","api","use","Route","post"],"sources":["../src/api/set-active-account/index.ts","../src/api/set-active-account/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/set-active-account\"\n\nexport const requestSchema = z.object({\n tenantId: z.string().min(1, \"Tenant is required\"),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n tenantId: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\n\nimport { restrictSessionMiddleware } from \"../../middleware\"\nimport { AUTHENTICATED_TENANT_ID_HEADER } from \"../../session\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SetActiveAccount from \"./index\"\n\n\nconst setActiveAccount: ApiHandler<\n SetActiveAccount.RequestPayload,\n SetActiveAccount.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SetActiveAccount.ResponsePayload> => {\n const parsed = SetActiveAccount.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const sessionUser = ctx.req.session?.user\n if (!sessionUser?.id) {\n ctx.res.status(401)\n return { success: false, error: \"unauthorized\" }\n }\n\n const tenantId = parsed.data.tenantId.trim()\n const signedInTenants = Array.isArray(sessionUser.signedInTenants)\n ? sessionUser.signedInTenants.map(String)\n : []\n\n if (signedInTenants.length > 0 && !signedInTenants.includes(tenantId)) {\n ctx.res.status(403)\n return { success: false, error: \"invalid_account\" }\n }\n\n if (signedInTenants.length === 0) {\n const currentTenantId = typeof sessionUser.currentTenantId === \"string\" ? sessionUser.currentTenantId.trim() : \"\"\n if (!currentTenantId || currentTenantId !== tenantId) {\n ctx.res.status(403)\n return { success: false, error: \"invalid_account\" }\n }\n }\n\n ctx.req.session.user = {\n ...sessionUser,\n currentTenantId: tenantId,\n }\n ctx.res.setHeader(AUTHENTICATED_TENANT_ID_HEADER, tenantId)\n\n return { success: true, tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.use(SetActiveAccount.Route, restrictSessionMiddleware)\n api.post(SetActiveAccount.Route, setActiveAccount)\n}\n"],"mappings":";;;;AAGA,IAAaC,QAAQ;AAErB,IAAaC,gBAAgBF,OAAS,EACpCI,UAAUJ,OAAS,CAAC,CAACM,IAAI,GAAG,oBAAoB,EAClD,CAAC;AAI6BN,OAAS;CACrCU,SAASV,QAAU;CACnBY,OAAOZ,OAAS,CAAC,CAACa,SAAS;CAC3BT,UAAUJ,OAAS,CAAC,CAACa,SAAS;AAChC,CAAC;;;ACND,IAAMS,mBAIF,OACFG,SACAC,QAC8C;CAC9C,MAAME,SAAAA,cAAwCE,UAAUL,OAAO;CAE/D,IAAI,CAACG,OAAOG,SAAS;EACnBL,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAEA,MAAMC,cAAcT,IAAIU,IAAIC,SAASC;CACrC,IAAI,CAACH,aAAaI,IAAI;EACpBb,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAe;CACjD;CAEA,MAAMM,WAAWZ,OAAOa,KAAKD,SAASE,KAAK;CAC3C,MAAMC,kBAAkBC,MAAMC,QAAQV,YAAYQ,eAAe,IAC7DR,YAAYQ,gBAAgBG,IAAIC,MAAM,IACtC,CAAA;CAEJ,IAAIJ,gBAAgBK,SAAS,KAAK,CAACL,gBAAgBM,SAAST,QAAQ,GAAG;EACrEd,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAEA,IAAIS,gBAAgBK,WAAW,GAAG;EAChC,MAAME,kBAAkB,OAAOf,YAAYe,oBAAoB,WAAWf,YAAYe,gBAAgBR,KAAK,IAAI;EAC/G,IAAI,CAACQ,mBAAmBA,oBAAoBV,UAAU;GACpDd,IAAIM,IAAIC,OAAO,GAAG;GAClB,OAAO;IAAEF,SAAS;IAAOG,OAAO;GAAkB;EACpD;CACF;CAEAR,IAAIU,IAAIC,QAAQC,OAAO;EACrB,GAAGH;EACHe,iBAAiBV;CACnB;CACAd,IAAIM,IAAImB,UAAUhC,gCAAgCqB,QAAQ;CAE1D,OAAO;EAAET,SAAS;EAAMS;CAAS;AACnC;AAEA,IAAA,mBAAgBY,QAA8B;CAC5CA,IAAIC,IAAIhC,OAAwBH,yBAAyB;CACzDkC,IAAIG,KAAKlC,OAAwBC,gBAAgB;AACnD"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { a as string, i as object, t as array } from "./schemas-BgOpQqUv.js";
|
|
2
|
+
import { r as restrictSessionMiddleware } from "./middleware-BT3Uput1.js";
|
|
3
|
+
import { models } from "@rpcbase/db";
|
|
4
|
+
//#region src/api/me/index.ts
|
|
5
|
+
var Route = "/api/rb/auth/me";
|
|
6
|
+
object({});
|
|
7
|
+
object({
|
|
8
|
+
id: string().optional(),
|
|
9
|
+
email: string().email().optional(),
|
|
10
|
+
phone: string().optional(),
|
|
11
|
+
name: string().optional(),
|
|
12
|
+
tenants: array(string()).default([]),
|
|
13
|
+
currentTenantId: string().optional(),
|
|
14
|
+
signedInTenants: array(string()).default([]).optional(),
|
|
15
|
+
error: string().optional()
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/api/me/handler.ts
|
|
19
|
+
var me = async (_payload, ctx) => {
|
|
20
|
+
const sessionUser = ctx.req.session?.user;
|
|
21
|
+
if (!sessionUser?.id) {
|
|
22
|
+
ctx.res.status(401);
|
|
23
|
+
return {
|
|
24
|
+
id: "",
|
|
25
|
+
currentTenantId: "",
|
|
26
|
+
signedInTenants: [],
|
|
27
|
+
tenants: [],
|
|
28
|
+
error: "not_authenticated"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const user = await (await models.getGlobal("RBUser", ctx)).findById(sessionUser.id);
|
|
32
|
+
if (!user) {
|
|
33
|
+
ctx.res.status(404);
|
|
34
|
+
return {
|
|
35
|
+
id: "",
|
|
36
|
+
currentTenantId: "",
|
|
37
|
+
signedInTenants: [],
|
|
38
|
+
tenants: [],
|
|
39
|
+
error: "user_not_found"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const tenantId = typeof sessionUser.currentTenantId === "string" ? sessionUser.currentTenantId.trim() : "";
|
|
43
|
+
if (!tenantId) {
|
|
44
|
+
ctx.res.status(500);
|
|
45
|
+
return {
|
|
46
|
+
id: "",
|
|
47
|
+
currentTenantId: "",
|
|
48
|
+
signedInTenants: [],
|
|
49
|
+
tenants: [],
|
|
50
|
+
error: "tenant_missing"
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
id: user._id.toString(),
|
|
55
|
+
email: user.email,
|
|
56
|
+
phone: user.get("phone"),
|
|
57
|
+
name: user.name,
|
|
58
|
+
tenants: (user.tenants || []).map(String),
|
|
59
|
+
currentTenantId: tenantId,
|
|
60
|
+
signedInTenants: sessionUser.signedInTenants || []
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
var handler_default = (api) => {
|
|
64
|
+
api.use(Route, restrictSessionMiddleware);
|
|
65
|
+
api.get(Route, me);
|
|
66
|
+
};
|
|
67
|
+
//#endregion
|
|
68
|
+
export { handler_default as default };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=handler-DsPS6JXV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-DsPS6JXV.js","names":["z","Route","requestSchema","object","RequestPayload","infer","responseSchema","id","string","optional","email","phone","name","tenants","array","default","currentTenantId","signedInTenants","error","ResponsePayload","Api","ApiHandler","Ctx","models","AuthSessionUser","restrictSessionMiddleware","Me","me","RequestPayload","ResponsePayload","_payload","ctx","Promise","sessionUser","req","session","user","id","res","status","currentTenantId","signedInTenants","tenants","error","User","getGlobal","findById","tenantId","trim","_id","toString","email","phone","get","name","map","String","api","use","Route"],"sources":["../src/api/me/index.ts","../src/api/me/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/me\"\n\nexport const requestSchema = z.object({})\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n id: z.string().optional(),\n email: z.string().email().optional(),\n phone: z.string().optional(),\n name: z.string().optional(),\n tenants: z.array(z.string()).default([]),\n currentTenantId: z.string().optional(),\n signedInTenants: z.array(z.string()).default([]).optional(),\n error: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\nimport type { AuthSessionUser } from \"../../types\"\nimport { restrictSessionMiddleware } from \"../../middleware\"\n\nimport * as Me from \"./index\"\n\n\nconst me: ApiHandler<Me.RequestPayload, Me.ResponsePayload, AuthSessionUser> = async(\n _payload: Me.RequestPayload,\n ctx: Ctx<AuthSessionUser>\n): Promise<Me.ResponsePayload> => {\n const sessionUser = ctx.req.session?.user\n\n if (!sessionUser?.id) {\n ctx.res.status(401)\n return {\n id: \"\",\n currentTenantId: \"\",\n signedInTenants: [],\n tenants: [],\n error: \"not_authenticated\",\n } as unknown as Me.ResponsePayload\n }\n\n const User = await models.getGlobal(\"RBUser\", ctx)\n const user = await User.findById(sessionUser.id)\n\n if (!user) {\n ctx.res.status(404)\n return {\n id: \"\",\n currentTenantId: \"\",\n signedInTenants: [],\n tenants: [],\n error: \"user_not_found\",\n } as unknown as Me.ResponsePayload\n }\n\n const tenantId = typeof sessionUser.currentTenantId === \"string\" ? sessionUser.currentTenantId.trim() : \"\"\n if (!tenantId) {\n ctx.res.status(500)\n return {\n id: \"\",\n currentTenantId: \"\",\n signedInTenants: [],\n tenants: [],\n error: \"tenant_missing\",\n } as unknown as Me.ResponsePayload\n }\n\n return {\n id: user._id.toString(),\n email: user.email,\n phone: user.get(\"phone\") as string | undefined,\n name: user.name,\n tenants: (user.tenants || []).map(String),\n currentTenantId: tenantId,\n signedInTenants: sessionUser.signedInTenants || [],\n }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.use(Me.Route, restrictSessionMiddleware)\n api.get(Me.Route, me)\n}\n"],"mappings":";;;;AAGA,IAAaC,QAAQ;AAEQD,OAAS,CAAC,CAAC;AAGVA,OAAS;CACrCO,IAAIP,OAAS,CAAC,CAACS,SAAS;CACxBC,OAAOV,OAAS,CAAC,CAACU,MAAM,CAAC,CAACD,SAAS;CACnCE,OAAOX,OAAS,CAAC,CAACS,SAAS;CAC3BG,MAAMZ,OAAS,CAAC,CAACS,SAAS;CAC1BI,SAASb,MAAQA,OAAS,CAAC,CAAC,CAACe,QAAQ,CAAA,CAAE;CACvCC,iBAAiBhB,OAAS,CAAC,CAACS,SAAS;CACrCQ,iBAAiBjB,MAAQA,OAAS,CAAC,CAAC,CAACe,QAAQ,CAAA,CAAE,CAAC,CAACN,SAAS;CAC1DS,OAAOlB,OAAS,CAAC,CAACS,SAAS;AAC7B,CAAC;;;ACRD,IAAMkB,KAAyE,OAC7EG,UACAC,QACgC;CAChC,MAAME,cAAcF,IAAIG,IAAIC,SAASC;CAErC,IAAI,CAACH,aAAaI,IAAI;EACpBN,IAAIO,IAAIC,OAAO,GAAG;EAClB,OAAO;GACLF,IAAI;GACJG,iBAAiB;GACjBC,iBAAiB,CAAA;GACjBC,SAAS,CAAA;GACTC,OAAO;EACT;CACF;CAGA,MAAMP,OAAO,OAAMQ,MADArB,OAAOsB,UAAU,UAAUd,GAAG,EAAA,CACzBe,SAASb,YAAYI,EAAE;CAE/C,IAAI,CAACD,MAAM;EACTL,IAAIO,IAAIC,OAAO,GAAG;EAClB,OAAO;GACLF,IAAI;GACJG,iBAAiB;GACjBC,iBAAiB,CAAA;GACjBC,SAAS,CAAA;GACTC,OAAO;EACT;CACF;CAEA,MAAMI,WAAW,OAAOd,YAAYO,oBAAoB,WAAWP,YAAYO,gBAAgBQ,KAAK,IAAI;CACxG,IAAI,CAACD,UAAU;EACbhB,IAAIO,IAAIC,OAAO,GAAG;EAClB,OAAO;GACLF,IAAI;GACJG,iBAAiB;GACjBC,iBAAiB,CAAA;GACjBC,SAAS,CAAA;GACTC,OAAO;EACT;CACF;CAEA,OAAO;EACLN,IAAID,KAAKa,IAAIC,SAAS;EACtBC,OAAOf,KAAKe;EACZC,OAAOhB,KAAKiB,IAAI,OAAO;EACvBC,MAAMlB,KAAKkB;EACXZ,UAAUN,KAAKM,WAAW,CAAA,EAAA,CAAIa,IAAIC,MAAM;EACxChB,iBAAiBO;EACjBN,iBAAiBR,YAAYQ,mBAAmB,CAAA;CAClD;AACF;AAEA,IAAA,mBAAgBgB,QAA8B;CAC5CA,IAAIC,IAAIhC,OAAUD,yBAAyB;CAC3CgC,IAAIJ,IAAI3B,OAAUC,EAAE;AACtB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { a as string, i as object, n as boolean } from "./schemas-BgOpQqUv.js";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { models } from "@rpcbase/db";
|
|
4
|
+
import { sendEmail } from "@rpcbase/server";
|
|
5
|
+
//#region src/api/resend-otp/index.ts
|
|
6
|
+
var Route = "/api/rb/auth/resend-otp";
|
|
7
|
+
var requestSchema = object({ email: string().email() });
|
|
8
|
+
object({
|
|
9
|
+
success: boolean(),
|
|
10
|
+
error: string().optional()
|
|
11
|
+
});
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/api/resend-otp/handler.ts
|
|
14
|
+
var resendOtp = async (payload, ctx) => {
|
|
15
|
+
const User = await models.getGlobal("RBUser", ctx);
|
|
16
|
+
const parsed = requestSchema.safeParse(payload);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
ctx.res.status(400);
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
error: "invalid_payload"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const { email } = parsed.data;
|
|
25
|
+
const user = await User.findOne({ email });
|
|
26
|
+
if (!user) {
|
|
27
|
+
ctx.res.status(404);
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
error: "user_not_found"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const emailVerificationCode = crypto.randomInt(0, 1e6).toString().padStart(6, "0");
|
|
34
|
+
const emailVerificationExpiresAt = new Date(Date.now() + 600 * 1e3);
|
|
35
|
+
user.emailVerificationCode = emailVerificationCode;
|
|
36
|
+
user.emailVerificationExpiresAt = emailVerificationExpiresAt;
|
|
37
|
+
await user.save();
|
|
38
|
+
try {
|
|
39
|
+
await sendEmail({
|
|
40
|
+
to: email,
|
|
41
|
+
subject: `Your verification code: ${emailVerificationCode}`,
|
|
42
|
+
html: `
|
|
43
|
+
<p>Your verification code is <strong>${emailVerificationCode}</strong>. It expires in 10 minutes.</p>
|
|
44
|
+
<p>If you didn't request this, you can ignore this message.</p>
|
|
45
|
+
`,
|
|
46
|
+
text: `Your verification code is ${emailVerificationCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`
|
|
47
|
+
});
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error("failed to resend otp email", err);
|
|
50
|
+
try {
|
|
51
|
+
await User.updateOne({
|
|
52
|
+
_id: user._id,
|
|
53
|
+
emailVerificationCode
|
|
54
|
+
}, { $unset: {
|
|
55
|
+
emailVerificationCode: "",
|
|
56
|
+
emailVerificationExpiresAt: ""
|
|
57
|
+
} });
|
|
58
|
+
} catch (releaseErr) {
|
|
59
|
+
console.error("failed to clear unsent otp", releaseErr);
|
|
60
|
+
}
|
|
61
|
+
ctx.res.status(500);
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: "email_delivery_failed"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { success: true };
|
|
68
|
+
};
|
|
69
|
+
var handler_default = (api) => {
|
|
70
|
+
api.post(Route, resendOtp);
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
export { handler_default as default, resendOtp };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=handler-GKdEwnBO.js.map
|