@meridianjs/meridian 0.1.35 → 0.1.37
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/api/admin/users/[id]/route.d.ts.map +1 -1
- package/dist/api/admin/users/[id]/route.js +40 -5
- package/dist/api/admin/users/[id]/route.js.map +1 -1
- package/dist/api/admin/users/me/password/_otp-store.d.ts +14 -0
- package/dist/api/admin/users/me/password/_otp-store.d.ts.map +1 -0
- package/dist/api/admin/users/me/password/_otp-store.js +41 -0
- package/dist/api/admin/users/me/password/_otp-store.js.map +1 -0
- package/dist/api/admin/users/me/password/route.d.ts +7 -0
- package/dist/api/admin/users/me/password/route.d.ts.map +1 -0
- package/dist/api/admin/users/me/password/route.js +31 -0
- package/dist/api/admin/users/me/password/route.js.map +1 -0
- package/dist/api/admin/users/me/password/send-otp/route.d.ts +7 -0
- package/dist/api/admin/users/me/password/send-otp/route.d.ts.map +1 -0
- package/dist/api/admin/users/me/password/send-otp/route.js +30 -0
- package/dist/api/admin/users/me/password/send-otp/route.js.map +1 -0
- package/dist/api/admin/workspaces/[id]/members/[userId]/route.d.ts.map +1 -1
- package/dist/api/admin/workspaces/[id]/members/[userId]/route.js +30 -0
- package/dist/api/admin/workspaces/[id]/members/[userId]/route.js.map +1 -1
- package/dist/api/auth/forgot-password/route.d.ts +7 -0
- package/dist/api/auth/forgot-password/route.d.ts.map +1 -0
- package/dist/api/auth/forgot-password/route.js +36 -0
- package/dist/api/auth/forgot-password/route.js.map +1 -0
- package/dist/api/auth/reset-password/route.d.ts +7 -0
- package/dist/api/auth/reset-password/route.d.ts.map +1 -0
- package/dist/api/auth/reset-password/route.js +20 -0
- package/dist/api/auth/reset-password/route.js.map +1 -0
- package/dist/subscribers/comment-created.d.ts.map +1 -1
- package/dist/subscribers/comment-created.js +2 -0
- package/dist/subscribers/comment-created.js.map +1 -1
- package/dist/subscribers/password-otp-requested.d.ts +10 -0
- package/dist/subscribers/password-otp-requested.d.ts.map +1 -0
- package/dist/subscribers/password-otp-requested.js +25 -0
- package/dist/subscribers/password-otp-requested.js.map +1 -0
- package/dist/subscribers/password-reset-requested.d.ts +10 -0
- package/dist/subscribers/password-reset-requested.d.ts.map +1 -0
- package/dist/subscribers/password-reset-requested.js +27 -0
- package/dist/subscribers/password-reset-requested.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/api/admin/users/[id]/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/api/admin/users/[id]/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAqBrD,eAAO,MAAM,KAAK,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAiCtE,CAAA;AAED,eAAO,MAAM,MAAM,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAwBvE,CAAA"}
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
import { requireRoles } from "@meridianjs/auth";
|
|
2
|
+
const ROLE_RANK = {
|
|
3
|
+
"super-admin": 3,
|
|
4
|
+
"admin": 2,
|
|
5
|
+
"moderator": 1,
|
|
6
|
+
"member": 0,
|
|
7
|
+
};
|
|
8
|
+
function actorRank(req) {
|
|
9
|
+
const roles = req.user?.roles ?? [];
|
|
10
|
+
return Math.max(...roles.map((r) => ROLE_RANK[r] ?? 0), 0);
|
|
11
|
+
}
|
|
12
|
+
async function targetRank(req) {
|
|
13
|
+
const userService = req.scope.resolve("userModuleService");
|
|
14
|
+
const target = await userService.retrieveUser(req.params.id);
|
|
15
|
+
return ROLE_RANK[target.role] ?? 0;
|
|
16
|
+
}
|
|
2
17
|
export const PATCH = async (req, res, next) => {
|
|
3
|
-
requireRoles("super-admin")(req, res, async () => {
|
|
18
|
+
requireRoles("super-admin", "admin")(req, res, async () => {
|
|
4
19
|
try {
|
|
5
|
-
const
|
|
20
|
+
const actor = actorRank(req);
|
|
21
|
+
const target = await targetRank(req);
|
|
22
|
+
if (target >= actor) {
|
|
23
|
+
res.status(403).json({ error: { message: "You cannot change the role of a user at or above your level" } });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
6
26
|
const { role } = req.body;
|
|
7
|
-
const allowed = ["super-admin", "admin", "member"];
|
|
27
|
+
const allowed = ["super-admin", "admin", "moderator", "member"];
|
|
8
28
|
if (!allowed.includes(role)) {
|
|
9
29
|
res.status(400).json({ error: { message: `Invalid role. Must be one of: ${allowed.join(", ")}` } });
|
|
10
30
|
return;
|
|
11
31
|
}
|
|
32
|
+
// Cannot promote someone to your level or above
|
|
33
|
+
if ((ROLE_RANK[role] ?? 0) >= actor) {
|
|
34
|
+
res.status(403).json({ error: { message: "You cannot assign a role equal to or above your own" } });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const userService = req.scope.resolve("userModuleService");
|
|
12
38
|
const user = await userService.updateUser(req.params.id, { role });
|
|
13
39
|
await userService.revokeAllUserSessions(req.params.id).catch(() => { });
|
|
14
40
|
const { password_hash: _, ...safe } = user;
|
|
@@ -20,11 +46,20 @@ export const PATCH = async (req, res, next) => {
|
|
|
20
46
|
});
|
|
21
47
|
};
|
|
22
48
|
export const DELETE = async (req, res, next) => {
|
|
23
|
-
requireRoles("super-admin")(req, res, async () => {
|
|
49
|
+
requireRoles("super-admin", "admin")(req, res, async () => {
|
|
24
50
|
try {
|
|
51
|
+
const actor = actorRank(req);
|
|
52
|
+
const target = await targetRank(req);
|
|
53
|
+
if (target >= actor) {
|
|
54
|
+
res.status(403).json({ error: { message: "You cannot delete a user at or above your level" } });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (req.params.id === req.user?.id) {
|
|
58
|
+
res.status(400).json({ error: { message: "You cannot delete yourself" } });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
25
61
|
const userService = req.scope.resolve("userModuleService");
|
|
26
62
|
await userService.softDeleteUser(req.params.id);
|
|
27
|
-
// Immediately invalidate all active sessions so the user is logged out
|
|
28
63
|
await userService.revokeAllUserSessions(req.params.id).catch(() => { });
|
|
29
64
|
res.json({ success: true });
|
|
30
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/admin/users/[id]/route.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE/C,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACzE,YAAY,CAAC,aAAa,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE;
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/admin/users/[id]/route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory OTP store for password create/change flow.
|
|
3
|
+
*
|
|
4
|
+
* One active OTP per user. 6-digit numeric code, 10-minute TTL.
|
|
5
|
+
* Rate-limited: rejects re-sends within 60 seconds.
|
|
6
|
+
*
|
|
7
|
+
* Uses globalThis to guarantee a single shared Map even when route files
|
|
8
|
+
* are loaded via separate dynamic import() calls by the route scanner.
|
|
9
|
+
*/
|
|
10
|
+
/** Generate and store a 6-digit OTP for a user. Throws if rate-limited. */
|
|
11
|
+
export declare function generateAndStoreOtp(userId: string): string;
|
|
12
|
+
/** Consume an OTP. Always deletes the entry (single-use). Returns true if valid. */
|
|
13
|
+
export declare function consumeOtp(userId: string, otp: string): boolean;
|
|
14
|
+
//# sourceMappingURL=_otp-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_otp-store.d.ts","sourceRoot":"","sources":["../../../../../../src/api/admin/users/me/password/_otp-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqBH,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAU1D;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAM/D"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory OTP store for password create/change flow.
|
|
3
|
+
*
|
|
4
|
+
* One active OTP per user. 6-digit numeric code, 10-minute TTL.
|
|
5
|
+
* Rate-limited: rejects re-sends within 60 seconds.
|
|
6
|
+
*
|
|
7
|
+
* Uses globalThis to guarantee a single shared Map even when route files
|
|
8
|
+
* are loaded via separate dynamic import() calls by the route scanner.
|
|
9
|
+
*/
|
|
10
|
+
import { randomInt } from "crypto";
|
|
11
|
+
const STORE_KEY = "__meridian_password_otp_store__";
|
|
12
|
+
const OTP_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
13
|
+
const RESEND_COOLDOWN_MS = 60 * 1000; // 60 seconds
|
|
14
|
+
function getStore() {
|
|
15
|
+
if (!globalThis[STORE_KEY]) {
|
|
16
|
+
;
|
|
17
|
+
globalThis[STORE_KEY] = new Map();
|
|
18
|
+
}
|
|
19
|
+
return globalThis[STORE_KEY];
|
|
20
|
+
}
|
|
21
|
+
/** Generate and store a 6-digit OTP for a user. Throws if rate-limited. */
|
|
22
|
+
export function generateAndStoreOtp(userId) {
|
|
23
|
+
const store = getStore();
|
|
24
|
+
const existing = store.get(userId);
|
|
25
|
+
if (existing && Date.now() - existing.sentAt < RESEND_COOLDOWN_MS) {
|
|
26
|
+
throw Object.assign(new Error("Please wait before requesting a new code"), { status: 429 });
|
|
27
|
+
}
|
|
28
|
+
const otp = String(randomInt(100000, 999999));
|
|
29
|
+
store.set(userId, { otp, expiresAt: Date.now() + OTP_TTL_MS, sentAt: Date.now() });
|
|
30
|
+
return otp;
|
|
31
|
+
}
|
|
32
|
+
/** Consume an OTP. Always deletes the entry (single-use). Returns true if valid. */
|
|
33
|
+
export function consumeOtp(userId, otp) {
|
|
34
|
+
const store = getStore();
|
|
35
|
+
const entry = store.get(userId);
|
|
36
|
+
store.delete(userId);
|
|
37
|
+
if (!entry || entry.expiresAt < Date.now())
|
|
38
|
+
return false;
|
|
39
|
+
return entry.otp === otp;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=_otp-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_otp-store.js","sourceRoot":"","sources":["../../../../../../src/api/admin/users/me/password/_otp-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAQlC,MAAM,SAAS,GAAG,iCAAiC,CAAA;AACnD,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA,CAAE,aAAa;AAChD,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAA,CAAC,aAAa;AAElD,SAAS,QAAQ;IACf,IAAI,CAAE,UAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;QACpC,CAAC;QAAC,UAAkB,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,EAAoB,CAAA;IAC/D,CAAC;IACD,OAAQ,UAAkB,CAAC,SAAS,CAAC,CAAA;AACvC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;IACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAClC,IAAI,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QAClE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,0CAA0C,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC7F,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC7C,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAClF,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,GAAW;IACpD,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;IACxB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC/B,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACpB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,KAAK,CAAA;IACxD,OAAO,KAAK,CAAC,GAAG,KAAK,GAAG,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* POST /admin/users/me/password
|
|
4
|
+
* Set or change the authenticated user's password after OTP verification.
|
|
5
|
+
*/
|
|
6
|
+
export declare const POST: (req: any, res: Response) => Promise<void>;
|
|
7
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/api/admin/users/me/password/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAQvC;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAsBjD,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { consumeOtp } from "./_otp-store.js";
|
|
3
|
+
const setPasswordSchema = z.object({
|
|
4
|
+
otp: z.string().length(6, "Verification code must be 6 digits"),
|
|
5
|
+
new_password: z.string().min(8, "Password must be at least 8 characters"),
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* POST /admin/users/me/password
|
|
9
|
+
* Set or change the authenticated user's password after OTP verification.
|
|
10
|
+
*/
|
|
11
|
+
export const POST = async (req, res) => {
|
|
12
|
+
const userId = req.user?.id;
|
|
13
|
+
if (!userId) {
|
|
14
|
+
res.status(401).json({ error: { message: "Not authenticated" } });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const result = setPasswordSchema.safeParse(req.body);
|
|
18
|
+
if (!result.success) {
|
|
19
|
+
res.status(400).json({ error: { message: "Validation error", details: result.error.flatten().fieldErrors } });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const valid = consumeOtp(userId, result.data.otp);
|
|
23
|
+
if (!valid) {
|
|
24
|
+
res.status(400).json({ error: { message: "Invalid or expired verification code" } });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const authService = req.scope.resolve("authModuleService");
|
|
28
|
+
await authService.setPassword(userId, result.data.new_password);
|
|
29
|
+
res.json({ ok: true });
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../src/api/admin/users/me/password/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,oCAAoC,CAAC;IAC/D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wCAAwC,CAAC;CAC1E,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACpD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,EAAY,CAAA;IACrC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;QACjE,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACpD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QAC7G,OAAM;IACR,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,sCAAsC,EAAE,EAAE,CAAC,CAAA;QACpF,OAAM;IACR,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC/D,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;AACxB,CAAC,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* POST /admin/users/me/password/send-otp
|
|
4
|
+
* Send a 6-digit OTP to the authenticated user's email for password create/change.
|
|
5
|
+
*/
|
|
6
|
+
export declare const POST: (req: any, res: Response) => Promise<void>;
|
|
7
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/api/admin/users/me/password/send-otp/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBA0BjD,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { generateAndStoreOtp } from "../_otp-store.js";
|
|
2
|
+
/**
|
|
3
|
+
* POST /admin/users/me/password/send-otp
|
|
4
|
+
* Send a 6-digit OTP to the authenticated user's email for password create/change.
|
|
5
|
+
*/
|
|
6
|
+
export const POST = async (req, res) => {
|
|
7
|
+
const userId = req.user?.id;
|
|
8
|
+
if (!userId) {
|
|
9
|
+
res.status(401).json({ error: { message: "Not authenticated" } });
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
let otp;
|
|
13
|
+
try {
|
|
14
|
+
otp = generateAndStoreOtp(userId);
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
// Rate-limited — still return 200 to avoid leaking timing info
|
|
18
|
+
res.json({ ok: true });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const userService = req.scope.resolve("userModuleService");
|
|
22
|
+
const user = await userService.retrieveUser(userId);
|
|
23
|
+
const eventBus = req.scope.resolve("eventBus");
|
|
24
|
+
await eventBus.emit({
|
|
25
|
+
name: "password.otp_requested",
|
|
26
|
+
data: { user_id: userId, email: user.email, otp },
|
|
27
|
+
});
|
|
28
|
+
res.json({ ok: true });
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../../src/api/admin/users/me/password/send-otp/route.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAEtD;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACpD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,EAAY,CAAA;IACrC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;QACjE,OAAM;IACR,CAAC;IAED,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;IACnC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,+DAA+D;QAC/D,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;QACtB,OAAM;IACR,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IAEnD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAQ,CAAA;IACrD,MAAM,QAAQ,CAAC,IAAI,CAAC;QAClB,IAAI,EAAE,wBAAwB;QAC9B,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE;KAClD,CAAC,CAAA;IAEF,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;AACxB,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/api/admin/workspaces/[id]/members/[userId]/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/api/admin/workspaces/[id]/members/[userId]/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAerD,eAAO,MAAM,KAAK,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAoCtE,CAAA;AAED,eAAO,MAAM,MAAM,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBA2CvE,CAAA"}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { requirePermission } from "@meridianjs/auth";
|
|
2
|
+
const ROLE_RANK = {
|
|
3
|
+
"super-admin": 3,
|
|
4
|
+
"admin": 2,
|
|
5
|
+
"moderator": 1,
|
|
6
|
+
"member": 0,
|
|
7
|
+
};
|
|
8
|
+
function actorRank(req) {
|
|
9
|
+
const roles = req.user?.roles ?? [];
|
|
10
|
+
return Math.max(...roles.map((r) => ROLE_RANK[r] ?? 0), 0);
|
|
11
|
+
}
|
|
2
12
|
export const PATCH = async (req, res, next) => {
|
|
3
13
|
requirePermission("member:update_role")(req, res, async () => {
|
|
4
14
|
try {
|
|
@@ -13,6 +23,16 @@ export const PATCH = async (req, res, next) => {
|
|
|
13
23
|
res.status(404).json({ error: { message: "Member not found" } });
|
|
14
24
|
return;
|
|
15
25
|
}
|
|
26
|
+
const actor = actorRank(req);
|
|
27
|
+
const targetRank = ROLE_RANK[membership.role] ?? 0;
|
|
28
|
+
if (targetRank >= actor) {
|
|
29
|
+
res.status(403).json({ error: { message: "You cannot change the role of a member at or above your level" } });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if ((ROLE_RANK[role] ?? 0) >= actor) {
|
|
33
|
+
res.status(403).json({ error: { message: "You cannot assign a role equal to or above your own" } });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
16
36
|
const updated = await workspaceMemberService.updateWorkspaceMember(membership.id, { role });
|
|
17
37
|
res.json({ member: updated });
|
|
18
38
|
}
|
|
@@ -32,6 +52,16 @@ export const DELETE = async (req, res, next) => {
|
|
|
32
52
|
res.status(404).json({ error: { message: "Member not found" } });
|
|
33
53
|
return;
|
|
34
54
|
}
|
|
55
|
+
const actor = actorRank(req);
|
|
56
|
+
const targetRank = ROLE_RANK[membership.role] ?? 0;
|
|
57
|
+
if (targetRank >= actor) {
|
|
58
|
+
res.status(403).json({ error: { message: "You cannot remove a member at or above your level" } });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (req.params.userId === req.user?.id) {
|
|
62
|
+
res.status(400).json({ error: { message: "You cannot remove yourself" } });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
35
65
|
// Remove from all projects in this workspace
|
|
36
66
|
const [projects] = await projectService.listAndCountProjects({ workspace_id: req.params.id }, { limit: 1000 });
|
|
37
67
|
await Promise.all(projects.map((p) => projectMemberService.removeProjectMember(p.id, req.params.userId).catch(() => { })));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../../src/api/admin/workspaces/[id]/members/[userId]/route.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEpD,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACzE,iBAAiB,CAAC,oBAAoB,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE;QAC3D,IAAI,CAAC;YACH,MAAM,sBAAsB,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,8BAA8B,CAAQ,CAAA;YACvF,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,IAAI,CAAA;YAEzB,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;gBAChF,OAAM;YACR,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,sBAAsB,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC/F,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAA;gBAChE,OAAM;YACR,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;YAC3F,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAA;QACX,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IAC1E,iBAAiB,CAAC,eAAe,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE;QACtD,IAAI,CAAC;YACH,MAAM,sBAAsB,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,8BAA8B,CAAQ,CAAA;YACvF,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,sBAAsB,CAAQ,CAAA;YACvE,MAAM,oBAAoB,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,4BAA4B,CAAQ,CAAA;YAEnF,MAAM,UAAU,GAAG,MAAM,sBAAsB,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC/F,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAA;gBAChE,OAAM;YACR,CAAC;YAED,6CAA6C;YAC7C,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,cAAc,CAAC,oBAAoB,CAC1D,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAC/B,EAAE,KAAK,EAAE,IAAI,EAAE,CAChB,CAAA;YACD,MAAM,OAAO,CAAC,GAAG,CACf,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CACtB,oBAAoB,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAClF,CACF,CAAA;YAED,MAAM,sBAAsB,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;YACjE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAA;QACX,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../../src/api/admin/workspaces/[id]/members/[userId]/route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* POST /auth/forgot-password
|
|
4
|
+
* Request a password reset link. Always returns 200 to prevent email enumeration.
|
|
5
|
+
*/
|
|
6
|
+
export declare const POST: (req: any, res: Response) => Promise<void>;
|
|
7
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/auth/forgot-password/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAMvC;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBA4BjD,CAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const forgotPasswordSchema = z.object({
|
|
3
|
+
email: z.string().email(),
|
|
4
|
+
});
|
|
5
|
+
/**
|
|
6
|
+
* POST /auth/forgot-password
|
|
7
|
+
* Request a password reset link. Always returns 200 to prevent email enumeration.
|
|
8
|
+
*/
|
|
9
|
+
export const POST = async (req, res) => {
|
|
10
|
+
const result = forgotPasswordSchema.safeParse(req.body);
|
|
11
|
+
if (!result.success) {
|
|
12
|
+
res.status(400).json({ error: { message: "Validation error", details: result.error.flatten().fieldErrors } });
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const authService = req.scope.resolve("authModuleService");
|
|
16
|
+
const resetInfo = await authService.requestPasswordReset(result.data.email);
|
|
17
|
+
// Emit event for email subscriber — but always return 200
|
|
18
|
+
if (resetInfo) {
|
|
19
|
+
try {
|
|
20
|
+
const eventBus = req.scope.resolve("eventBus");
|
|
21
|
+
await eventBus.emit({
|
|
22
|
+
name: "password.reset_requested",
|
|
23
|
+
data: {
|
|
24
|
+
user_id: resetInfo.userId,
|
|
25
|
+
email: resetInfo.email,
|
|
26
|
+
token: resetInfo.token,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Non-fatal — reset was generated, email delivery is best-effort
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
res.json({ ok: true, message: "If an account exists with that email, a reset link has been sent." });
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/auth/forgot-password/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;CAC1B,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACpD,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACvD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QAC7G,OAAM;IACR,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAE3E,0DAA0D;IAC1D,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAQ,CAAA;YACrD,MAAM,QAAQ,CAAC,IAAI,CAAC;gBAClB,IAAI,EAAE,0BAA0B;gBAChC,IAAI,EAAE;oBACJ,OAAO,EAAE,SAAS,CAAC,MAAM;oBACzB,KAAK,EAAE,SAAS,CAAC,KAAK;oBACtB,KAAK,EAAE,SAAS,CAAC,KAAK;iBACvB;aACF,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,iEAAiE;QACnE,CAAC;IACH,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,mEAAmE,EAAE,CAAC,CAAA;AACtG,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/auth/reset-password/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAOvC;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAUjD,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const resetPasswordSchema = z.object({
|
|
3
|
+
token: z.string().min(1, "Reset token is required"),
|
|
4
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
5
|
+
});
|
|
6
|
+
/**
|
|
7
|
+
* POST /auth/reset-password
|
|
8
|
+
* Reset password using a valid reset token.
|
|
9
|
+
*/
|
|
10
|
+
export const POST = async (req, res) => {
|
|
11
|
+
const result = resetPasswordSchema.safeParse(req.body);
|
|
12
|
+
if (!result.success) {
|
|
13
|
+
res.status(400).json({ error: { message: "Validation error", details: result.error.flatten().fieldErrors } });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const authService = req.scope.resolve("authModuleService");
|
|
17
|
+
await authService.resetPassword(result.data.token, result.data.password);
|
|
18
|
+
res.json({ ok: true, message: "Password has been reset. You can now sign in." });
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/auth/reset-password/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,yBAAyB,CAAC;IACnD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wCAAwC,CAAC;CACtE,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACpD,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QAC7G,OAAM;IACR,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,WAAW,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACxE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAC,CAAA;AAClF,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-created.d.ts","sourceRoot":"","sources":["../../src/subscribers/comment-created.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIzE,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC9B;AAED,wBAA8B,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,cAAc,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"comment-created.d.ts","sourceRoot":"","sources":["../../src/subscribers/comment-created.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIzE,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC9B;AAED,wBAA8B,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,cAAc,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAoG7G;AAED,eAAO,MAAM,MAAM,EAAE,gBAA+C,CAAA"}
|
|
@@ -21,6 +21,7 @@ export default async function handler({ event, container }) {
|
|
|
21
21
|
user_id: userId, entity_type: "issue", entity_id: data.issue_id,
|
|
22
22
|
action: "commented", message: "Someone commented on an issue you're involved with",
|
|
23
23
|
workspace_id: issue.workspace_id,
|
|
24
|
+
metadata: { project_id: issue.project_id },
|
|
24
25
|
})));
|
|
25
26
|
// Notify mentioned users (skip those already notified above)
|
|
26
27
|
const mentionedIds = data.mentioned_user_ids ?? [];
|
|
@@ -29,6 +30,7 @@ export default async function handler({ event, container }) {
|
|
|
29
30
|
user_id: userId, entity_type: "issue", entity_id: data.issue_id,
|
|
30
31
|
action: "mentioned", message: `You were mentioned in a comment on [${issue.identifier}]: ${issue.title}`,
|
|
31
32
|
workspace_id: issue.workspace_id,
|
|
33
|
+
metadata: { project_id: issue.project_id },
|
|
32
34
|
})));
|
|
33
35
|
sseManager.broadcast(issue.workspace_id, "comment.created", {
|
|
34
36
|
comment_id: data.comment_id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-created.js","sourceRoot":"","sources":["../../src/subscribers/comment-created.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAS/D,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAsC;IAC5F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACvB,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,oBAAoB,CAAQ,CAAA;IACnE,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,2BAA2B,CAAQ,CAAA;IAE1E,IAAI,KAAU,CAAA;IACd,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAM;IACR,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAA;IACpC,IAAI,KAAK,CAAC,YAAY;QAAE,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IACtF,IAAI,KAAK,CAAC,WAAW;QAAE,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IACxD,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAEjC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAC7C,YAAY,CAAC,kBAAkB,CAAC;QAC9B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ;QAC/D,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,oDAAoD;QAClF,YAAY,EAAE,KAAK,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"comment-created.js","sourceRoot":"","sources":["../../src/subscribers/comment-created.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SubscriberArgs, SubscriberConfig } from "@meridianjs/types";
|
|
2
|
+
interface PasswordOtpRequestedData {
|
|
3
|
+
user_id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
otp: string;
|
|
6
|
+
}
|
|
7
|
+
export default function handler({ event, container }: SubscriberArgs<PasswordOtpRequestedData>): Promise<void>;
|
|
8
|
+
export declare const config: SubscriberConfig;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=password-otp-requested.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-otp-requested.d.ts","sourceRoot":"","sources":["../../src/subscribers/password-otp-requested.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAGzE,UAAU,wBAAwB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,wBAA8B,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,cAAc,CAAC,wBAAwB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBnH;AAED,eAAO,MAAM,MAAM,EAAE,gBAAsD,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { emailHtml, resolveTemplate } from "./_email-helper.js";
|
|
2
|
+
export default async function handler({ event, container }) {
|
|
3
|
+
const data = event.data;
|
|
4
|
+
try {
|
|
5
|
+
const emailService = container.resolve("emailService");
|
|
6
|
+
const tpl = resolveTemplate(container, "password.otp_requested", {
|
|
7
|
+
user: { email: data.email },
|
|
8
|
+
otp: data.otp,
|
|
9
|
+
});
|
|
10
|
+
await emailService.send({
|
|
11
|
+
to: data.email,
|
|
12
|
+
subject: tpl?.subject ?? "Your Meridian verification code",
|
|
13
|
+
text: tpl?.text ?? `Your verification code is: ${data.otp}\n\nThis code expires in 10 minutes. If you didn't request this, you can safely ignore this email.`,
|
|
14
|
+
html: tpl?.html ?? emailHtml(`Your verification code is:<br/><br/>` +
|
|
15
|
+
`<div style="font-size:32px;font-weight:700;letter-spacing:8px;text-align:center;padding:16px 0;font-family:monospace;color:#4f46e5">${data.otp}</div><br/>` +
|
|
16
|
+
`This code expires in 10 minutes. If you didn't request this, you can safely ignore this email.`),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const logger = container.resolve("logger");
|
|
21
|
+
logger.error(`[email] password.otp_requested: ${err instanceof Error ? err.message : String(err)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export const config = { event: "password.otp_requested" };
|
|
25
|
+
//# sourceMappingURL=password-otp-requested.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-otp-requested.js","sourceRoot":"","sources":["../../src/subscribers/password-otp-requested.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAQ/D,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAA4C;IAClG,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IAEvB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,cAAc,CAAQ,CAAA;QAE7D,MAAM,GAAG,GAAG,eAAe,CAAC,SAAS,EAAE,wBAAwB,EAAE;YAC/D,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;YAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,CAAA;QAEF,MAAM,YAAY,CAAC,IAAI,CAAC;YACtB,EAAE,EAAE,IAAI,CAAC,KAAK;YACd,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,iCAAiC;YAC1D,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,8BAA8B,IAAI,CAAC,GAAG,oGAAoG;YAC7J,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,SAAS,CAC1B,sCAAsC;gBACtC,uIAAuI,IAAI,CAAC,GAAG,aAAa;gBAC5J,gGAAgG,CACjG;SACF,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAQ,CAAA;QACjD,MAAM,CAAC,KAAK,CAAC,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACrG,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAqB,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SubscriberArgs, SubscriberConfig } from "@meridianjs/types";
|
|
2
|
+
interface PasswordResetRequestedData {
|
|
3
|
+
user_id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
token: string;
|
|
6
|
+
}
|
|
7
|
+
export default function handler({ event, container }: SubscriberArgs<PasswordResetRequestedData>): Promise<void>;
|
|
8
|
+
export declare const config: SubscriberConfig;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=password-reset-requested.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-reset-requested.d.ts","sourceRoot":"","sources":["../../src/subscribers/password-reset-requested.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAGzE,UAAU,0BAA0B;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAA8B,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,cAAc,CAAC,0BAA0B,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BrH;AAED,eAAO,MAAM,MAAM,EAAE,gBAAwD,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { emailHtml, resolveTemplate } from "./_email-helper.js";
|
|
2
|
+
export default async function handler({ event, container }) {
|
|
3
|
+
const data = event.data;
|
|
4
|
+
try {
|
|
5
|
+
const emailService = container.resolve("emailService");
|
|
6
|
+
const appUrl = process.env.APP_URL ?? "http://localhost:9000";
|
|
7
|
+
const resetLink = `${appUrl}/reset-password?token=${encodeURIComponent(data.token)}`;
|
|
8
|
+
const tpl = resolveTemplate(container, "password.reset_requested", {
|
|
9
|
+
user: { email: data.email },
|
|
10
|
+
reset: { link: resetLink, token: data.token },
|
|
11
|
+
});
|
|
12
|
+
await emailService.send({
|
|
13
|
+
to: data.email,
|
|
14
|
+
subject: tpl?.subject ?? "Reset your Meridian password",
|
|
15
|
+
text: tpl?.text ?? `You requested a password reset. Use this link to set a new password (valid for 30 minutes):\n\n${resetLink}\n\nIf you didn't request this, you can safely ignore this email.`,
|
|
16
|
+
html: tpl?.html ?? emailHtml(`You requested a password reset.<br/><br/>` +
|
|
17
|
+
`<a href="${resetLink}" style="display:inline-block;padding:10px 20px;background:#4f46e5;color:#fff;text-decoration:none;border-radius:6px;font-weight:600">Reset Password</a><br/><br/>` +
|
|
18
|
+
`This link is valid for 30 minutes. If you didn't request this, you can safely ignore this email.`),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
const logger = container.resolve("logger");
|
|
23
|
+
logger.error(`[email] password.reset_requested: ${err instanceof Error ? err.message : String(err)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export const config = { event: "password.reset_requested" };
|
|
27
|
+
//# sourceMappingURL=password-reset-requested.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-reset-requested.js","sourceRoot":"","sources":["../../src/subscribers/password-reset-requested.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAQ/D,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAA8C;IACpG,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IAEvB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,cAAc,CAAQ,CAAA;QAE7D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,uBAAuB,CAAA;QAC7D,MAAM,SAAS,GAAG,GAAG,MAAM,yBAAyB,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAA;QAEpF,MAAM,GAAG,GAAG,eAAe,CAAC,SAAS,EAAE,0BAA0B,EAAE;YACjE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;YAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;SAC9C,CAAC,CAAA;QAEF,MAAM,YAAY,CAAC,IAAI,CAAC;YACtB,EAAE,EAAE,IAAI,CAAC,KAAK;YACd,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,8BAA8B;YACvD,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,kGAAkG,SAAS,mEAAmE;YACjM,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,SAAS,CAC1B,2CAA2C;gBAC3C,YAAY,SAAS,oKAAoK;gBACzL,kGAAkG,CACnG;SACF,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAQ,CAAA;QACjD,MAAM,CAAC,KAAK,CAAC,qCAAqC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACvG,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAqB,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAA"}
|
package/package.json
CHANGED