@meridianjs/meridian 0.1.27 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/api/admin/config/route.d.ts.map +1 -1
  2. package/dist/api/admin/config/route.js +9 -0
  3. package/dist/api/admin/config/route.js.map +1 -1
  4. package/dist/api/admin/users/me/avatar/route.d.ts +13 -0
  5. package/dist/api/admin/users/me/avatar/route.d.ts.map +1 -0
  6. package/dist/api/admin/users/me/avatar/route.js +51 -0
  7. package/dist/api/admin/users/me/avatar/route.js.map +1 -0
  8. package/dist/api/admin/users/me/google/route.d.ts +7 -0
  9. package/dist/api/admin/users/me/google/route.d.ts.map +1 -0
  10. package/dist/api/admin/users/me/google/route.js +15 -0
  11. package/dist/api/admin/users/me/google/route.js.map +1 -0
  12. package/dist/api/admin/users/me/route.d.ts +14 -0
  13. package/dist/api/admin/users/me/route.d.ts.map +1 -0
  14. package/dist/api/admin/users/me/route.js +43 -0
  15. package/dist/api/admin/users/me/route.js.map +1 -0
  16. package/dist/api/admin/workspaces/[id]/invitations/route.d.ts.map +1 -1
  17. package/dist/api/admin/workspaces/[id]/invitations/route.js +10 -2
  18. package/dist/api/admin/workspaces/[id]/invitations/route.js.map +1 -1
  19. package/dist/api/admin/workspaces/[id]/members/route.d.ts.map +1 -1
  20. package/dist/api/admin/workspaces/[id]/members/route.js +24 -2
  21. package/dist/api/admin/workspaces/[id]/members/route.js.map +1 -1
  22. package/dist/api/auth/google/_exchange-store.d.ts +20 -0
  23. package/dist/api/auth/google/_exchange-store.d.ts.map +1 -0
  24. package/dist/api/auth/google/_exchange-store.js +29 -0
  25. package/dist/api/auth/google/_exchange-store.js.map +1 -0
  26. package/dist/api/auth/google/callback/route.d.ts +7 -0
  27. package/dist/api/auth/google/callback/route.d.ts.map +1 -0
  28. package/dist/api/auth/google/callback/route.js +160 -0
  29. package/dist/api/auth/google/callback/route.js.map +1 -0
  30. package/dist/api/auth/google/exchange/route.d.ts +10 -0
  31. package/dist/api/auth/google/exchange/route.d.ts.map +1 -0
  32. package/dist/api/auth/google/exchange/route.js +22 -0
  33. package/dist/api/auth/google/exchange/route.js.map +1 -0
  34. package/dist/api/auth/google/link/route.d.ts +9 -0
  35. package/dist/api/auth/google/link/route.d.ts.map +1 -0
  36. package/dist/api/auth/google/link/route.js +53 -0
  37. package/dist/api/auth/google/link/route.js.map +1 -0
  38. package/dist/api/auth/google/route.d.ts +7 -0
  39. package/dist/api/auth/google/route.d.ts.map +1 -0
  40. package/dist/api/auth/google/route.js +45 -0
  41. package/dist/api/auth/google/route.js.map +1 -0
  42. package/dist/api/auth/invite/[token]/route.d.ts.map +1 -1
  43. package/dist/api/auth/invite/[token]/route.js +25 -8
  44. package/dist/api/auth/invite/[token]/route.js.map +1 -1
  45. package/dist/api/auth/register/route.d.ts.map +1 -1
  46. package/dist/api/auth/register/route.js +10 -0
  47. package/dist/api/auth/register/route.js.map +1 -1
  48. package/dist/api/auth/setup/route.d.ts.map +1 -1
  49. package/dist/api/auth/setup/route.js +11 -2
  50. package/dist/api/auth/setup/route.js.map +1 -1
  51. package/dist/workflows/create-invitation.d.ts +1 -1
  52. package/dist/workflows/create-invitation.d.ts.map +1 -1
  53. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/admin/config/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAKhD,CAAA"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/admin/config/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAehD,CAAA"}
@@ -1,7 +1,16 @@
1
1
  export const GET = async (req, res) => {
2
2
  const config = req.scope.resolve("config");
3
+ let googleOAuthEnabled = false;
4
+ try {
5
+ req.scope.resolve("googleOAuthService");
6
+ googleOAuthEnabled = true;
7
+ }
8
+ catch {
9
+ // googleOAuthService not registered — feature disabled
10
+ }
3
11
  res.json({
4
12
  maxChildIssueDepth: config?.projectConfig?.maxChildIssueDepth ?? 1,
13
+ googleOAuthEnabled,
5
14
  });
6
15
  };
7
16
  //# sourceMappingURL=route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/admin/config/route.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACnD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAQ,CAAA;IACjD,GAAG,CAAC,IAAI,CAAC;QACP,kBAAkB,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,IAAI,CAAC;KACnE,CAAC,CAAA;AACJ,CAAC,CAAA"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/admin/config/route.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACnD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAQ,CAAA;IAEjD,IAAI,kBAAkB,GAAG,KAAK,CAAA;IAC9B,IAAI,CAAC;QACH,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;QACvC,kBAAkB,GAAG,IAAI,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;IACzD,CAAC;IAED,GAAG,CAAC,IAAI,CAAC;QACP,kBAAkB,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,IAAI,CAAC;QAClE,kBAAkB;KACnB,CAAC,CAAA;AACJ,CAAC,CAAA"}
