nextly 0.0.1 → 0.0.2-alpha.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 (268) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +122 -0
  3. package/dist/_dts-chunks/collections-handler.d-DjgO74Wt.d.ts +20540 -0
  4. package/dist/_dts-chunks/config.d-DNwsDnjs.d.ts +2589 -0
  5. package/dist/_dts-chunks/define-component.d-BUgTHmt3.d.ts +1149 -0
  6. package/dist/_dts-chunks/image-processor.d-OO1PmMrv.d.ts +335 -0
  7. package/dist/_dts-chunks/index.d-axCAzZ7m.d.ts +17842 -0
  8. package/dist/_dts-chunks/media.d-DjDOZo4B.d.ts +117 -0
  9. package/dist/_dts-chunks/on-error.d-CHIKWNxd.d.ts +38 -0
  10. package/dist/_dts-chunks/storage.d-BUhQ2we_.d.ts +404 -0
  11. package/dist/actions/index.d.ts +239 -0
  12. package/dist/actions/index.mjs +281 -0
  13. package/dist/api/auth-state.d.ts +5 -0
  14. package/dist/api/auth-state.mjs +131 -0
  15. package/dist/api/collections-schema-detail.d.ts +56 -0
  16. package/dist/api/collections-schema-detail.mjs +244 -0
  17. package/dist/api/collections-schema-export.d.ts +56 -0
  18. package/dist/api/collections-schema-export.mjs +129 -0
  19. package/dist/api/collections-schema.d.ts +59 -0
  20. package/dist/api/collections-schema.mjs +207 -0
  21. package/dist/api/components-detail.d.ts +50 -0
  22. package/dist/api/components-detail.mjs +132 -0
  23. package/dist/api/components.d.ts +69 -0
  24. package/dist/api/components.mjs +144 -0
  25. package/dist/api/email-providers-default.d.ts +40 -0
  26. package/dist/api/email-providers-default.mjs +75 -0
  27. package/dist/api/email-providers-detail.d.ts +81 -0
  28. package/dist/api/email-providers-detail.mjs +109 -0
  29. package/dist/api/email-providers-test.d.ts +43 -0
  30. package/dist/api/email-providers-test.mjs +114 -0
  31. package/dist/api/email-providers.d.ts +69 -0
  32. package/dist/api/email-providers.mjs +110 -0
  33. package/dist/api/email-send-template.d.ts +41 -0
  34. package/dist/api/email-send-template.mjs +58 -0
  35. package/dist/api/email-send.d.ts +42 -0
  36. package/dist/api/email-send.mjs +58 -0
  37. package/dist/api/email-templates-detail.d.ts +74 -0
  38. package/dist/api/email-templates-detail.mjs +112 -0
  39. package/dist/api/email-templates-layout.d.ts +55 -0
  40. package/dist/api/email-templates-layout.mjs +92 -0
  41. package/dist/api/email-templates-preview.d.ts +48 -0
  42. package/dist/api/email-templates-preview.mjs +93 -0
  43. package/dist/api/email-templates.d.ts +61 -0
  44. package/dist/api/email-templates.mjs +118 -0
  45. package/dist/api/health.d.ts +68 -0
  46. package/dist/api/health.mjs +67 -0
  47. package/dist/api/index.d.ts +54 -0
  48. package/dist/api/index.mjs +16 -0
  49. package/dist/api/media-bulk.d.ts +74 -0
  50. package/dist/api/media-bulk.mjs +196 -0
  51. package/dist/api/media-folders.d.ts +112 -0
  52. package/dist/api/media-folders.mjs +187 -0
  53. package/dist/api/media-handlers.d.ts +102 -0
  54. package/dist/api/media-handlers.mjs +437 -0
  55. package/dist/api/media.d.ts +117 -0
  56. package/dist/api/media.mjs +242 -0
  57. package/dist/api/singles-detail.d.ts +87 -0
  58. package/dist/api/singles-detail.mjs +170 -0
  59. package/dist/api/singles-schema-detail.d.ts +54 -0
  60. package/dist/api/singles-schema-detail.mjs +182 -0
  61. package/dist/api/singles.d.ts +34 -0
  62. package/dist/api/singles.mjs +94 -0
  63. package/dist/api/storage-upload-url.d.ts +48 -0
  64. package/dist/api/storage-upload-url.mjs +202 -0
  65. package/dist/api/uploads.d.ts +109 -0
  66. package/dist/api/uploads.mjs +359 -0
  67. package/dist/auth/index.d.ts +425 -0
  68. package/dist/auth/index.mjs +199 -0
  69. package/dist/boot-apply-PQSYLDIN.mjs +7 -0
  70. package/dist/chunk-2OALJTK6.mjs +489 -0
  71. package/dist/chunk-2Q2SX2CS.mjs +365 -0
  72. package/dist/chunk-2TFX4ND3.mjs +13 -0
  73. package/dist/chunk-2TWPDSYD.mjs +87 -0
  74. package/dist/chunk-2W3DVD7S.mjs +647 -0
  75. package/dist/chunk-2ZFKXPQM.mjs +88 -0
  76. package/dist/chunk-3FA7FKAV.mjs +832 -0
  77. package/dist/chunk-3NZ2KMBL.mjs +58 -0
  78. package/dist/chunk-4MJLT6PZ.mjs +0 -0
  79. package/dist/chunk-56WO4WX7.mjs +0 -0
  80. package/dist/chunk-5APFUGAD.mjs +89 -0
  81. package/dist/chunk-5HMZ644B.mjs +108 -0
  82. package/dist/chunk-67GXH6PR.mjs +32 -0
  83. package/dist/chunk-6JNEPWRW.mjs +14368 -0
  84. package/dist/chunk-6NFHQIJD.mjs +45 -0
  85. package/dist/chunk-7P6ASYW6.mjs +9 -0
  86. package/dist/chunk-A3WPLSDT.mjs +1364 -0
  87. package/dist/chunk-AGJ6F2T3.mjs +144 -0
  88. package/dist/chunk-AK6Z23OX.mjs +1464 -0
  89. package/dist/chunk-APKKRD2G.mjs +102 -0
  90. package/dist/chunk-B2GV2BWH.mjs +73 -0
  91. package/dist/chunk-D5HQBNUB.mjs +74 -0
  92. package/dist/chunk-DNNG377Z.mjs +204 -0
  93. package/dist/chunk-DP3G27G5.mjs +135 -0
  94. package/dist/chunk-DV6WVX2Q.mjs +0 -0
  95. package/dist/chunk-DXGGXIUZ.mjs +57 -0
  96. package/dist/chunk-EGXBZCGC.mjs +943 -0
  97. package/dist/chunk-ERCNLX3V.mjs +176 -0
  98. package/dist/chunk-FQULBZ53.mjs +850 -0
  99. package/dist/chunk-G2AA4QLC.mjs +262 -0
  100. package/dist/chunk-GDBJ5JCU.mjs +488 -0
  101. package/dist/chunk-GJNSJU4S.mjs +19 -0
  102. package/dist/chunk-GZ6DCQKC.mjs +69 -0
  103. package/dist/chunk-H26B4FYG.mjs +167 -0
  104. package/dist/chunk-I4JMR3UR.mjs +21 -0
  105. package/dist/chunk-INV7QKLG.mjs +508 -0
  106. package/dist/chunk-IUDOC7N7.mjs +46 -0
  107. package/dist/chunk-IZWPRDC3.mjs +206 -0
  108. package/dist/chunk-KIMNCZGV.mjs +15 -0
  109. package/dist/chunk-L6HW2DA7.mjs +15 -0
  110. package/dist/chunk-LAZXX4HR.mjs +100 -0
  111. package/dist/chunk-LDKCUMHK.mjs +95 -0
  112. package/dist/chunk-LRXMECUA.mjs +0 -0
  113. package/dist/chunk-M52VMPGA.mjs +119 -0
  114. package/dist/chunk-MGUWEEI6.mjs +160 -0
  115. package/dist/chunk-NRUWQ5Z7.mjs +419 -0
  116. package/dist/chunk-NSEFNNU4.mjs +25360 -0
  117. package/dist/chunk-NTHVDFGO.mjs +138 -0
  118. package/dist/chunk-O3QHXMOX.mjs +3166 -0
  119. package/dist/chunk-P7NH2OSC.mjs +2605 -0
  120. package/dist/chunk-PKMABBB5.mjs +184 -0
  121. package/dist/chunk-PWS6XGJK.mjs +76 -0
  122. package/dist/chunk-R6JJQHFC.mjs +20 -0
  123. package/dist/chunk-RJLLGGPG.mjs +0 -0
  124. package/dist/chunk-SBACDPNX.mjs +689 -0
  125. package/dist/chunk-TO5AFLVQ.mjs +124 -0
  126. package/dist/chunk-TS7GHTG2.mjs +5436 -0
  127. package/dist/chunk-UJ2IMJ4W.mjs +133 -0
  128. package/dist/chunk-UOP63Q54.mjs +102 -0
  129. package/dist/chunk-UUOFWCM6.mjs +78 -0
  130. package/dist/chunk-V4EQTOA4.mjs +893 -0
  131. package/dist/chunk-VJ66NCL4.mjs +193 -0
  132. package/dist/chunk-VQJQHVEV.mjs +29 -0
  133. package/dist/chunk-VTJADRO3.mjs +141 -0
  134. package/dist/chunk-VWF3JO32.mjs +0 -0
  135. package/dist/chunk-W4MGXIRR.mjs +27 -0
  136. package/dist/chunk-W5KKPZT5.mjs +1204 -0
  137. package/dist/chunk-WD34YQ6T.mjs +381 -0
  138. package/dist/chunk-WZBYMYVW.mjs +14 -0
  139. package/dist/chunk-X23WKS3Z.mjs +50 -0
  140. package/dist/chunk-X7TXCYYN.mjs +6496 -0
  141. package/dist/chunk-XGI4EMS3.mjs +140 -0
  142. package/dist/chunk-XZKLBMN6.mjs +1153 -0
  143. package/dist/chunk-YB7INWPY.mjs +0 -0
  144. package/dist/chunk-YV4Y7SDL.mjs +83 -0
  145. package/dist/chunk-YZNBLFIW.mjs +1688 -0
  146. package/dist/chunk-YZZCTONM.mjs +263 -0
  147. package/dist/chunk-ZE6A3FYH.mjs +289 -0
  148. package/dist/cli/nextly.mjs +68 -0
  149. package/dist/cli/utils/index.d.ts +449 -0
  150. package/dist/cli/utils/index.mjs +49 -0
  151. package/dist/component-schema-service-5577KVW6.mjs +11 -0
  152. package/dist/config-loader-23YEMC3Z.mjs +23 -0
  153. package/dist/config.d.ts +44 -0
  154. package/dist/config.mjs +109 -0
  155. package/dist/container-ORGFGYSZ.mjs +9 -0
  156. package/dist/database/index.d.ts +12 -0
  157. package/dist/database/index.mjs +40 -0
  158. package/dist/database/seeders/index.d.ts +93 -0
  159. package/dist/database/seeders/index.mjs +47 -0
  160. package/dist/db-sync-demote-LJGKLB3S.mjs +117 -0
  161. package/dist/db-sync-promote-B26VSYQF.mjs +113 -0
  162. package/dist/dev-reload-broadcaster-B73IQ53V.mjs +25 -0
  163. package/dist/dist-M2NOU37V.mjs +19 -0
  164. package/dist/drizzle-kit-lazy-D2M2PXR2.mjs +13 -0
  165. package/dist/dynamic-collection-schema-service-IEXTPIZ7.mjs +8 -0
  166. package/dist/errors/index.d.ts +159 -0
  167. package/dist/errors/index.mjs +10 -0
  168. package/dist/factory-IWMBKUJM.mjs +15 -0
  169. package/dist/first-run-QIVKWJIF.mjs +63 -0
  170. package/dist/fresh-push-NR67DC3R.mjs +8 -0
  171. package/dist/index.d.ts +4175 -0
  172. package/dist/index.mjs +1336 -0
  173. package/dist/local-plugin-PTET4NAT.mjs +7 -0
  174. package/dist/logger-NU46DXNY.mjs +15 -0
  175. package/dist/logger-YE4TC7ZN.mjs +9 -0
  176. package/dist/migration-journal-EP532Y4L.mjs +139 -0
  177. package/dist/migrations/mysql/0000_eager_sentry.sql +174 -0
  178. package/dist/migrations/mysql/0001_soft_giant_girl.sql +27 -0
  179. package/dist/migrations/mysql/0002_media_table.sql +24 -0
  180. package/dist/migrations/mysql/0003_dynamic_singles.sql +37 -0
  181. package/dist/migrations/mysql/0004_dynamic_components.sql +35 -0
  182. package/dist/migrations/mysql/0005_user_management_tables.sql +92 -0
  183. package/dist/migrations/mysql/0006_api_keys.sql +36 -0
  184. package/dist/migrations/mysql/0007_general_settings.sql +20 -0
  185. package/dist/migrations/mysql/0008_site_settings_logo_url.sql +9 -0
  186. package/dist/migrations/mysql/0009_activity_log.sql +30 -0
  187. package/dist/migrations/mysql/0010_site_settings_sidebar.sql +13 -0
  188. package/dist/migrations/mysql/0011_missing_tables_and_columns.sql +54 -0
  189. package/dist/migrations/mysql/0012_image_sizes_and_focal_point.sql +30 -0
  190. package/dist/migrations/mysql/0012_media_folders.sql +43 -0
  191. package/dist/migrations/mysql/0013_user_brute_force_protection.sql +31 -0
  192. package/dist/migrations/mysql/0014_email_template_attachments.sql +12 -0
  193. package/dist/migrations/mysql/0015_media_uploaded_by_nullable.sql +15 -0
  194. package/dist/migrations/mysql/20260429_000000_000_initial_journal.sql +22 -0
  195. package/dist/migrations/mysql/20260501_000000_journal_batch.sql +17 -0
  196. package/dist/migrations/mysql/20260501_000001_audit_log.sql +24 -0
  197. package/dist/migrations/mysql/20260504_000000_nextly_meta.sql +21 -0
  198. package/dist/migrations/mysql/meta/0000_snapshot.json +1005 -0
  199. package/dist/migrations/mysql/meta/0001_snapshot.json +1099 -0
  200. package/dist/migrations/mysql/meta/_journal.json +41 -0
  201. package/dist/migrations/postgresql/0000_misty_king_bedlam.sql +169 -0
  202. package/dist/migrations/postgresql/0001_perpetual_captain_marvel.sql +8 -0
  203. package/dist/migrations/postgresql/0002_sad_spectrum.sql +16 -0
  204. package/dist/migrations/postgresql/0003_hesitant_ultron.sql +17 -0
  205. package/dist/migrations/postgresql/0004_media_table.sql +24 -0
  206. package/dist/migrations/postgresql/0005_media_folders.sql +36 -0
  207. package/dist/migrations/postgresql/0006_dynamic_collections_update.sql +50 -0
  208. package/dist/migrations/postgresql/0007_dynamic_singles.sql +38 -0
  209. package/dist/migrations/postgresql/0008_dynamic_components.sql +37 -0
  210. package/dist/migrations/postgresql/0009_user_management_tables.sql +95 -0
  211. package/dist/migrations/postgresql/0010_api_keys.sql +34 -0
  212. package/dist/migrations/postgresql/0011_general_settings.sql +20 -0
  213. package/dist/migrations/postgresql/0012_site_settings_logo_url.sql +9 -0
  214. package/dist/migrations/postgresql/0013_activity_log.sql +29 -0
  215. package/dist/migrations/postgresql/0014_image_sizes_and_focal_point.sql +33 -0
  216. package/dist/migrations/postgresql/0014_site_settings_sidebar.sql +13 -0
  217. package/dist/migrations/postgresql/0015_user_brute_force_protection.sql +29 -0
  218. package/dist/migrations/postgresql/0016_email_template_attachments.sql +12 -0
  219. package/dist/migrations/postgresql/0017_media_uploaded_by_nullable.sql +15 -0
  220. package/dist/migrations/postgresql/20260429_000000_000_initial_journal.sql +24 -0
  221. package/dist/migrations/postgresql/20260501_000000_journal_batch.sql +17 -0
  222. package/dist/migrations/postgresql/20260501_000001_audit_log.sql +24 -0
  223. package/dist/migrations/postgresql/20260504_000000_nextly_meta.sql +22 -0
  224. package/dist/migrations/postgresql/meta/0000_snapshot.json +1286 -0
  225. package/dist/migrations/postgresql/meta/0001_snapshot.json +1407 -0
  226. package/dist/migrations/postgresql/meta/0002_snapshot.json +1552 -0
  227. package/dist/migrations/postgresql/meta/0003_snapshot.json +1695 -0
  228. package/dist/migrations/postgresql/meta/0010_snapshot.json +2345 -0
  229. package/dist/migrations/postgresql/meta/_journal.json +90 -0
  230. package/dist/migrations/sqlite/0000_api_keys.sql +34 -0
  231. package/dist/migrations/sqlite/0001_general_settings.sql +20 -0
  232. package/dist/migrations/sqlite/0002_site_settings_logo_url.sql +9 -0
  233. package/dist/migrations/sqlite/0003_activity_log.sql +29 -0
  234. package/dist/migrations/sqlite/0004_image_sizes_and_focal_point.sql +29 -0
  235. package/dist/migrations/sqlite/0004_site_settings_sidebar.sql +11 -0
  236. package/dist/migrations/sqlite/0005_user_brute_force_protection.sql +29 -0
  237. package/dist/migrations/sqlite/0006_email_template_attachments.sql +12 -0
  238. package/dist/migrations/sqlite/0007_media_uploaded_by_nullable.sql +111 -0
  239. package/dist/migrations/sqlite/20260429_000000_000_initial_journal.sql +24 -0
  240. package/dist/migrations/sqlite/20260501_000000_journal_batch.sql +19 -0
  241. package/dist/migrations/sqlite/20260501_000001_audit_log.sql +24 -0
  242. package/dist/migrations/sqlite/20260504_000000_nextly_meta.sql +21 -0
  243. package/dist/migrations/sqlite/20260505_000000_user_management_tables.sql +77 -0
  244. package/dist/next.d.ts +57 -0
  245. package/dist/next.mjs +55 -0
  246. package/dist/observability/index.d.ts +87 -0
  247. package/dist/observability/index.mjs +57 -0
  248. package/dist/permissions-3DZZQZMI.mjs +39 -0
  249. package/dist/pipeline-YOML7SWF.mjs +29 -0
  250. package/dist/preview-ZZTR3QGS.mjs +9 -0
  251. package/dist/program-PW6UB2ZC.mjs +5934 -0
  252. package/dist/reconcile-single-tables-7ENVXJGB.mjs +7 -0
  253. package/dist/register-SF6E6FVU.mjs +49 -0
  254. package/dist/reload-config-HWQ4G5MM.mjs +23 -0
  255. package/dist/resolve-single-table-name-JSOMUB3R.mjs +7 -0
  256. package/dist/routeHandler-UNMMJIBM.mjs +77 -0
  257. package/dist/runtime-schema-generator-NRA6A6Z6.mjs +8 -0
  258. package/dist/runtime.d.ts +120 -0
  259. package/dist/runtime.mjs +73 -0
  260. package/dist/schema-hash-FMMG6VPJ.mjs +13 -0
  261. package/dist/schema-registry-EQ36FZDP.mjs +7 -0
  262. package/dist/scripts/load-env.mjs +42 -0
  263. package/dist/storage/index.d.ts +566 -0
  264. package/dist/storage/index.mjs +45 -0
  265. package/dist/super-admin-G5ZK5F4T.mjs +39 -0
  266. package/dist/system-table-service-WGSRVEGT.mjs +17 -0
  267. package/dist/users-7KELGRYJ.mjs +38 -0
  268. package/package.json +308 -9
