@actuate-media/cms-core 0.38.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/__tests__/api/ai-status.test.d.ts +2 -0
  2. package/dist/__tests__/api/ai-status.test.d.ts.map +1 -0
  3. package/dist/__tests__/api/ai-status.test.js +110 -0
  4. package/dist/__tests__/api/ai-status.test.js.map +1 -0
  5. package/dist/__tests__/api/users-detail.test.d.ts +2 -0
  6. package/dist/__tests__/api/users-detail.test.d.ts.map +1 -0
  7. package/dist/__tests__/api/users-detail.test.js +349 -0
  8. package/dist/__tests__/api/users-detail.test.js.map +1 -0
  9. package/dist/__tests__/api/users-invite.test.d.ts +2 -0
  10. package/dist/__tests__/api/users-invite.test.d.ts.map +1 -0
  11. package/dist/__tests__/api/users-invite.test.js +221 -0
  12. package/dist/__tests__/api/users-invite.test.js.map +1 -0
  13. package/dist/__tests__/auth/invites.test.d.ts +2 -0
  14. package/dist/__tests__/auth/invites.test.d.ts.map +1 -0
  15. package/dist/__tests__/auth/invites.test.js +181 -0
  16. package/dist/__tests__/auth/invites.test.js.map +1 -0
  17. package/dist/__tests__/auth/reset.test.js +52 -1
  18. package/dist/__tests__/auth/reset.test.js.map +1 -1
  19. package/dist/__tests__/auth/roles.test.d.ts +2 -0
  20. package/dist/__tests__/auth/roles.test.d.ts.map +1 -0
  21. package/dist/__tests__/auth/roles.test.js +60 -0
  22. package/dist/__tests__/auth/roles.test.js.map +1 -0
  23. package/dist/__tests__/auth/team.test.d.ts +2 -0
  24. package/dist/__tests__/auth/team.test.d.ts.map +1 -0
  25. package/dist/__tests__/auth/team.test.js +139 -0
  26. package/dist/__tests__/auth/team.test.js.map +1 -0
  27. package/dist/__tests__/auth/user-admin.test.d.ts +2 -0
  28. package/dist/__tests__/auth/user-admin.test.d.ts.map +1 -0
  29. package/dist/__tests__/auth/user-admin.test.js +162 -0
  30. package/dist/__tests__/auth/user-admin.test.js.map +1 -0
  31. package/dist/__tests__/email/sender.test.d.ts +2 -0
  32. package/dist/__tests__/email/sender.test.d.ts.map +1 -0
  33. package/dist/__tests__/email/sender.test.js +33 -0
  34. package/dist/__tests__/email/sender.test.js.map +1 -0
  35. package/dist/__tests__/middleware.test.js +30 -0
  36. package/dist/__tests__/middleware.test.js.map +1 -1
  37. package/dist/__tests__/setup/index.test.js +37 -2
  38. package/dist/__tests__/setup/index.test.js.map +1 -1
  39. package/dist/api/handlers.d.ts.map +1 -1
  40. package/dist/api/handlers.js +1116 -194
  41. package/dist/api/handlers.js.map +1 -1
  42. package/dist/auth/index.d.ts +4 -0
  43. package/dist/auth/index.d.ts.map +1 -1
  44. package/dist/auth/index.js +2 -0
  45. package/dist/auth/index.js.map +1 -1
  46. package/dist/auth/invite-email.d.ts +12 -0
  47. package/dist/auth/invite-email.d.ts.map +1 -0
  48. package/dist/auth/invite-email.js +57 -0
  49. package/dist/auth/invite-email.js.map +1 -0
  50. package/dist/auth/invites.d.ts +71 -0
  51. package/dist/auth/invites.d.ts.map +1 -0
  52. package/dist/auth/invites.js +279 -0
  53. package/dist/auth/invites.js.map +1 -0
  54. package/dist/auth/reset.d.ts +6 -0
  55. package/dist/auth/reset.d.ts.map +1 -1
  56. package/dist/auth/reset.js +38 -7
  57. package/dist/auth/reset.js.map +1 -1
  58. package/dist/auth/roles.d.ts +51 -0
  59. package/dist/auth/roles.d.ts.map +1 -0
  60. package/dist/auth/roles.js +159 -0
  61. package/dist/auth/roles.js.map +1 -0
  62. package/dist/auth/team.d.ts +86 -0
  63. package/dist/auth/team.d.ts.map +1 -0
  64. package/dist/auth/team.js +270 -0
  65. package/dist/auth/team.js.map +1 -0
  66. package/dist/auth/user-admin.d.ts +91 -0
  67. package/dist/auth/user-admin.d.ts.map +1 -0
  68. package/dist/auth/user-admin.js +250 -0
  69. package/dist/auth/user-admin.js.map +1 -0
  70. package/dist/email/sender.d.ts +35 -0
  71. package/dist/email/sender.d.ts.map +1 -0
  72. package/dist/email/sender.js +25 -0
  73. package/dist/email/sender.js.map +1 -0
  74. package/dist/forms/notify.d.ts.map +1 -1
  75. package/dist/forms/notify.js +3 -2
  76. package/dist/forms/notify.js.map +1 -1
  77. package/dist/index.d.ts +3 -0
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +4 -0
  80. package/dist/index.js.map +1 -1
  81. package/dist/middleware.d.ts.map +1 -1
  82. package/dist/middleware.js +60 -1
  83. package/dist/middleware.js.map +1 -1
  84. package/dist/setup/index.d.ts +14 -0
  85. package/dist/setup/index.d.ts.map +1 -1
  86. package/dist/setup/index.js +29 -3
  87. package/dist/setup/index.js.map +1 -1
  88. package/package.json +11 -1
  89. package/prisma/migrations/0011_user_roles_owner_viewer/migration.sql +6 -0
  90. package/prisma/migrations/0012_invites_and_user_lifecycle/migration.sql +40 -0
  91. package/prisma/schema.prisma +43 -0