@@ -0,0 +1,13 @@
1
+ import type { Response } from "express";
2
+ /**
3
+ * POST /admin/users/me/avatar
4
+ * Upload / replace the authenticated user's avatar image.
5
+ * Expects multipart/form-data with field name "avatar".
6
+ */
7
+ export declare const POST: (req: any, res: Response) => Promise<void>;
8
+ /**
9
+ * DELETE /admin/users/me/avatar
10
+ * Remove the authenticated user's avatar.
11
+ */
12
+ export declare const DELETE: (req: any, res: Response) => Promise<void>;
13
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/api/admin/users/me/avatar/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC;;;;GAIG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAyBjD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAiBnD,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { processUpload, deleteUpload } from "../../../../../utils/upload.js";
2
+ /**
3
+ * POST /admin/users/me/avatar
4
+ * Upload / replace the authenticated user's avatar image.
5
+ * Expects multipart/form-data with field name "avatar".
6
+ */
7
+ export const POST = async (req, res) => {
8
+ const userId = req.user?.id;
9
+ if (!userId) {
10
+ res.status(401).json({ error: { message: "Not authenticated" } });
11
+ return;
12
+ }
13
+ const userService = req.scope.resolve("userModuleService");
14
+ const existing = await userService.retrieveUser(userId).catch(() => null);
15
+ if (!existing) {
16
+ res.status(404).json({ error: { message: "User not found." } });
17
+ return;
18
+ }
19
+ const upload = await processUpload(req, res, "avatar", "user-avatars");
20
+ if (!upload) {
21
+ res.status(400).json({ error: { message: "No file uploaded. Use multipart/form-data with field name 'avatar'." } });
22
+ return;
23
+ }
24
+ if (existing.avatar_url)
25
+ await deleteUpload(req, existing.avatar_url);
26
+ const user = await userService.updateUser(userId, { avatar_url: upload.url });
27
+ const { password_hash: _, ...safeUser } = user;
28
+ res.json({ user: safeUser });
29
+ };
30
+ /**
31
+ * DELETE /admin/users/me/avatar
32
+ * Remove the authenticated user's avatar.
33
+ */
34
+ export const DELETE = async (req, res) => {
35
+ const userId = req.user?.id;
36
+ if (!userId) {
37
+ res.status(401).json({ error: { message: "Not authenticated" } });
38
+ return;
39
+ }
40
+ const userService = req.scope.resolve("userModuleService");
41
+ const existing = await userService.retrieveUser(userId).catch(() => null);
42
+ if (!existing) {
43
+ res.status(404).json({ error: { message: "User not found." } });
44
+ return;
45
+ }
46
+ if (existing.avatar_url)
47
+ await deleteUpload(req, existing.avatar_url);
48
+ await userService.updateUser(userId, { avatar_url: null });
49
+ res.json({ ok: true });
50
+ };
51
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../src/api/admin/users/me/avatar/route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Response } from "express";
2
+ /**
3
+ * DELETE /admin/users/me/google
4
+ * Unlinks the Google account from the currently authenticated user.
5
+ */
6
+ export declare const DELETE: (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/google/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAUnD,CAAA"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * DELETE /admin/users/me/google
3
+ * Unlinks the Google account from the currently authenticated user.
4
+ */
5
+ export const DELETE = async (req, res) => {
6
+ const userId = req.user?.id;
7
+ if (!userId) {
8
+ res.status(401).json({ error: { message: "Not authenticated" } });
9
+ return;
10
+ }
11
+ const userService = req.scope.resolve("userModuleService");
12
+ await userService.updateUser(userId, { google_id: null });
13
+ res.json({ ok: true });
14
+ };
15
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../src/api/admin/users/me/google/route.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACtD,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,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACzD,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;AACxB,CAAC,CAAA"}
@@ -0,0 +1,14 @@
1
+ import type { Response } from "express";
2
+ /**
3
+ * GET /admin/users/me
4
+ * Returns the currently authenticated user's profile.
5
+ * Requires JWT (handled by /admin/* middleware).
6
+ */
7
+ export declare const GET: (req: any, res: Response) => Promise<void>;
8
+ /**
9
+ * PATCH /admin/users/me
10
+ * Update the current user's personal details.
11
+ * Allowed fields: first_name, last_name, designation, phone_number.
12
+ */
13
+ export declare const PATCH: (req: any, res: Response) => Promise<void>;
14
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/api/admin/users/me/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC;;;;GAIG;AACH,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAYhD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAmBlD,CAAA"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * GET /admin/users/me
3
+ * Returns the currently authenticated user's profile.
4
+ * Requires JWT (handled by /admin/* middleware).
5
+ */
6
+ export const GET = async (req, res) => {
7
+ const userService = req.scope.resolve("userModuleService");
8
+ const userId = req.user?.id;
9
+ if (!userId) {
10
+ res.status(401).json({ error: { message: "Not authenticated" } });
11
+ return;
12
+ }
13
+ const user = await userService.retrieveUser(userId);
14
+ const { password_hash: _, ...safeUser } = user;
15
+ res.json(safeUser);
16
+ };
17
+ /**
18
+ * PATCH /admin/users/me
19
+ * Update the current user's personal details.
20
+ * Allowed fields: first_name, last_name, designation, phone_number.
21
+ */
22
+ export const PATCH = async (req, res) => {
23
+ const userService = req.scope.resolve("userModuleService");
24
+ const userId = req.user?.id;
25
+ if (!userId) {
26
+ res.status(401).json({ error: { message: "Not authenticated" } });
27
+ return;
28
+ }
29
+ const { first_name, last_name, designation, phone_number } = req.body;
30
+ const updates = {};
31
+ if (first_name !== undefined)
32
+ updates.first_name = first_name;
33
+ if (last_name !== undefined)
34
+ updates.last_name = last_name;
35
+ if (designation !== undefined)
36
+ updates.designation = designation;
37
+ if (phone_number !== undefined)
38
+ updates.phone_number = phone_number;
39
+ const user = await userService.updateUser(userId, updates);
40
+ const { password_hash: _, ...safeUser } = user;
41
+ res.json({ user: safeUser });
42
+ };
43
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/admin/users/me/route.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACnD,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,EAAY,CAAA;IAErC,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,IAAI,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IACnD,MAAM,EAAE,aAAa,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAA;IAC9C,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AACpB,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACrD,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,EAAY,CAAA;IAErC,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,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,IAA+B,CAAA;IAChG,MAAM,OAAO,GAA4B,EAAE,CAAA;IAC3C,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,CAAC,UAAU,GAAG,UAAU,CAAA;IAC7D,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,CAAC,SAAS,GAAG,SAAS,CAAA;IAC1D,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAA;IAChE,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,CAAC,YAAY,GAAG,YAAY,CAAA;IAEnE,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1D,MAAM,EAAE,aAAa,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAA;IAC9C,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;AAC9B,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/invitations/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAevC,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAQhD,CAAA;AAED,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAkCjD,CAAA"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/invitations/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAevC,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAQhD,CAAA;AAED,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBA2CjD,CAAA"}
@@ -22,10 +22,18 @@ export const POST = async (req, res) => {
22
22
  if (!await assertWorkspaceMembership(req, res))
23
23
  return;
24
24
  const { email, role, app_role_id } = req.body;
25
- if (!role || !["admin", "member"].includes(role)) {
26
- res.status(400).json({ error: { message: "role must be 'admin' or 'member'" } });
25
+ if (!role || !["super-admin", "admin", "member"].includes(role)) {
26
+ res.status(400).json({ error: { message: "role must be 'super-admin', 'admin', or 'member'" } });
27
27
  return;
28
28
  }
29
+ // Privilege check: only admins/super-admins can invite admins or super-admins
30
+ if (role !== "member") {
31
+ const callerRoles = req.user?.roles ?? [];
32
+ if (!callerRoles.includes("super-admin") && !callerRoles.includes("admin")) {
33
+ res.status(403).json({ error: { message: "Only admins can invite users with elevated roles" } });
34
+ return;
35
+ }
36
+ }
29
37
  if (email?.trim()) {
30
38
  const userService = req.scope.resolve("userModuleService");
31
39
  const [existing] = await userService.listAndCountUsers({ email: email.trim().toLowerCase() }, { limit: 1 });
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/invitations/route.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/invitations/route.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/members/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGrD,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBA2BhD,CAAA;AAED,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBA4BrE,CAAA"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/members/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGrD,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAsChD,CAAA;AAED,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAyCrE,CAAA"}
@@ -1,5 +1,15 @@
1
1
  import { requirePermission } from "@meridianjs/auth";
2
2
  export const GET = async (req, res) => {
3
+ // Authorization: super-admin/admin see all; members must belong to this workspace
4
+ const roles = req.user?.roles ?? [];
5
+ if (!roles.includes("super-admin") && !roles.includes("admin")) {
6
+ const workspaceMemberService0 = req.scope.resolve("workspaceMemberModuleService");
7
+ const membership = await workspaceMemberService0.getMembership(req.params.id, req.user?.id);
8
+ if (!membership) {
9
+ res.status(403).json({ error: { message: "Forbidden — not a member of this workspace" } });
10
+ return;
11
+ }
12
+ }
3
13
  const workspaceMemberService = req.scope.resolve("workspaceMemberModuleService");
4
14
  const userService = req.scope.resolve("userModuleService");
5
15
  const [members] = await workspaceMemberService.listAndCountWorkspaceMembers({ workspace_id: req.params.id }, { limit: 100 });
@@ -24,7 +34,7 @@ export const POST = async (req, res, next) => {
24
34
  requirePermission("member:invite")(req, res, async () => {
25
35
  try {
26
36
  const workspaceMemberService = req.scope.resolve("workspaceMemberModuleService");
27
- const { user_id, role } = req.body;
37
+ const { user_id, role, app_role_id } = req.body;
28
38
  if (!user_id) {
29
39
  res.status(400).json({ error: { message: "user_id is required" } });
30
40
  return;
@@ -34,11 +44,23 @@ export const POST = async (req, res, next) => {
34
44
  res.status(409).json({ error: { message: "User is already a member of this workspace" } });
35
45
  return;
36
46
  }
47
+ // workspace_member.role only supports "admin" | "member" — map super-admin → admin
48
+ const wsRole = role === "member" ? "member" : "admin";
37
49
  const member = await workspaceMemberService.createWorkspaceMember({
38
50
  workspace_id: req.params.id,
39
51
  user_id,
40
- role: role ?? "member",
52
+ role: wsRole,
41
53
  });
54
+ // Optionally assign custom app role to the user
55
+ if (app_role_id) {
56
+ try {
57
+ const userService = req.scope.resolve("userModuleService");
58
+ await userService.updateUser(user_id, { app_role_id });
59
+ }
60
+ catch {
61
+ // Non-fatal
62
+ }
63
+ }
42
64
  res.status(201).json({ member });
43
65
  }
44
66
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/members/route.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../src/api/admin/workspaces/[id]/members/route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * In-memory one-time code store for the Google OAuth callback.
3
+ *
4
+ * After a successful OAuth flow, the backend issues a short-lived one-time code
5
+ * (instead of putting the JWT directly in the redirect URL where it would appear
6
+ * in server access logs). The frontend exchanges this code for the real JWT via
7
+ * POST /auth/google/exchange within a 2-minute window.
8
+ *
9
+ * Module-level singleton — shared between the callback and exchange routes
10
+ * because Node.js caches module instances.
11
+ */
12
+ /** Store a one-time code mapped to a JWT. TTL: 2 minutes. */
13
+ export declare function storeExchangeCode(code: string, token: string): void;
14
+ /**
15
+ * Consume a one-time code and return the associated JWT.
16
+ * The code is deleted immediately — it cannot be reused.
17
+ * Returns null if the code is invalid or expired.
18
+ */
19
+ export declare function consumeExchangeCode(code: string): string | null;
20
+ //# sourceMappingURL=_exchange-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_exchange-store.d.ts","sourceRoot":"","sources":["../../../../src/api/auth/google/_exchange-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * In-memory one-time code store for the Google OAuth callback.
3
+ *
4
+ * After a successful OAuth flow, the backend issues a short-lived one-time code
5
+ * (instead of putting the JWT directly in the redirect URL where it would appear
6
+ * in server access logs). The frontend exchanges this code for the real JWT via
7
+ * POST /auth/google/exchange within a 2-minute window.
8
+ *
9
+ * Module-level singleton — shared between the callback and exchange routes
10
+ * because Node.js caches module instances.
11
+ */
12
+ const store = new Map();
13
+ /** Store a one-time code mapped to a JWT. TTL: 2 minutes. */
14
+ export function storeExchangeCode(code, token) {
15
+ store.set(code, { token, expiresAt: Date.now() + 2 * 60 * 1000 });
16
+ }
17
+ /**
18
+ * Consume a one-time code and return the associated JWT.
19
+ * The code is deleted immediately — it cannot be reused.
20
+ * Returns null if the code is invalid or expired.
21
+ */
22
+ export function consumeExchangeCode(code) {
23
+ const entry = store.get(code);
24
+ store.delete(code); // always delete — single use
25
+ if (!entry || entry.expiresAt < Date.now())
26
+ return null;
27
+ return entry.token;
28
+ }
29
+ //# sourceMappingURL=_exchange-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_exchange-store.js","sourceRoot":"","sources":["../../../../src/api/auth/google/_exchange-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAA;AAE9C,6DAA6D;AAC7D,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAa;IAC3D,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,CAAA;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC7B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA,CAAC,6BAA6B;IAChD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,IAAI,CAAA;IACvD,OAAO,KAAK,CAAC,KAAK,CAAA;AACpB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Response } from "express";
2
+ /**
3
+ * GET /auth/google/callback?code=...&state=...
4
+ * Handles the Google OAuth redirect. Absent googleOAuthService → 501.
5
+ */
6
+ export declare const GET: (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/google/callback/route.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC;;;GAGG;AACH,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAoKhD,CAAA"}
@@ -0,0 +1,160 @@
1
+ import jwt from "jsonwebtoken";
2
+ import { randomBytes } from "node:crypto";
3
+ import { storeExchangeCode } from "../_exchange-store.js";
4
+ /**
5
+ * GET /auth/google/callback?code=...&state=...
6
+ * Handles the Google OAuth redirect. Absent googleOAuthService → 501.
7
+ */
8
+ export const GET = async (req, res) => {
9
+ let googleOAuthService;
10
+ try {
11
+ googleOAuthService = req.scope.resolve("googleOAuthService");
12
+ }
13
+ catch {
14
+ res.status(501).json({ error: { message: "Google OAuth is not configured" } });
15
+ return;
16
+ }
17
+ const frontendUrl = googleOAuthService.frontendUrl;
18
+ const errorRedirect = (msg) => {
19
+ // Error messages are not credentials, query param is fine here
20
+ res.redirect(302, `${frontendUrl}/oauth/google/callback?error=${encodeURIComponent(msg)}`);
21
+ };
22
+ const { code, state } = req.query;
23
+ if (!code || !state) {
24
+ errorRedirect("Missing authorization code or state");
25
+ return;
26
+ }
27
+ const config = req.scope.resolve("config");
28
+ const jwtSecret = config?.projectConfig?.jwtSecret;
29
+ // Verify state JWT
30
+ let statePayload;
31
+ try {
32
+ statePayload = jwt.verify(state, jwtSecret, { algorithms: ["HS256"] });
33
+ }
34
+ catch (err) {
35
+ if (err.name === "TokenExpiredError") {
36
+ errorRedirect("OAuth session expired. Please try again.");
37
+ }
38
+ else {
39
+ errorRedirect("Invalid OAuth state. Please try again.");
40
+ }
41
+ return;
42
+ }
43
+ // CSRF check — nonce must match the HttpOnly cookie
44
+ const cookieNonce = req.cookies?.oauth_nonce;
45
+ if (!cookieNonce || cookieNonce !== statePayload.nonce) {
46
+ errorRedirect("CSRF validation failed. Please try again.");
47
+ return;
48
+ }
49
+ // Clear the nonce cookie immediately (single-use)
50
+ res.clearCookie("oauth_nonce", { path: "/auth/google/callback" });
51
+ const flow = statePayload.flow;
52
+ const inviteToken = statePayload.invite_token;
53
+ // ── Link flow: attach Google account to existing user ────────────────────────
54
+ if (flow === "link" && statePayload.userId) {
55
+ let profile;
56
+ try {
57
+ profile = await googleOAuthService.exchangeCode(code);
58
+ }
59
+ catch (err) {
60
+ errorRedirect(err.message ?? "Failed to authenticate with Google");
61
+ return;
62
+ }
63
+ const userService = req.scope.resolve("userModuleService");
64
+ // Check if this Google account is already tied to a different user
65
+ try {
66
+ const [existing] = await userService.listUsers({ google_id: profile.googleId }, { limit: 1 });
67
+ if (existing && existing.id !== statePayload.userId) {
68
+ errorRedirect("This Google account is already linked to another user.");
69
+ return;
70
+ }
71
+ }
72
+ catch {
73
+ // listUsers may throw if no results — that's fine, continue
74
+ }
75
+ await userService.updateUser(statePayload.userId, { google_id: profile.googleId });
76
+ res.redirect(302, `${frontendUrl}/oauth/google/callback?linked=true`);
77
+ return;
78
+ }
79
+ // Pre-flight invite validation
80
+ let inviteRecord = null;
81
+ if (flow === "invite" && inviteToken) {
82
+ try {
83
+ const invitationService = req.scope.resolve("invitationModuleService");
84
+ const [invitations] = await invitationService.listAndCountInvitations({ token: inviteToken }, { limit: 1 });
85
+ const invitation = invitations?.[0];
86
+ if (!invitation) {
87
+ errorRedirect("Invitation not found");
88
+ return;
89
+ }
90
+ if (invitation.status !== "pending") {
91
+ errorRedirect("Invitation has already been used");
92
+ return;
93
+ }
94
+ if (invitation.expires_at && new Date(invitation.expires_at) < new Date()) {
95
+ errorRedirect("Invitation has expired. Ask for a new one.");
96
+ return;
97
+ }
98
+ inviteRecord = {
99
+ id: invitation.id,
100
+ email: invitation.email,
101
+ role: invitation.role,
102
+ workspace_id: invitation.workspace_id,
103
+ app_role_id: invitation.app_role_id ?? null,
104
+ };
105
+ }
106
+ catch {
107
+ errorRedirect("Failed to validate invitation");
108
+ return;
109
+ }
110
+ }
111
+ // Exchange code for Google profile
112
+ let profile;
113
+ try {
114
+ profile = await googleOAuthService.exchangeCode(code);
115
+ }
116
+ catch (err) {
117
+ errorRedirect(err.message ?? "Failed to authenticate with Google");
118
+ return;
119
+ }
120
+ // Verify invite email matches Google email (if invite has a locked email)
121
+ if (inviteRecord?.email && profile.email.toLowerCase() !== inviteRecord.email.toLowerCase()) {
122
+ errorRedirect(`This invitation was sent to ${inviteRecord.email}. Please sign in with that Google account.`);
123
+ return;
124
+ }
125
+ // Perform login / register
126
+ let authResult;
127
+ try {
128
+ const authService = req.scope.resolve("authModuleService");
129
+ authResult = await authService.loginOrRegisterWithGoogle({
130
+ googleId: profile.googleId,
131
+ email: profile.email,
132
+ firstName: profile.firstName,
133
+ lastName: profile.lastName,
134
+ picture: profile.picture,
135
+ inviteRecord,
136
+ });
137
+ }
138
+ catch (err) {
139
+ errorRedirect(err.message ?? "Authentication failed");
140
+ return;
141
+ }
142
+ // Mark invite accepted after successful login/register
143
+ if (flow === "invite" && inviteRecord) {
144
+ try {
145
+ const invitationService = req.scope.resolve("invitationModuleService");
146
+ await invitationService.updateInvitation(inviteRecord.id, { status: "accepted" });
147
+ }
148
+ catch {
149
+ // Non-fatal
150
+ }
151
+ }
152
+ // Issue a one-time code instead of putting the JWT in the redirect URL.
153
+ // The JWT in a redirect URL (even as a hash fragment) appears in the server's
154
+ // access log on the 302 Location header. The frontend exchanges this short-lived
155
+ // code for the real JWT via POST /auth/google/exchange within 2 minutes.
156
+ const exchangeCode = randomBytes(32).toString("hex");
157
+ storeExchangeCode(exchangeCode, authResult.token);
158
+ res.redirect(302, `${frontendUrl}/oauth/google/callback?code=${exchangeCode}`);
159
+ };
160
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/auth/google/callback/route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Response } from "express";
2
+ /**
3
+ * POST /auth/google/exchange
4
+ * Public — exchanges a short-lived one-time code (issued by the OAuth callback)
5
+ * for the real JWT. The code is valid for 2 minutes and cannot be reused.
6
+ *
7
+ * This keeps the JWT out of redirect URLs (and therefore out of server access logs).
8
+ */
9
+ export declare const POST: (req: any, res: Response) => Promise<void>;
10
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/api/auth/google/exchange/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAgBjD,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { consumeExchangeCode } from "../_exchange-store.js";
2
+ /**
3
+ * POST /auth/google/exchange
4
+ * Public — exchanges a short-lived one-time code (issued by the OAuth callback)
5
+ * for the real JWT. The code is valid for 2 minutes and cannot be reused.
6
+ *
7
+ * This keeps the JWT out of redirect URLs (and therefore out of server access logs).
8
+ */
9
+ export const POST = async (req, res) => {
10
+ const { code } = req.body;
11
+ if (!code || typeof code !== "string") {
12
+ res.status(400).json({ error: { message: "code is required" } });
13
+ return;
14
+ }
15
+ const token = consumeExchangeCode(code);
16
+ if (!token) {
17
+ res.status(401).json({ error: { message: "Invalid or expired exchange code" } });
18
+ return;
19
+ }
20
+ res.json({ token });
21
+ };
22
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/auth/google/exchange/route.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAE3D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACpD,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,IAAyB,CAAA;IAE9C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAA;QAChE,OAAM;IACR,CAAC;IAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAA;IAEvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,kCAAkC,EAAE,EAAE,CAAC,CAAA;QAChF,OAAM;IACR,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AACrB,CAAC,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { Response } from "express";
2
+ /**
3
+ * GET /auth/google/link
4
+ * Returns a Google OAuth URL for the link flow (connecting Google to an existing account).
5
+ * Requires a valid Bearer token in the Authorization header (manually verified here
6
+ * because /auth/* middleware does not apply JWT authentication).
7
+ */
8
+ export declare const GET: (req: any, res: Response) => Promise<void>;
9
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/api/auth/google/link/route.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAgDhD,CAAA"}
@@ -0,0 +1,53 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import jwt from "jsonwebtoken";
3
+ /**
4
+ * GET /auth/google/link
5
+ * Returns a Google OAuth URL for the link flow (connecting Google to an existing account).
6
+ * Requires a valid Bearer token in the Authorization header (manually verified here
7
+ * because /auth/* middleware does not apply JWT authentication).
8
+ */
9
+ export const GET = async (req, res) => {
10
+ // Manually verify the Bearer token
11
+ const authHeader = req.headers.authorization;
12
+ const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
13
+ if (!token) {
14
+ res.status(401).json({ error: { message: "Authorization header required" } });
15
+ return;
16
+ }
17
+ const config = req.scope.resolve("config");
18
+ const jwtSecret = config?.projectConfig?.jwtSecret;
19
+ let payload;
20
+ try {
21
+ payload = jwt.verify(token, jwtSecret, { algorithms: ["HS256"] });
22
+ }
23
+ catch {
24
+ res.status(401).json({ error: { message: "Invalid or expired token" } });
25
+ return;
26
+ }
27
+ const userId = (payload.sub ?? payload.id);
28
+ if (!userId) {
29
+ res.status(401).json({ error: { message: "Invalid token: missing user id" } });
30
+ return;
31
+ }
32
+ let googleOAuthService;
33
+ try {
34
+ googleOAuthService = req.scope.resolve("googleOAuthService");
35
+ }
36
+ catch {
37
+ res.status(501).json({ error: { message: "Google OAuth is not configured" } });
38
+ return;
39
+ }
40
+ const nonce = randomBytes(16).toString("hex");
41
+ const statePayload = { nonce, flow: "link", userId };
42
+ const state = jwt.sign(statePayload, jwtSecret, { expiresIn: "10m", algorithm: "HS256" });
43
+ res.cookie("oauth_nonce", nonce, {
44
+ httpOnly: true,
45
+ sameSite: "lax",
46
+ path: "/auth/google/callback",
47
+ maxAge: 600_000,
48
+ secure: process.env.NODE_ENV === "production",
49
+ });
50
+ const url = googleOAuthService.getAuthUrl(state);
51
+ res.json({ url });
52
+ };
53
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/auth/google/link/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,GAAG,MAAM,cAAc,CAAA;AAG9B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACnD,mCAAmC;IACnC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAmC,CAAA;IAClE,MAAM,KAAK,GAAG,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,EAAE,CAAC,CAAA;QAC7E,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAQ,CAAA;IACjD,MAAM,SAAS,GAAG,MAAM,EAAE,aAAa,EAAE,SAAmB,CAAA;IAE5D,IAAI,OAAsC,CAAA;IAC1C,IAAI,CAAC;QACH,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAQ,CAAA;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAA;QACxE,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,EAAE,CAAuB,CAAA;IAChE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAA;QAC9E,OAAM;IACR,CAAC;IAED,IAAI,kBAAuB,CAAA;IAC3B,IAAI,CAAC;QACH,kBAAkB,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAA;QAC9E,OAAM;IACR,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;IAEzF,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,EAAE;QAC/B,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;KAC9C,CAAC,CAAA;IAEF,MAAM,GAAG,GAAW,kBAAkB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IACxD,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;AACnB,CAAC,CAAA"}
@@ -0,0 +1,7 @@
1
+ import type { Response } from "express";
2
+ /**
3
+ * GET /auth/google?flow=login|register|invite&invite_token=<tok>
4
+ * Initiates Google OAuth flow. Absent googleOAuthService → 501.
5
+ */
6
+ export declare const GET: (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/google/route.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC;;;GAGG;AACH,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBA2ChD,CAAA"}
@@ -0,0 +1,45 @@
1
+ import { randomBytes } from "crypto";
2
+ import jwt from "jsonwebtoken";
3
+ /**
4
+ * GET /auth/google?flow=login|register|invite&invite_token=<tok>
5
+ * Initiates Google OAuth flow. Absent googleOAuthService → 501.
6
+ */
7
+ export const GET = async (req, res) => {
8
+ let googleOAuthService;
9
+ try {
10
+ googleOAuthService = req.scope.resolve("googleOAuthService");
11
+ }
12
+ catch {
13
+ res.status(501).json({ error: { message: "Google OAuth is not configured" } });
14
+ return;
15
+ }
16
+ const flow = req.query.flow;
17
+ if (!["login", "register", "invite"].includes(flow)) {
18
+ res.status(400).json({ error: { message: "Invalid flow. Must be login, register, or invite." } });
19
+ return;
20
+ }
21
+ if (flow === "invite" && !req.query.invite_token) {
22
+ res.status(400).json({ error: { message: "invite_token is required for invite flow" } });
23
+ return;
24
+ }
25
+ const config = req.scope.resolve("config");
26
+ const jwtSecret = config?.projectConfig?.jwtSecret;
27
+ // Generate a cryptographically random nonce for CSRF protection
28
+ const nonce = randomBytes(16).toString("hex");
29
+ // Sign a state JWT containing the nonce and flow metadata
30
+ const statePayload = { nonce, flow };
31
+ if (flow === "invite" && req.query.invite_token) {
32
+ statePayload.invite_token = req.query.invite_token;
33
+ }
34
+ const state = jwt.sign(statePayload, jwtSecret, { expiresIn: "10m", algorithm: "HS256" });
35
+ // HttpOnly SameSite=Lax nonce cookie scoped to the callback path
36
+ res.cookie("oauth_nonce", nonce, {
37
+ httpOnly: true,
38
+ sameSite: "lax",
39
+ path: "/auth/google/callback",
40
+ maxAge: 600_000,
41
+ secure: process.env.NODE_ENV === "production",
42
+ });
43
+ res.redirect(302, googleOAuthService.getAuthUrl(state));
44
+ };
45
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/auth/google/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpC,OAAO,GAAG,MAAM,cAAc,CAAA;AAG9B;;;GAGG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACnD,IAAI,kBAAuB,CAAA;IAC3B,IAAI,CAAC;QACH,kBAAkB,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAA;QAC9E,OAAM;IACR,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAc,CAAA;IACrC,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,mDAAmD,EAAE,EAAE,CAAC,CAAA;QACjG,OAAM;IACR,CAAC;IAED,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QACjD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,0CAA0C,EAAE,EAAE,CAAC,CAAA;QACxF,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAQ,CAAA;IACjD,MAAM,SAAS,GAAG,MAAM,EAAE,aAAa,EAAE,SAAmB,CAAA;IAE5D,gEAAgE;IAChE,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAE7C,0DAA0D;IAC1D,MAAM,YAAY,GAA2B,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IAC5D,IAAI,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QAChD,YAAY,CAAC,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,YAAsB,CAAA;IAC9D,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;IAEzF,iEAAiE;IACjE,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,EAAE;QAC/B,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;KAC9C,CAAC,CAAA;IAEF,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;AACzD,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/api/auth/invite/[token]/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,qGAAqG;AACrG,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBA+BhD,CAAA;AASD;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAwDjD,CAAA"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/api/auth/invite/[token]/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,qGAAqG;AACrG,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAoChD,CAAA;AASD;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAqEjD,CAAA"}
@@ -13,6 +13,10 @@ export const GET = async (req, res) => {
13
13
  res.status(410).json({ error: { message: `Invitation has been ${invitation.status}` } });
14
14
  return;
15
15
  }
16
+ if (invitation.expires_at && new Date(invitation.expires_at) < new Date()) {
17
+ res.status(410).json({ error: { message: "Invitation has expired. Ask for a new one." } });
18
+ return;
19
+ }
16
20
  const workspace = await workspaceService.retrieveWorkspace(invitation.workspace_id);
17
21
  res.json({
18
22
  invitation: {
@@ -48,6 +52,10 @@ export const POST = async (req, res) => {
48
52
  res.status(410).json({ error: { message: `Invitation has already been ${invitation.status}` } });
49
53
  return;
50
54
  }
55
+ if (invitation.expires_at && new Date(invitation.expires_at) < new Date()) {
56
+ res.status(410).json({ error: { message: "Invitation has expired. Ask for a new one." } });
57
+ return;
58
+ }
51
59
  const parsed = acceptSchema.safeParse(req.body);
52
60
  if (!parsed.success) {
53
61
  res.status(400).json({ error: { message: "Validation error", details: parsed.error.flatten().fieldErrors } });
@@ -59,14 +67,23 @@ export const POST = async (req, res) => {
59
67
  });
60
68
  return;
61
69
  }
62
- const authResult = await authService.register({
63
- email: parsed.data.email,
64
- password: parsed.data.password,
65
- first_name: parsed.data.first_name,
66
- last_name: parsed.data.last_name,
67
- role: invitation.role,
68
- });
69
- await workspaceMemberService.ensureMember(invitation.workspace_id, authResult.user.id, invitation.role);
70
+ let authResult;
71
+ try {
72
+ authResult = await authService.register({
73
+ email: parsed.data.email,
74
+ password: parsed.data.password,
75
+ first_name: parsed.data.first_name,
76
+ last_name: parsed.data.last_name,
77
+ role: invitation.role,
78
+ });
79
+ }
80
+ catch (err) {
81
+ res.status(err.status ?? 500).json({ error: { message: err.message ?? "Registration failed" } });
82
+ return;
83
+ }
84
+ // workspace_member.role only supports "admin" | "member" — map super-admin → admin
85
+ const wsRole = invitation.role === "member" ? "member" : "admin";
86
+ await workspaceMemberService.ensureMember(invitation.workspace_id, authResult.user.id, wsRole);
70
87
  if (invitation.app_role_id) {
71
88
  try {
72
89
  const userService = req.scope.resolve("userModuleService");
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/auth/invite/[token]/route.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../src/api/auth/invite/[token]/route.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/auth/register/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AASvC,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBASjD,CAAA"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/auth/register/route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AASvC,eAAO,MAAM,IAAI,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAoBjD,CAAA"}
@@ -6,6 +6,16 @@ const registerSchema = z.object({
6
6
  last_name: z.string().optional(),
7
7
  });
8
8
  export const POST = async (req, res) => {
9
+ // Registration is only open for the very first user (super-admin setup).
10
+ // All subsequent accounts must be created via an invitation link.
11
+ const userService = req.scope.resolve("userModuleService");
12
+ const [, userCount] = await userService.listAndCountUsers({}, { limit: 1 });
13
+ if (userCount > 0) {
14
+ res.status(403).json({
15
+ error: { message: "Registration is closed. Ask an admin to send you an invitation link." },
16
+ });
17
+ return;
18
+ }
9
19
  const result = registerSchema.safeParse(req.body);
10
20
  if (!result.success) {
11
21
  res.status(400).json({ error: { message: "Validation error", details: result.error.flatten().fieldErrors } });
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/auth/register/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IACzB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wCAAwC,CAAC;IACrE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACpD,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACjD,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;IACD,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACxD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAChC,CAAC,CAAA"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/auth/register/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IACzB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wCAAwC,CAAC;IACrE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACpD,yEAAyE;IACzE,kEAAkE;IAClE,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,CAAC,EAAE,SAAS,CAAC,GAAG,MAAM,WAAW,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3E,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,EAAE,OAAO,EAAE,sEAAsE,EAAE;SAC3F,CAAC,CAAA;QACF,OAAM;IACR,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACjD,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;IACD,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;IACjE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACxD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAChC,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/auth/setup/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEhD;;;;GAIG;AACH,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAShD,CAAA"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/auth/setup/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEhD;;;;GAIG;AACH,eAAO,MAAM,GAAG,GAAU,KAAK,GAAG,EAAE,KAAK,QAAQ,kBAmBhD,CAAA"}
@@ -4,14 +4,23 @@
4
4
  * Returns whether the app needs first-time setup (no users registered yet).
5
5
  */
6
6
  export const GET = async (req, res) => {
7
+ let needsSetup = false;
7
8
  try {
8
9
  const userService = req.scope.resolve("userModuleService");
9
10
  const count = await userService.countUsers();
10
- res.json({ needsSetup: count === 0 });
11
+ needsSetup = count === 0;
11
12
  }
12
13
  catch {
13
14
  // userModuleService not configured — assume setup not needed
14
- res.json({ needsSetup: false });
15
15
  }
16
+ let googleOAuthEnabled = false;
17
+ try {
18
+ req.scope.resolve("googleOAuthService");
19
+ googleOAuthEnabled = true;
20
+ }
21
+ catch {
22
+ // googleOAuthService not registered — feature disabled
23
+ }
24
+ res.json({ needsSetup, googleOAuthEnabled });
16
25
  };
17
26
  //# sourceMappingURL=route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/auth/setup/route.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACnD,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;QACjE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,CAAA;QAC5C,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;QAC7D,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAA;IACjC,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/api/auth/setup/route.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAQ,EAAE,GAAa,EAAE,EAAE;IACnD,IAAI,UAAU,GAAG,KAAK,CAAA;IACtB,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAQ,CAAA;QACjE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,CAAA;QAC5C,UAAU,GAAG,KAAK,KAAK,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;IAED,IAAI,kBAAkB,GAAG,KAAK,CAAA;IAC9B,IAAI,CAAC;QACH,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;QACvC,kBAAkB,GAAG,IAAI,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;IACzD,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC,CAAA;AAC9C,CAAC,CAAA"}
@@ -1,7 +1,7 @@
1
1
  export interface CreateInvitationInput {
2
2
  workspace_id: string;
3
3
  email?: string | null;
4
- role: "admin" | "member";
4
+ role: "super-admin" | "admin" | "member";
5
5
  app_role_id?: string | null;
6
6
  created_by: string;
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create-invitation.d.ts","sourceRoot":"","sources":["../../src/workflows/create-invitation.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;CACnB;AAqBD,eAAO,MAAM,wBAAwB,gJAkBpC,CAAA"}
1
+ {"version":3,"file":"create-invitation.d.ts","sourceRoot":"","sources":["../../src/workflows/create-invitation.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,EAAE,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;CACnB;AAqBD,eAAO,MAAM,wBAAwB,gJAkBpC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meridianjs/meridian",
3
- "version": "0.1.27",
3
+ "version": "0.1.30",
4
4
  "description": "Default API routes, workflows, links, and subscribers for Meridian applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",