@@ -0,0 +1,365 @@
1
+ import {
2
+ rateLimiter
3
+ } from "./chunk-DXGGXIUZ.mjs";
4
+ import {
5
+ getSession
6
+ } from "./chunk-UUOFWCM6.mjs";
7
+ import {
8
+ hasAnyPermission,
9
+ hasPermission
10
+ } from "./chunk-W5KKPZT5.mjs";
11
+ import {
12
+ env
13
+ } from "./chunk-UJ2IMJ4W.mjs";
14
+ import {
15
+ container
16
+ } from "./chunk-D5HQBNUB.mjs";
17
+ import {
18
+ NextlyError
19
+ } from "./chunk-NRUWQ5Z7.mjs";
20
+
21
+ // src/auth/middleware/index.ts
22
+ var DEFAULT_RATE_LIMIT = 1e3;
23
+ var DEFAULT_RATE_WINDOW_MS = 36e5;
24
+ function getRBACService() {
25
+ try {
26
+ if (container.has("rbacAccessControlService")) {
27
+ return container.get(
28
+ "rbacAccessControlService"
29
+ );
30
+ }
31
+ } catch {
32
+ }
33
+ return void 0;
34
+ }
35
+ function getApiKeyService() {
36
+ try {
37
+ if (container.has("apiKeyService")) {
38
+ return container.get("apiKeyService");
39
+ }
40
+ } catch {
41
+ }
42
+ return void 0;
43
+ }
44
+ function getConfig() {
45
+ try {
46
+ if (container.has("config")) {
47
+ return container.get("config");
48
+ }
49
+ } catch {
50
+ }
51
+ return void 0;
52
+ }
53
+ function createErrorResponse(statusCode, message, error, headers, code) {
54
+ return {
55
+ success: false,
56
+ statusCode,
57
+ message,
58
+ error: error || message,
59
+ data: null,
60
+ ...code && { code },
61
+ ...headers && { headers }
62
+ };
63
+ }
64
+ async function requireApiKeyAuth(req) {
65
+ const authHeader = req.headers.get("authorization");
66
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
67
+ return null;
68
+ }
69
+ const rawKey = authHeader.slice(7).trim();
70
+ if (!rawKey) {
71
+ return null;
72
+ }
73
+ const apiKeyService = getApiKeyService();
74
+ if (!apiKeyService) {
75
+ return createErrorResponse(
76
+ 503,
77
+ "Service Unavailable",
78
+ "API key authentication service is not available"
79
+ );
80
+ }
81
+ const keyAuth = await apiKeyService.authenticateApiKey(rawKey);
82
+ if (!keyAuth) {
83
+ return createErrorResponse(
84
+ 401,
85
+ "Invalid API key",
86
+ "The provided API key is invalid, expired, or revoked"
87
+ );
88
+ }
89
+ const cfg = getConfig();
90
+ const resolvedLimit = cfg?.apiKeys?.rateLimit.requestsPerHour ?? DEFAULT_RATE_LIMIT;
91
+ const resolvedWindowMs = cfg?.apiKeys?.rateLimit.windowMs ?? DEFAULT_RATE_WINDOW_MS;
92
+ const rl = rateLimiter.check(keyAuth.id, resolvedLimit, resolvedWindowMs);
93
+ if (!rl.allowed) {
94
+ const retryAfterSecs = Math.ceil(
95
+ (rl.resetAt.getTime() - Date.now()) / 1e3
96
+ );
97
+ return createErrorResponse(
98
+ 429,
99
+ "Too Many Requests",
100
+ "Rate limit exceeded for this API key",
101
+ {
102
+ "Retry-After": String(retryAfterSecs),
103
+ "X-RateLimit-Limit": String(resolvedLimit),
104
+ "X-RateLimit-Remaining": "0"
105
+ }
106
+ );
107
+ }
108
+ const [permissions, roles] = await Promise.all([
109
+ apiKeyService.resolveApiKeyPermissions(
110
+ keyAuth.tokenType,
111
+ keyAuth.roleId,
112
+ keyAuth.userId,
113
+ keyAuth.id
114
+ ),
115
+ apiKeyService.resolveApiKeyRoles(
116
+ keyAuth.tokenType,
117
+ keyAuth.roleId,
118
+ keyAuth.userId
119
+ )
120
+ ]);
121
+ return { userId: keyAuth.userId, permissions, roles };
122
+ }
123
+ async function requireAuthentication(req) {
124
+ const sessionResult = await getSession(req, env.NEXTLY_SECRET || "");
125
+ if (sessionResult.authenticated) {
126
+ const { user } = sessionResult;
127
+ return {
128
+ userId: user.id,
129
+ userName: user.name ?? void 0,
130
+ userEmail: user.email ?? void 0,
131
+ permissions: [],
132
+ roles: user.roleIds,
133
+ authMethod: "session"
134
+ };
135
+ }
136
+ const apiKeyResult = await requireApiKeyAuth(req);
137
+ if (apiKeyResult === null) {
138
+ if (sessionResult.reason === "expired") {
139
+ return createErrorResponse(
140
+ 401,
141
+ "Session expired",
142
+ "Your session has expired, please refresh",
143
+ void 0,
144
+ "TOKEN_EXPIRED"
145
+ );
146
+ }
147
+ return createErrorResponse(
148
+ 401,
149
+ "Authentication required",
150
+ "You must be logged in to access this resource",
151
+ void 0,
152
+ "AUTH_REQUIRED"
153
+ );
154
+ }
155
+ if ("statusCode" in apiKeyResult) {
156
+ return apiKeyResult;
157
+ }
158
+ return {
159
+ userId: apiKeyResult.userId,
160
+ permissions: apiKeyResult.permissions,
161
+ roles: apiKeyResult.roles,
162
+ authMethod: "api-key"
163
+ };
164
+ }
165
+ async function requirePermission(req, action, resource) {
166
+ const authResult = await requireAuthentication(req);
167
+ if ("statusCode" in authResult) {
168
+ return authResult;
169
+ }
170
+ if (authResult.authMethod === "api-key") {
171
+ const slug = `${action}-${resource}`;
172
+ if (!authResult.permissions.includes(slug)) {
173
+ return createErrorResponse(
174
+ 403,
175
+ "Forbidden",
176
+ `You do not have permission to ${action} ${resource}`
177
+ );
178
+ }
179
+ return authResult;
180
+ }
181
+ const rbac = getRBACService();
182
+ const hasAccess = rbac ? await rbac.checkAccess({
183
+ userId: authResult.userId,
184
+ operation: action,
185
+ resource
186
+ }) : await hasPermission(authResult.userId, action, resource);
187
+ if (!hasAccess) {
188
+ return createErrorResponse(
189
+ 403,
190
+ "Forbidden",
191
+ `You do not have permission to ${action} ${resource}`
192
+ );
193
+ }
194
+ return authResult;
195
+ }
196
+ async function requireAnyPermission(req, permissions) {
197
+ const authResult = await requireAuthentication(req);
198
+ if ("statusCode" in authResult) {
199
+ return authResult;
200
+ }
201
+ if (authResult.authMethod === "api-key") {
202
+ const hasAny = permissions.some(
203
+ ({ action, resource }) => authResult.permissions.includes(`${action}-${resource}`)
204
+ );
205
+ if (!hasAny) {
206
+ return createErrorResponse(
207
+ 403,
208
+ "Forbidden",
209
+ `You do not have the required permissions to access this resource`
210
+ );
211
+ }
212
+ return authResult;
213
+ }
214
+ const hasAccess = await hasAnyPermission(authResult.userId, permissions);
215
+ if (!hasAccess) {
216
+ return createErrorResponse(
217
+ 403,
218
+ "Forbidden",
219
+ `You do not have the required permissions to access this resource`
220
+ );
221
+ }
222
+ return authResult;
223
+ }
224
+ async function requireCollectionAccess(req, action, collectionSlug) {
225
+ const authResult = await requireAuthentication(req);
226
+ if ("statusCode" in authResult) {
227
+ return authResult;
228
+ }
229
+ if (authResult.authMethod === "api-key") {
230
+ const slug = `${action}-${collectionSlug}`;
231
+ if (!authResult.permissions.includes(slug)) {
232
+ return createErrorResponse(
233
+ 403,
234
+ "Forbidden",
235
+ `You do not have permission to ${action} ${collectionSlug}`
236
+ );
237
+ }
238
+ const rbac2 = getRBACService();
239
+ if (rbac2) {
240
+ const codeAccess = rbac2.getRegisteredAccess(collectionSlug);
241
+ if (codeAccess) {
242
+ const denied = await evaluateCodeAccess(
243
+ codeAccess,
244
+ action,
245
+ collectionSlug,
246
+ authResult
247
+ );
248
+ if (denied) return denied;
249
+ }
250
+ }
251
+ return authResult;
252
+ }
253
+ const rbac = getRBACService();
254
+ const hasAccess = rbac ? await rbac.checkAccess({
255
+ userId: authResult.userId,
256
+ operation: action,
257
+ resource: collectionSlug
258
+ }) : await hasPermission(authResult.userId, action, collectionSlug);
259
+ if (!hasAccess) {
260
+ return createErrorResponse(
261
+ 403,
262
+ "Forbidden",
263
+ `You do not have permission to ${action} ${collectionSlug}`
264
+ );
265
+ }
266
+ return authResult;
267
+ }
268
+ async function evaluateCodeAccess(codeAccess, operation, resource, authResult) {
269
+ const operationAccess = codeAccess[operation];
270
+ if (operationAccess === void 0) {
271
+ return null;
272
+ }
273
+ if (typeof operationAccess === "boolean") {
274
+ return operationAccess ? null : createErrorResponse(
275
+ 403,
276
+ "Forbidden",
277
+ `You do not have permission to ${operation} ${resource}`
278
+ );
279
+ }
280
+ const ctx = {
281
+ user: { id: authResult.userId },
282
+ roles: authResult.roles,
283
+ permissions: authResult.permissions,
284
+ operation,
285
+ collection: resource
286
+ };
287
+ try {
288
+ const allowed = await operationAccess(ctx);
289
+ return allowed ? null : createErrorResponse(
290
+ 403,
291
+ "Forbidden",
292
+ `You do not have permission to ${operation} ${resource}`
293
+ );
294
+ } catch (error) {
295
+ console.error(
296
+ `[auth] Code access function for ${operation}:${resource} threw:`,
297
+ error
298
+ );
299
+ return createErrorResponse(
300
+ 403,
301
+ "Forbidden",
302
+ `You do not have permission to ${operation} ${resource}`
303
+ );
304
+ }
305
+ }
306
+ function createJsonErrorResponse(error) {
307
+ const { headers: extraHeaders, ...bodyPayload } = error;
308
+ const responseHeaders = {
309
+ "Content-Type": "application/json",
310
+ ...extraHeaders
311
+ };
312
+ return new Response(JSON.stringify({ data: bodyPayload }), {
313
+ status: error.statusCode,
314
+ headers: responseHeaders
315
+ });
316
+ }
317
+ function isErrorResponse(result) {
318
+ return "statusCode" in result;
319
+ }
320
+
321
+ // src/auth/middleware/to-nextly-error.ts
322
+ function toNextlyAuthError(legacy) {
323
+ const logContext = {
324
+ legacyStatus: legacy.statusCode,
325
+ legacyMessage: legacy.message,
326
+ legacyError: legacy.error
327
+ };
328
+ if (legacy.code) logContext.legacyCode = legacy.code;
329
+ if (legacy.statusCode === 401) {
330
+ if (legacy.code === "TOKEN_EXPIRED") {
331
+ return new NextlyError({
332
+ code: "TOKEN_EXPIRED",
333
+ publicMessage: "Authentication required.",
334
+ logContext
335
+ });
336
+ }
337
+ return NextlyError.authRequired({ logContext });
338
+ }
339
+ if (legacy.statusCode === 403) {
340
+ return NextlyError.forbidden({ logContext });
341
+ }
342
+ if (legacy.statusCode === 429) {
343
+ const headerLookup = legacy.headers ? new Headers(legacy.headers) : void 0;
344
+ const retryAfterRaw = headerLookup?.get("retry-after") ?? void 0;
345
+ const retryAfterSeconds = retryAfterRaw ? Number(retryAfterRaw) : void 0;
346
+ return NextlyError.rateLimited({
347
+ retryAfterSeconds: typeof retryAfterSeconds === "number" && Number.isFinite(retryAfterSeconds) ? retryAfterSeconds : void 0,
348
+ logContext
349
+ });
350
+ }
351
+ if (legacy.statusCode === 503) {
352
+ return NextlyError.serviceUnavailable({ logContext });
353
+ }
354
+ return NextlyError.internal({ logContext });
355
+ }
356
+
357
+ export {
358
+ requireAuthentication,
359
+ requirePermission,
360
+ requireAnyPermission,
361
+ requireCollectionAccess,
362
+ createJsonErrorResponse,
363
+ isErrorResponse,
364
+ toNextlyAuthError
365
+ };
@@ -0,0 +1,13 @@
1
+ // src/observability/on-error.ts
2
+ var globalOnError;
3
+ function setGlobalOnError(hook) {
4
+ globalOnError = hook;
5
+ }
6
+ function getGlobalOnError() {
7
+ return globalOnError;
8
+ }
9
+
10
+ export {
11
+ setGlobalOnError,
12
+ getGlobalOnError
13
+ };
@@ -0,0 +1,87 @@
1
+ // src/database/schema-registry.ts
2
+ import { getTableName, is } from "drizzle-orm";
3
+ import { MySqlTable } from "drizzle-orm/mysql-core";
4
+ import { PgTable } from "drizzle-orm/pg-core";
5
+ import { SQLiteTable } from "drizzle-orm/sqlite-core";
6
+ var SchemaRegistry = class {
7
+ dialect;
8
+ // Keyed by SQL table name (e.g., "dynamic_collections", not "dynamicCollections")
9
+ staticSchemas = {};
10
+ dynamicSchemas = /* @__PURE__ */ new Map();
11
+ constructor(dialect) {
12
+ this.dialect = dialect;
13
+ }
14
+ getDialect() {
15
+ return this.dialect;
16
+ }
17
+ // Register system tables from the dialect schema exports.
18
+ // Converts from JS export names (camelCase) to SQL table names (snake_case)
19
+ // so that getTable("dynamic_collections") works correctly.
20
+ registerStaticSchemas(schemas) {
21
+ this.staticSchemas = {};
22
+ for (const [key, value] of Object.entries(schemas)) {
23
+ if (this.isDrizzleTable(value)) {
24
+ try {
25
+ const sqlName = getTableName(value);
26
+ this.staticSchemas[sqlName] = value;
27
+ } catch {
28
+ this.staticSchemas[key] = value;
29
+ }
30
+ } else {
31
+ this.staticSchemas[key] = value;
32
+ }
33
+ }
34
+ }
35
+ // Register a single dynamic collection's table object.
36
+ // Called per collection when loading from dynamic_collections table.
37
+ // tableName should be the SQL table name (e.g., "dc_products").
38
+ registerDynamicSchema(tableName, table) {
39
+ this.dynamicSchemas.set(tableName, table);
40
+ }
41
+ // Look up a table object by SQL table name (for queries).
42
+ // Checks dynamic schemas first since they may override static in edge cases.
43
+ getTable(tableName) {
44
+ if (this.dynamicSchemas.has(tableName)) {
45
+ return this.dynamicSchemas.get(tableName);
46
+ }
47
+ if (tableName in this.staticSchemas) {
48
+ return this.staticSchemas[tableName];
49
+ }
50
+ return null;
51
+ }
52
+ // Get all schemas merged (for pushSchema() and drizzle() init).
53
+ // Dynamic schemas override static schemas with the same key.
54
+ getAllSchemas() {
55
+ const dynamicObj = {};
56
+ for (const [key, value] of this.dynamicSchemas) {
57
+ dynamicObj[key] = value;
58
+ }
59
+ return { ...this.staticSchemas, ...dynamicObj };
60
+ }
61
+ // Get only dynamic table names (for cleanup, debugging)
62
+ getDynamicTableNames() {
63
+ return Array.from(this.dynamicSchemas.keys());
64
+ }
65
+ // Clear dynamic schemas (before reload on restart)
66
+ clear() {
67
+ this.dynamicSchemas.clear();
68
+ }
69
+ // Check if a table exists in the registry
70
+ hasTable(tableName) {
71
+ return this.dynamicSchemas.has(tableName) || tableName in this.staticSchemas;
72
+ }
73
+ // Check if a value is a Drizzle table object
74
+ isDrizzleTable(value) {
75
+ if (!value || typeof value !== "object") return false;
76
+ try {
77
+ return is(value, PgTable) || is(value, MySqlTable) || is(value, SQLiteTable);
78
+ } catch {
79
+ const obj = value;
80
+ return "_" in obj && typeof obj._?.name === "string";
81
+ }
82
+ }
83
+ };
84
+
85
+ export {
86
+ SchemaRegistry
87
+ };