@@ -34,8 +34,13 @@ export function createCMSMiddleware(cmsConfig) {
34
34
  return new Response('Forbidden', { status: 403 });
35
35
  }
36
36
  }
37
+ // CMS_SECRET is mandatory: it signs session JWTs, so the admin (including
38
+ // the setup wizard, which auto-logs-in the new admin) cannot function
39
+ // without it. Rather than a bare 500, return an actionable page that tells
40
+ // the operator exactly what to set. The setup *API* route is already
41
+ // bypassed above, so this only guards the admin UI.
37
42
  if (isAdminRoute && !secret) {
38
- return new Response('Server misconfiguration: CMS_SECRET is not set', { status: 500 });
43
+ return configMissingResponse();
39
44
  }
40
45
  // Session verification for admin routes (not API routes — those handle their own auth)
41
46
  if (isAdminRoute && secret) {
@@ -60,6 +65,60 @@ export function createCMSMiddleware(cmsConfig) {
60
65
  return null;
61
66
  };
62
67
  }
68
+ /**
69
+ * Actionable error page shown when the admin UI is requested but `CMS_SECRET`
70
+ * is not configured. Self-contained HTML (no external assets, no theme access)
71
+ * because it renders before the admin shell can load. Deliberately leaks no
72
+ * secret values — only the variable name and remediation steps.
73
+ */
74
+ function configMissingResponse() {
75
+ const html = `<!doctype html>
76
+ <html lang="en">
77
+ <head>
78
+ <meta charset="utf-8" />
79
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
80
+ <title>Actuate CMS — Configuration required</title>
81
+ <style>
82
+ :root { color-scheme: light dark; }
83
+ body { margin: 0; min-height: 100vh; display: grid; place-items: center;
84
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
85
+ background: #0b0d12; color: #e6e8eb; line-height: 1.5; padding: 24px; }
86
+ .card { max-width: 560px; width: 100%; background: #151922; border: 1px solid #262c38;
87
+ border-radius: 12px; padding: 28px 32px; }
88
+ h1 { font-size: 1.25rem; margin: 0 0 8px; font-weight: 500; }
89
+ p { margin: 0 0 12px; color: #aab2bd; }
90
+ code { background: #0b0d12; border: 1px solid #262c38; border-radius: 6px;
91
+ padding: 2px 6px; font-size: 0.9em; }
92
+ pre { background: #0b0d12; border: 1px solid #262c38; border-radius: 8px;
93
+ padding: 12px 14px; overflow-x: auto; font-size: 0.85rem; color: #e6e8eb; }
94
+ ol { margin: 0 0 4px; padding-left: 20px; color: #aab2bd; }
95
+ li { margin-bottom: 6px; }
96
+ </style>
97
+ </head>
98
+ <body>
99
+ <main class="card">
100
+ <h1>Actuate CMS isn't configured yet</h1>
101
+ <p>The <code>CMS_SECRET</code> environment variable is missing or empty. It's
102
+ required to sign admin sessions, so the admin panel can't start without it.</p>
103
+ <ol>
104
+ <li>Generate a secret (at least 32 characters):
105
+ <pre>node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"</pre>
106
+ </li>
107
+ <li>Set <code>CMS_SECRET</code> to that value in your environment (or your
108
+ host's environment variables), then restart / redeploy.</li>
109
+ </ol>
110
+ <p>This page is only visible to operators — visitors never see it.</p>
111
+ </main>
112
+ </body>
113
+ </html>`;
114
+ return new Response(html, {
115
+ status: 503,
116
+ headers: {
117
+ 'Content-Type': 'text/html; charset=utf-8',
118
+ 'Cache-Control': 'no-store',
119
+ },
120
+ });
121
+ }
63
122
  /**
64
123
  * Match a pathname against a route prefix on a path-segment boundary, so the
65
124
  * admin guard for `/admin` does not also capture sibling public routes like
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAYnE,MAAM,UAAU,mBAAmB,CAAC,SAAkC;IACpE,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,IAAI,QAAQ,CAAA;IACnD,MAAM,WAAW,GAAG,SAAS,CAAC,KAAK,EAAE,WAAW,CAAA;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAA;IAE3C,OAAO,KAAK,UAAU,aAAa,CAAC,OAAgB;QAClD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEhC,MAAM,YAAY,GAAG,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;QAC/D,MAAM,aAAa,GAAG,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;QAEjE,IAAI,CAAC,YAAY,IAAI,CAAC,aAAa,EAAE,CAAC;YACpC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,0EAA0E;QAC1E,uEAAuE;QACvE,wEAAwE;QACxE,iCAAiC;QACjC,MAAM,YAAY,GAChB,GAAG,CAAC,QAAQ,KAAK,GAAG,SAAS,QAAQ;YACrC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,SAAS,CAAC;YAC9C,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAA;QAC3C,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,uEAAuE;QACvE,yEAAyE;QACzE,iEAAiE;QACjE,4CAA4C;QAC5C,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC,EAAE,CAAC;YAC7E,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;YACrC,kEAAkE;YAClE,mCAAmC;YACnC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC;gBACnE,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;YACnD,CAAC;QACH,CAAC;QAED,IAAI,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,OAAO,IAAI,QAAQ,CAAC,gDAAgD,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QACxF,CAAC;QAED,uFAAuF;QACvF,IAAI,YAAY,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;YAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAA;YAEvC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,CAAA;YACb,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,aAAa,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;oBACxB,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE;wBACP,QAAQ,EAAE,SAAS;wBACnB,YAAY,EAAE,+CAA+C;qBAC9D;iBACF,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,QAAgB,EAAE,MAAc;IACzD,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IACtE,OAAO,QAAQ,KAAK,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,GAAG,CAAC,CAAA;AACzE,CAAC;AAED,SAAS,WAAW,CAAC,YAAoB;IACvC,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACtC,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,12 @@
8
8
  export interface SetupStatus {
9
9
  setupRequired: boolean;
10
10
  userCount: number;
11
+ /**
12
+ * True when the database is reachable but the Actuate CMS tables don't exist
13
+ * yet (migrations have not been applied). Distinct from `setupRequired`: the
14
+ * fix is `prisma migrate deploy`, not the setup wizard.
15
+ */
16
+ migrationsRequired?: boolean;
11
17
  }
12
18
  export interface CreateAdminInput {
13
19
  email: string;
@@ -30,6 +36,14 @@ export interface SetupResult {
30
36
  * see the real cause (e.g. `ECONNREFUSED`, Prisma `P1001`).
31
37
  */
32
38
  export declare function isDatabaseUnavailableError(err: unknown): boolean;
39
+ /**
40
+ * Detect errors that mean the database is reachable but the Actuate CMS tables
41
+ * have not been created yet (migrations not applied). This is a distinct,
42
+ * recoverable state from "empty database" — surfacing it lets the UI tell the
43
+ * operator to run migrations instead of dropping them on the login screen or a
44
+ * wizard that will fail on the first write.
45
+ */
46
+ export declare function isSchemaMissingError(err: unknown): boolean;
33
47
  /**
34
48
  * Check if the CMS needs first-run setup (no admin users exist).
35
49
  * Uses a count query to avoid loading the full user model.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,OAAO,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CA4BhE;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAQtE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuC/F;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgBxE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,OAAO,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CA4BhE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAa1D;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAYtE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAwC/F;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgBxE"}
@@ -46,6 +46,27 @@ export function isDatabaseUnavailableError(err) {
46
46
  }
47
47
  return false;
48
48
  }
49
+ /**
50
+ * Detect errors that mean the database is reachable but the Actuate CMS tables
51
+ * have not been created yet (migrations not applied). This is a distinct,
52
+ * recoverable state from "empty database" — surfacing it lets the UI tell the
53
+ * operator to run migrations instead of dropping them on the login screen or a
54
+ * wizard that will fail on the first write.
55
+ */
56
+ export function isSchemaMissingError(err) {
57
+ if (!err || typeof err !== 'object')
58
+ return false;
59
+ const code = err.code;
60
+ // Prisma P2021: table does not exist. P2022: column does not exist.
61
+ if (code === 'P2021' || code === 'P2022')
62
+ return true;
63
+ const message = err.message;
64
+ if (typeof message === 'string') {
65
+ return (message.includes('does not exist in the current database') ||
66
+ /relation "[^"]+" does not exist/i.test(message));
67
+ }
68
+ return false;
69
+ }
49
70
  /**
50
71
  * Check if the CMS needs first-run setup (no admin users exist).
51
72
  * Uses a count query to avoid loading the full user model.
@@ -62,6 +83,10 @@ export async function checkSetupRequired(db) {
62
83
  catch (err) {
63
84
  if (isDatabaseUnavailableError(err))
64
85
  throw err;
86
+ // Reachable DB but no tables yet → operator must run migrations.
87
+ if (isSchemaMissingError(err)) {
88
+ return { setupRequired: false, userCount: 0, migrationsRequired: true };
89
+ }
65
90
  return { setupRequired: true, userCount: 0 };
66
91
  }
67
92
  }
@@ -98,9 +123,10 @@ export async function createInitialAdmin(db, input) {
98
123
  return { success: true, userId: user.id };
99
124
  }
100
125
  catch (err) {
101
- // A DB outage is an infrastructure failure, not a bad request —
102
- // rethrow so the caller can return a 5xx instead of a confusing 400.
103
- if (isDatabaseUnavailableError(err))
126
+ // A DB outage or an unmigrated schema is an infrastructure failure, not a
127
+ // bad request — rethrow so the caller can return a 5xx with an actionable
128
+ // message instead of a confusing 400.
129
+ if (isDatabaseUnavailableError(err) || isSchemaMissingError(err))
104
130
  throw err;
105
131
  const message = err instanceof Error ? err.message : 'Setup failed';
106
132
  return { success: false, error: message };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actuate-media/cms-core",
3
- "version": "0.38.0",
3
+ "version": "0.40.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/actuate-media/actuatecms.git",
@@ -64,6 +64,16 @@
64
64
  "import": "./dist/security/center/index.js",
65
65
  "default": "./dist/security/center/index.js"
66
66
  },
67
+ "./email-sender": {
68
+ "types": "./dist/email/sender.d.ts",
69
+ "import": "./dist/email/sender.js",
70
+ "default": "./dist/email/sender.js"
71
+ },
72
+ "./roles": {
73
+ "types": "./dist/auth/roles.d.ts",
74
+ "import": "./dist/auth/roles.js",
75
+ "default": "./dist/auth/roles.js"
76
+ },
67
77
  "./page-builder": {
68
78
  "types": "./dist/page-builder/index.d.ts",
69
79
  "import": "./dist/page-builder/index.js",
@@ -0,0 +1,6 @@
1
+ -- AlterEnum
2
+ -- Add OWNER and VIEWER to the UserRole enum. These are appended (Postgres
3
+ -- ALTER TYPE ... ADD VALUE appends), and must be committed in their own
4
+ -- migration before any later statement references the new values.
5
+ ALTER TYPE "UserRole" ADD VALUE IF NOT EXISTS 'OWNER';
6
+ ALTER TYPE "UserRole" ADD VALUE IF NOT EXISTS 'VIEWER';
@@ -0,0 +1,40 @@
1
+ -- CreateEnum
2
+ CREATE TYPE "InviteStatus" AS ENUM ('PENDING', 'ACCEPTED', 'EXPIRED', 'CANCELLED');
3
+
4
+ -- AlterTable: access-control lifecycle columns (all additive, nullable/defaulted)
5
+ ALTER TABLE "actuate_users" ADD COLUMN "suspendedAt" TIMESTAMP(3);
6
+ ALTER TABLE "actuate_users" ADD COLUMN "lockedAt" TIMESTAMP(3);
7
+ ALTER TABLE "actuate_users" ADD COLUMN "forcePasswordReset" BOOLEAN NOT NULL DEFAULT false;
8
+ ALTER TABLE "actuate_users" ADD COLUMN "mfaRequired" BOOLEAN NOT NULL DEFAULT false;
9
+ ALTER TABLE "actuate_users" ADD COLUMN "failedLoginCount" INTEGER NOT NULL DEFAULT 0;
10
+ ALTER TABLE "actuate_users" ADD COLUMN "lastActiveAt" TIMESTAMP(3);
11
+ ALTER TABLE "actuate_users" ADD COLUMN "invitedById" TEXT;
12
+ ALTER TABLE "actuate_users" ADD COLUMN "acceptedInviteAt" TIMESTAMP(3);
13
+
14
+ -- CreateTable
15
+ CREATE TABLE "actuate_invites" (
16
+ "id" TEXT NOT NULL,
17
+ "email" TEXT NOT NULL,
18
+ "role" "UserRole" NOT NULL,
19
+ "status" "InviteStatus" NOT NULL DEFAULT 'PENDING',
20
+ "tokenHash" TEXT NOT NULL,
21
+ "message" TEXT,
22
+ "invitedByUserId" TEXT,
23
+ "acceptedUserId" TEXT,
24
+ "expiresAt" TIMESTAMP(3) NOT NULL,
25
+ "acceptedAt" TIMESTAMP(3),
26
+ "cancelledAt" TIMESTAMP(3),
27
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
28
+ "updatedAt" TIMESTAMP(3) NOT NULL,
29
+
30
+ CONSTRAINT "actuate_invites_pkey" PRIMARY KEY ("id")
31
+ );
32
+
33
+ -- CreateIndex
34
+ CREATE INDEX "actuate_invites_email_idx" ON "actuate_invites"("email");
35
+
36
+ -- CreateIndex
37
+ CREATE INDEX "actuate_invites_tokenHash_idx" ON "actuate_invites"("tokenHash");
38
+
39
+ -- CreateIndex
40
+ CREATE INDEX "actuate_invites_status_idx" ON "actuate_invites"("status");
@@ -37,6 +37,15 @@ enum UserRole {
37
37
  EDITOR
38
38
  AUTHOR
39
39
  CLIENT
40
+ OWNER
41
+ VIEWER
42
+ }
43
+
44
+ enum InviteStatus {
45
+ PENDING
46
+ ACCEPTED
47
+ EXPIRED
48
+ CANCELLED
40
49
  }
41
50
 
42
51
  enum ChangeType {
@@ -163,6 +172,17 @@ model User {
163
172
  createdAt DateTime @default(now())
164
173
  updatedAt DateTime @updatedAt
165
174
 
175
+ // Access-control lifecycle (Users management subsystem). All additive and
176
+ // nullable/defaulted so existing rows migrate cleanly.
177
+ suspendedAt DateTime?
178
+ lockedAt DateTime?
179
+ forcePasswordReset Boolean @default(false)
180
+ mfaRequired Boolean @default(false)
181
+ failedLoginCount Int @default(0)
182
+ lastActiveAt DateTime?
183
+ invitedById String?
184
+ acceptedInviteAt DateTime?
185
+
166
186
  sessions Session[]
167
187
  oauthAccounts OAuthAccount[]
168
188
  apiKeys ApiKey[]
@@ -805,6 +825,29 @@ model PasswordResetToken {
805
825
  @@map("actuate_password_reset_tokens")
806
826
  }
807
827
 
828
+ model Invite {
829
+ id String @id @default(cuid())
830
+ email String
831
+ role UserRole
832
+ status InviteStatus @default(PENDING)
833
+ tokenHash String
834
+ message String?
835
+ // Stored as a bare id (not an FK relation) to mirror AuditLog: an inviter
836
+ // being removed must never cascade-delete the invite history.
837
+ invitedByUserId String?
838
+ acceptedUserId String?
839
+ expiresAt DateTime
840
+ acceptedAt DateTime?
841
+ cancelledAt DateTime?
842
+ createdAt DateTime @default(now())
843
+ updatedAt DateTime @updatedAt
844
+
845
+ @@index([email])
846
+ @@index([tokenHash])
847
+ @@index([status])
848
+ @@map("actuate_invites")
849
+ }
850
+
808
851
  model ScriptTag {
809
852
  id String @id @default(cuid())
810
853
  name String