@nextsparkjs/core 0.1.0-beta.185 → 0.1.0-beta.187

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 (518) hide show
  1. package/dist/components/app/guards/DeveloperGuard.d.ts.map +1 -1
  2. package/dist/components/app/guards/DeveloperGuard.js +8 -4
  3. package/dist/components/app/guards/SuperAdminGuard.d.ts.map +1 -1
  4. package/dist/components/app/guards/SuperAdminGuard.js +8 -4
  5. package/dist/components/app/layouts/PublicFooter.d.ts.map +1 -1
  6. package/dist/components/app/layouts/PublicFooter.js +2 -2
  7. package/dist/components/app/layouts/PublicNavbar.d.ts.map +1 -1
  8. package/dist/components/app/layouts/PublicNavbar.js +2 -2
  9. package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
  10. package/dist/components/app/misc/ThemeToggle.js +4 -1
  11. package/dist/components/auth/DevKeyring.d.ts.map +1 -1
  12. package/dist/components/auth/DevKeyring.js +10 -2
  13. package/dist/components/auth/SessionCookieRefresher.d.ts +21 -0
  14. package/dist/components/auth/SessionCookieRefresher.d.ts.map +1 -0
  15. package/dist/components/auth/SessionCookieRefresher.js +11 -0
  16. package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
  17. package/dist/components/auth/forms/LoginForm.js +300 -6
  18. package/dist/components/auth/index.d.ts +1 -0
  19. package/dist/components/auth/index.d.ts.map +1 -1
  20. package/dist/components/auth/index.js +2 -0
  21. package/dist/components/billing/InvoicesTable.d.ts.map +1 -1
  22. package/dist/components/billing/InvoicesTable.js +4 -3
  23. package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
  24. package/dist/components/dashboard/layouts/AuthGuard.d.ts.map +1 -1
  25. package/dist/components/dashboard/layouts/AuthGuard.js +2 -2
  26. package/dist/components/dashboard/layouts/DashboardAuthLayout.d.ts.map +1 -1
  27. package/dist/components/dashboard/layouts/DashboardAuthLayout.js +2 -2
  28. package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts +17 -0
  29. package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts.map +1 -0
  30. package/dist/components/dashboard/layouts/DashboardAuthSkeleton.js +52 -0
  31. package/dist/components/dashboard/layouts/Sidebar.d.ts.map +1 -1
  32. package/dist/components/dashboard/layouts/Sidebar.js +2 -3
  33. package/dist/components/dashboard/layouts/TopNavbar.js +2 -2
  34. package/dist/components/dashboard/layouts/index.d.ts +1 -0
  35. package/dist/components/dashboard/layouts/index.d.ts.map +1 -1
  36. package/dist/components/dashboard/layouts/index.js +2 -0
  37. package/dist/components/devtools/api-explorer/ApiExplorer.js +4 -4
  38. package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
  39. package/dist/components/entities/EntityFieldRenderer.js +3 -0
  40. package/dist/components/entities/EntityTable.d.ts.map +1 -1
  41. package/dist/components/entities/EntityTable.js +2 -1
  42. package/dist/components/entities/wrappers/EntityFormWrapper.d.ts.map +1 -1
  43. package/dist/components/entities/wrappers/EntityFormWrapper.js +7 -2
  44. package/dist/components/patterns/pages/PatternEditPage.d.ts +5 -1
  45. package/dist/components/patterns/pages/PatternEditPage.d.ts.map +1 -1
  46. package/dist/components/patterns/pages/PatternEditPage.js +16 -5
  47. package/dist/components/patterns/pages/PatternsCreatePage.d.ts +2 -1
  48. package/dist/components/patterns/pages/PatternsCreatePage.d.ts.map +1 -1
  49. package/dist/components/patterns/pages/PatternsCreatePage.js +17 -5
  50. package/dist/components/ui/combobox.d.ts +8 -1
  51. package/dist/components/ui/combobox.d.ts.map +1 -1
  52. package/dist/components/ui/combobox.js +37 -44
  53. package/dist/components/ui/country-select.d.ts +3 -1
  54. package/dist/components/ui/country-select.d.ts.map +1 -1
  55. package/dist/components/ui/country-select.js +3 -1
  56. package/dist/components/ui/currency-select.d.ts +3 -1
  57. package/dist/components/ui/currency-select.d.ts.map +1 -1
  58. package/dist/components/ui/currency-select.js +3 -1
  59. package/dist/components/ui/sheet.d.ts +5 -0
  60. package/dist/components/ui/sheet.d.ts.map +1 -1
  61. package/dist/components/ui/sheet.js +46 -26
  62. package/dist/components/ui/timezone-select.d.ts +3 -1
  63. package/dist/components/ui/timezone-select.d.ts.map +1 -1
  64. package/dist/components/ui/timezone-select.js +3 -1
  65. package/dist/contexts/TeamContext.d.ts +6 -0
  66. package/dist/contexts/TeamContext.d.ts.map +1 -1
  67. package/dist/contexts/TeamContext.js +8 -1
  68. package/dist/emails/otp-verification.d.ts.map +1 -1
  69. package/dist/emails/reset-password.d.ts.map +1 -1
  70. package/dist/emails/team-invitation.d.ts.map +1 -1
  71. package/dist/emails/team-invitation.js +3 -2
  72. package/dist/emails/verify-email.d.ts.map +1 -1
  73. package/dist/hooks/index.d.ts +1 -0
  74. package/dist/hooks/index.d.ts.map +1 -1
  75. package/dist/hooks/index.js +1 -0
  76. package/dist/hooks/useAuth.d.ts +29 -0
  77. package/dist/hooks/useAuth.d.ts.map +1 -1
  78. package/dist/hooks/useAuth.js +24 -0
  79. package/dist/hooks/useEnsureUserMetadata.d.ts +22 -0
  80. package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
  81. package/dist/hooks/useEnsureUserMetadata.js +18 -5
  82. package/dist/hooks/useSessionCookieRefresh.d.ts +35 -0
  83. package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -0
  84. package/dist/hooks/useSessionCookieRefresh.js +49 -0
  85. package/dist/lib/api/api-error.d.ts +3 -1
  86. package/dist/lib/api/api-error.d.ts.map +1 -1
  87. package/dist/lib/api/api-error.js +4 -2
  88. package/dist/lib/api/auth/dual-auth.d.ts +38 -3
  89. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  90. package/dist/lib/api/auth/dual-auth.js +42 -7
  91. package/dist/lib/api/auth/permissions.d.ts +7 -2
  92. package/dist/lib/api/auth/permissions.d.ts.map +1 -1
  93. package/dist/lib/api/auth/permissions.js +2 -2
  94. package/dist/lib/api/auth/scope-policy.d.ts +71 -0
  95. package/dist/lib/api/auth/scope-policy.d.ts.map +1 -0
  96. package/dist/lib/api/auth/scope-policy.js +53 -0
  97. package/dist/lib/api/auth.d.ts +15 -0
  98. package/dist/lib/api/auth.d.ts.map +1 -1
  99. package/dist/lib/api/auth.js +17 -4
  100. package/dist/lib/api/entities.js +2 -1
  101. package/dist/lib/api/entity/audit-log.d.ts +35 -0
  102. package/dist/lib/api/entity/audit-log.d.ts.map +1 -0
  103. package/dist/lib/api/entity/audit-log.js +35 -0
  104. package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
  105. package/dist/lib/api/entity/generic-handler.js +333 -138
  106. package/dist/lib/api/entity-handler.js +2 -2
  107. package/dist/lib/api/helpers.d.ts +29 -4
  108. package/dist/lib/api/helpers.d.ts.map +1 -1
  109. package/dist/lib/api/helpers.js +30 -6
  110. package/dist/lib/api/keys.d.ts +16 -0
  111. package/dist/lib/api/keys.d.ts.map +1 -1
  112. package/dist/lib/api/keys.js +19 -1
  113. package/dist/lib/api/routes/patterns-usages.d.ts.map +1 -1
  114. package/dist/lib/api/routes/patterns-usages.js +3 -11
  115. package/dist/lib/auth/auth-methods.d.ts +52 -0
  116. package/dist/lib/auth/auth-methods.d.ts.map +1 -0
  117. package/dist/lib/auth/auth-methods.js +63 -0
  118. package/dist/lib/auth/registration-guard-plugin.d.ts.map +1 -1
  119. package/dist/lib/auth/registration-helpers.d.ts +9 -0
  120. package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
  121. package/dist/lib/auth/registration-helpers.js +8 -2
  122. package/dist/lib/auth/security-notifications/device-fingerprint.d.ts +52 -0
  123. package/dist/lib/auth/security-notifications/device-fingerprint.d.ts.map +1 -0
  124. package/dist/lib/auth/security-notifications/device-fingerprint.js +42 -0
  125. package/dist/lib/auth/security-notifications/dispatcher.d.ts +37 -0
  126. package/dist/lib/auth/security-notifications/dispatcher.d.ts.map +1 -0
  127. package/dist/lib/auth/security-notifications/dispatcher.js +157 -0
  128. package/dist/lib/auth/security-notifications/index.d.ts +20 -0
  129. package/dist/lib/auth/security-notifications/index.d.ts.map +1 -0
  130. package/dist/lib/auth/security-notifications/index.js +22 -0
  131. package/dist/lib/auth/security-notifications/registry.d.ts +36 -0
  132. package/dist/lib/auth/security-notifications/registry.d.ts.map +1 -0
  133. package/dist/lib/auth/security-notifications/registry.js +26 -0
  134. package/dist/lib/auth/security-notifications/templates/email-changed.d.ts +12 -0
  135. package/dist/lib/auth/security-notifications/templates/email-changed.d.ts.map +1 -0
  136. package/dist/lib/auth/security-notifications/templates/email-changed.js +43 -0
  137. package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts +7 -0
  138. package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts.map +1 -0
  139. package/dist/lib/auth/security-notifications/templates/new-device-login.js +46 -0
  140. package/dist/lib/auth/security-notifications/templates/password-changed.d.ts +7 -0
  141. package/dist/lib/auth/security-notifications/templates/password-changed.d.ts.map +1 -0
  142. package/dist/lib/auth/security-notifications/templates/password-changed.js +35 -0
  143. package/dist/lib/auth/security-notifications/templates/shared.d.ts +36 -0
  144. package/dist/lib/auth/security-notifications/templates/shared.d.ts.map +1 -0
  145. package/dist/lib/auth/security-notifications/templates/shared.js +68 -0
  146. package/dist/lib/auth/security-notifications/types.d.ts +74 -0
  147. package/dist/lib/auth/security-notifications/types.d.ts.map +1 -0
  148. package/dist/lib/auth/session-config.d.ts +37 -0
  149. package/dist/lib/auth/session-config.d.ts.map +1 -0
  150. package/dist/lib/auth/session-config.js +59 -0
  151. package/dist/lib/auth/session-refresh.d.ts +46 -0
  152. package/dist/lib/auth/session-refresh.d.ts.map +1 -0
  153. package/dist/lib/auth/session-refresh.js +11 -0
  154. package/dist/lib/auth-client.d.ts +1639 -0
  155. package/dist/lib/auth-client.d.ts.map +1 -0
  156. package/dist/lib/auth-client.js +13 -1
  157. package/dist/lib/auth.d.ts.map +1 -1
  158. package/dist/lib/auth.js +45 -11
  159. package/dist/lib/billing/gateways/factory.d.ts +8 -3
  160. package/dist/lib/billing/gateways/factory.d.ts.map +1 -1
  161. package/dist/lib/billing/gateways/factory.js +37 -23
  162. package/dist/lib/billing/stripe-webhook.d.ts.map +1 -1
  163. package/dist/lib/billing/stripe-webhook.js +2 -1
  164. package/dist/lib/config/app.config.d.ts.map +1 -1
  165. package/dist/lib/config/app.config.js +71 -0
  166. package/dist/lib/config/config-sync.d.ts +1 -4
  167. package/dist/lib/config/config-sync.d.ts.map +1 -1
  168. package/dist/lib/config/config-sync.js +6 -2
  169. package/dist/lib/config/types.d.ts +130 -0
  170. package/dist/lib/config/types.d.ts.map +1 -1
  171. package/dist/lib/email/templates.d.ts +3 -3
  172. package/dist/lib/email/templates.d.ts.map +1 -1
  173. package/dist/lib/entities/api-generator.js +5 -5
  174. package/dist/lib/entities/entity-hooks.d.ts +9 -1
  175. package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
  176. package/dist/lib/entities/entity-hooks.js +77 -4
  177. package/dist/lib/entities/schema-generator.d.ts.map +1 -1
  178. package/dist/lib/entities/schema-generator.js +20 -4
  179. package/dist/lib/entities/serialization.d.ts +2 -2
  180. package/dist/lib/entities/serialization.d.ts.map +1 -1
  181. package/dist/lib/entities/serialization.js +3 -2
  182. package/dist/lib/entities/types.d.ts +20 -0
  183. package/dist/lib/entities/types.d.ts.map +1 -1
  184. package/dist/lib/locale.d.ts.map +1 -1
  185. package/dist/lib/locale.js +4 -1
  186. package/dist/lib/mcp/audit.d.ts +8 -4
  187. package/dist/lib/mcp/audit.d.ts.map +1 -1
  188. package/dist/lib/plugins/hook-system.d.ts +13 -0
  189. package/dist/lib/plugins/hook-system.d.ts.map +1 -1
  190. package/dist/lib/plugins/hook-system.js +32 -2
  191. package/dist/lib/scheduled-actions/handlers/index.d.ts +1 -0
  192. package/dist/lib/scheduled-actions/handlers/index.d.ts.map +1 -1
  193. package/dist/lib/scheduled-actions/handlers/index.js +3 -1
  194. package/dist/lib/scheduled-actions/handlers/security-notification.d.ts +26 -0
  195. package/dist/lib/scheduled-actions/handlers/security-notification.d.ts.map +1 -0
  196. package/dist/lib/scheduled-actions/handlers/security-notification.js +54 -0
  197. package/dist/lib/selectors/core-selectors.d.ts +12 -0
  198. package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
  199. package/dist/lib/selectors/domains/auth.selectors.d.ts +12 -0
  200. package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
  201. package/dist/lib/selectors/domains/auth.selectors.js +14 -1
  202. package/dist/lib/selectors/selectors.d.ts +24 -0
  203. package/dist/lib/selectors/selectors.d.ts.map +1 -1
  204. package/dist/lib/services/api-docs.service.d.ts +5 -1
  205. package/dist/lib/services/api-docs.service.d.ts.map +1 -1
  206. package/dist/lib/services/api-presets.service.d.ts +6 -1
  207. package/dist/lib/services/api-presets.service.d.ts.map +1 -1
  208. package/dist/lib/services/feature.service.d.ts +58 -4
  209. package/dist/lib/services/feature.service.d.ts.map +1 -1
  210. package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
  211. package/dist/lib/services/generic-entity.service.js +16 -32
  212. package/dist/lib/services/membership.service.d.ts +10 -2
  213. package/dist/lib/services/membership.service.d.ts.map +1 -1
  214. package/dist/lib/services/membership.service.js +37 -16
  215. package/dist/lib/services/permission.service.d.ts +10 -4
  216. package/dist/lib/services/permission.service.d.ts.map +1 -1
  217. package/dist/lib/services/plugin.service.d.ts +1 -1
  218. package/dist/lib/services/plugin.service.d.ts.map +1 -1
  219. package/dist/lib/services/subscription.service.d.ts.map +1 -1
  220. package/dist/lib/services/subscription.service.js +2 -1
  221. package/dist/lib/services/team-member.service.d.ts +15 -3
  222. package/dist/lib/services/team-member.service.d.ts.map +1 -1
  223. package/dist/lib/services/team-member.service.js +27 -6
  224. package/dist/lib/services/team.service.d.ts.map +1 -1
  225. package/dist/lib/teams/permissions.d.ts +3 -1
  226. package/dist/lib/teams/permissions.d.ts.map +1 -1
  227. package/dist/lib/teams/permissions.js +6 -0
  228. package/dist/lib/teams/schema.d.ts.map +1 -1
  229. package/dist/lib/teams/schema.js +3 -0
  230. package/dist/lib/template-resolver.d.ts +3 -3
  231. package/dist/lib/theme/forced-theme.d.ts +34 -0
  232. package/dist/lib/theme/forced-theme.d.ts.map +1 -0
  233. package/dist/lib/theme/forced-theme.js +27 -0
  234. package/dist/lib/theme/get-default-theme-mode.d.ts +3 -0
  235. package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
  236. package/dist/lib/theme/get-default-theme-mode.js +5 -3
  237. package/dist/lib/utils/cors.d.ts +13 -0
  238. package/dist/lib/utils/cors.d.ts.map +1 -1
  239. package/dist/lib/utils/cors.js +20 -0
  240. package/dist/messages/de/auth.json +25 -2
  241. package/dist/messages/de/index.d.ts +83 -0
  242. package/dist/messages/de/index.d.ts.map +1 -1
  243. package/dist/messages/de/index.js +2 -0
  244. package/dist/messages/de/index.ts +2 -0
  245. package/dist/messages/de/navigation.json +3 -1
  246. package/dist/messages/en/auth.json +25 -2
  247. package/dist/messages/en/index.d.ts +83 -0
  248. package/dist/messages/en/index.d.ts.map +1 -1
  249. package/dist/messages/en/index.js +2 -0
  250. package/dist/messages/en/index.ts +2 -0
  251. package/dist/messages/en/navigation.json +3 -1
  252. package/dist/messages/es/auth.json +25 -2
  253. package/dist/messages/es/index.d.ts +83 -0
  254. package/dist/messages/es/index.d.ts.map +1 -1
  255. package/dist/messages/es/index.js +2 -0
  256. package/dist/messages/es/index.ts +2 -0
  257. package/dist/messages/es/navigation.json +3 -1
  258. package/dist/messages/fr/auth.json +25 -2
  259. package/dist/messages/fr/index.d.ts +83 -0
  260. package/dist/messages/fr/index.d.ts.map +1 -1
  261. package/dist/messages/fr/index.js +2 -0
  262. package/dist/messages/fr/index.ts +2 -0
  263. package/dist/messages/fr/navigation.json +3 -1
  264. package/dist/messages/it/auth.json +25 -2
  265. package/dist/messages/it/index.d.ts +83 -0
  266. package/dist/messages/it/index.d.ts.map +1 -1
  267. package/dist/messages/it/index.js +2 -0
  268. package/dist/messages/it/index.ts +2 -0
  269. package/dist/messages/it/navigation.json +3 -1
  270. package/dist/messages/pt/auth.json +25 -2
  271. package/dist/messages/pt/index.d.ts +83 -0
  272. package/dist/messages/pt/index.d.ts.map +1 -1
  273. package/dist/messages/pt/index.js +2 -0
  274. package/dist/messages/pt/index.ts +2 -0
  275. package/dist/messages/pt/navigation.json +3 -1
  276. package/dist/migrations/005_api_audit_log.sql +2 -1
  277. package/dist/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
  278. package/dist/migrations/026_login_events.sql +91 -0
  279. package/dist/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
  280. package/dist/nextspark-registries.d.ts +580 -236
  281. package/dist/providers/DashboardProviders.d.ts +12 -3
  282. package/dist/providers/DashboardProviders.d.ts.map +1 -1
  283. package/dist/providers/DashboardProviders.js +6 -2
  284. package/dist/providers/theme-provider.d.ts +19 -2
  285. package/dist/providers/theme-provider.d.ts.map +1 -1
  286. package/dist/providers/theme-provider.js +6 -2
  287. package/dist/templates/app/(auth)/signup/page.tsx +8 -0
  288. package/dist/templates/app/(public)/[...slug]/page.tsx +9 -14
  289. package/dist/templates/app/api/auth/[...all]/route.ts +23 -1
  290. package/dist/templates/app/api/health/route.ts +4 -2
  291. package/dist/templates/app/api/internal/user-metadata/route.ts +5 -3
  292. package/dist/templates/app/api/mcp/route.ts +4 -1
  293. package/dist/templates/app/api/superadmin/subscriptions/route.ts +2 -1
  294. package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
  295. package/dist/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
  296. package/dist/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
  297. package/dist/templates/app/api/v1/api-keys/route.ts +21 -43
  298. package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
  299. package/dist/templates/app/api/v1/billing/cancel/route.ts +7 -6
  300. package/dist/templates/app/api/v1/billing/change-plan/route.ts +5 -4
  301. package/dist/templates/app/api/v1/billing/check-action/route.ts +6 -4
  302. package/dist/templates/app/api/v1/billing/checkout/route.ts +7 -5
  303. package/dist/templates/app/api/v1/billing/plans/route.ts +8 -12
  304. package/dist/templates/app/api/v1/billing/portal/route.ts +7 -5
  305. package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
  306. package/dist/templates/app/api/v1/devtools/blocks/route.ts +5 -4
  307. package/dist/templates/app/api/v1/devtools/features/route.ts +5 -4
  308. package/dist/templates/app/api/v1/devtools/flows/route.ts +5 -4
  309. package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
  310. package/dist/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
  311. package/dist/templates/app/api/v1/devtools/testing/route.ts +5 -4
  312. package/dist/templates/app/api/v1/media/[id]/route.ts +13 -25
  313. package/dist/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
  314. package/dist/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
  315. package/dist/templates/app/api/v1/media/route.ts +5 -9
  316. package/dist/templates/app/api/v1/media/upload/route.ts +9 -21
  317. package/dist/templates/app/api/v1/media-tags/route.ts +9 -13
  318. package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
  319. package/dist/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
  320. package/dist/templates/app/api/v1/post-categories/route.ts +5 -4
  321. package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
  322. package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
  323. package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
  324. package/dist/templates/app/api/v1/team-invitations/route.ts +9 -11
  325. package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
  326. package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
  327. package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
  328. package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
  329. package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
  330. package/dist/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
  331. package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
  332. package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
  333. package/dist/templates/app/api/v1/teams/route.ts +33 -27
  334. package/dist/templates/app/api/v1/teams/switch/route.ts +11 -13
  335. package/dist/templates/app/api/v1/theme/[...path]/route.ts +23 -2
  336. package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
  337. package/dist/templates/app/api/v1/users/[id]/route.ts +35 -68
  338. package/dist/templates/app/api/v1/users/route.ts +21 -25
  339. package/dist/templates/app/dashboard/layout.tsx +2 -6
  340. package/dist/templates/app/layout.ppr.tsx +15 -15
  341. package/dist/templates/app/layout.tsx +46 -20
  342. package/dist/templates/contents/themes/starter/config/app.config.ts +19 -0
  343. package/dist/templates/contents/themes/starter/config/theme.config.ts +6 -0
  344. package/dist/templates/contents/themes/starter/styles/globals.css +6 -2
  345. package/dist/templates/proxy.ts +46 -10
  346. package/dist/templates/tsconfig.json +4 -0
  347. package/dist/types/theme.d.ts +9 -0
  348. package/dist/types/theme.d.ts.map +1 -1
  349. package/migrations/005_api_audit_log.sql +2 -1
  350. package/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
  351. package/migrations/026_login_events.sql +91 -0
  352. package/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
  353. package/package.json +14 -11
  354. package/scripts/build/registry/__tests__/scope-registry.test.mjs +18 -0
  355. package/scripts/build/registry/generators/scope-registry.mjs +9 -2
  356. package/templates/app/(auth)/signup/page.tsx +8 -0
  357. package/templates/app/(public)/[...slug]/page.tsx +9 -14
  358. package/templates/app/api/auth/[...all]/route.ts +23 -1
  359. package/templates/app/api/health/route.ts +4 -2
  360. package/templates/app/api/internal/user-metadata/route.ts +5 -3
  361. package/templates/app/api/mcp/route.ts +4 -1
  362. package/templates/app/api/superadmin/subscriptions/route.ts +2 -1
  363. package/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
  364. package/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
  365. package/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
  366. package/templates/app/api/v1/api-keys/route.ts +21 -43
  367. package/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
  368. package/templates/app/api/v1/billing/cancel/route.ts +7 -6
  369. package/templates/app/api/v1/billing/change-plan/route.ts +5 -4
  370. package/templates/app/api/v1/billing/check-action/route.ts +6 -4
  371. package/templates/app/api/v1/billing/checkout/route.ts +7 -5
  372. package/templates/app/api/v1/billing/plans/route.ts +8 -12
  373. package/templates/app/api/v1/billing/portal/route.ts +7 -5
  374. package/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
  375. package/templates/app/api/v1/devtools/blocks/route.ts +5 -4
  376. package/templates/app/api/v1/devtools/features/route.ts +5 -4
  377. package/templates/app/api/v1/devtools/flows/route.ts +5 -4
  378. package/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
  379. package/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
  380. package/templates/app/api/v1/devtools/testing/route.ts +5 -4
  381. package/templates/app/api/v1/media/[id]/route.ts +13 -25
  382. package/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
  383. package/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
  384. package/templates/app/api/v1/media/route.ts +5 -9
  385. package/templates/app/api/v1/media/upload/route.ts +9 -21
  386. package/templates/app/api/v1/media-tags/route.ts +9 -13
  387. package/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
  388. package/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
  389. package/templates/app/api/v1/post-categories/route.ts +5 -4
  390. package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
  391. package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
  392. package/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
  393. package/templates/app/api/v1/team-invitations/route.ts +9 -11
  394. package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
  395. package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
  396. package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
  397. package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
  398. package/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
  399. package/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
  400. package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
  401. package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
  402. package/templates/app/api/v1/teams/route.ts +33 -27
  403. package/templates/app/api/v1/teams/switch/route.ts +11 -13
  404. package/templates/app/api/v1/theme/[...path]/route.ts +23 -2
  405. package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
  406. package/templates/app/api/v1/users/[id]/route.ts +35 -68
  407. package/templates/app/api/v1/users/route.ts +21 -25
  408. package/templates/app/dashboard/layout.tsx +2 -6
  409. package/templates/app/layout.ppr.tsx +15 -15
  410. package/templates/app/layout.tsx +46 -20
  411. package/templates/contents/themes/starter/config/app.config.ts +19 -0
  412. package/templates/contents/themes/starter/config/theme.config.ts +6 -0
  413. package/templates/contents/themes/starter/styles/globals.css +6 -2
  414. package/templates/proxy.ts +46 -10
  415. package/templates/tsconfig.json +4 -0
  416. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +23 -0
  417. package/tests/jest/__mocks__/next-server.js +24 -0
  418. package/dist/components/teams/TeamSwitchModal.d.js +0 -0
  419. package/dist/components/ui/accordion.d.js +0 -0
  420. package/dist/components/ui/avatar.d.js +0 -0
  421. package/dist/components/ui/badge.d.js +0 -0
  422. package/dist/components/ui/button.d.js +0 -0
  423. package/dist/components/ui/card.d.js +0 -0
  424. package/dist/components/ui/dialog.d.js +0 -0
  425. package/dist/components/ui/dropdown-menu.d.js +0 -0
  426. package/dist/components/ui/input.d.js +0 -0
  427. package/dist/components/ui/progress.d.js +0 -0
  428. package/dist/components/ui/scroll-area.d.js +0 -0
  429. package/dist/components/ui/select.d.js +0 -0
  430. package/dist/components/ui/separator.d.js +0 -0
  431. package/dist/components/ui/table.d.js +0 -0
  432. package/dist/components/ui/textarea.d.js +0 -0
  433. package/dist/contexts/TeamContext.d.js +0 -0
  434. package/dist/hooks/useAuth.d.js +0 -0
  435. package/dist/hooks/useLastAuthMethod.d.js +0 -0
  436. package/dist/hooks/useOrigin.d.js +0 -0
  437. package/dist/lib/api/auth/dual-auth.d.js +0 -0
  438. package/dist/lib/api/auth.d.js +0 -0
  439. package/dist/lib/api/cache.d.js +0 -0
  440. package/dist/lib/api/keys.d.js +0 -0
  441. package/dist/lib/auth-client.d.js +0 -0
  442. package/dist/lib/auth-context.d.js +0 -0
  443. package/dist/lib/auth.d.js +0 -0
  444. package/dist/lib/billing/config-types.d.js +0 -0
  445. package/dist/lib/billing/enforcement.d.js +0 -0
  446. package/dist/lib/billing/helpers.d.js +0 -0
  447. package/dist/lib/billing/jobs.d.js +0 -0
  448. package/dist/lib/billing/types.d.js +0 -0
  449. package/dist/lib/blocks/loader.d.js +0 -0
  450. package/dist/lib/config/app.config.d.js +0 -0
  451. package/dist/lib/config/config-loader.d.js +0 -0
  452. package/dist/lib/config/config-sync.d.js +0 -0
  453. package/dist/lib/config/config-types.d.js +0 -0
  454. package/dist/lib/config/config-utils.d.js +0 -0
  455. package/dist/lib/config/dashboard-types.d.js +0 -0
  456. package/dist/lib/config/dashboard.config.d.js +0 -0
  457. package/dist/lib/config/icon-map.d.js +0 -0
  458. package/dist/lib/config/index.d.js +0 -7
  459. package/dist/lib/config/nextspark-types.d.js +0 -0
  460. package/dist/lib/config/roles-merge.d.js +0 -0
  461. package/dist/lib/config/types.d.js +0 -0
  462. package/dist/lib/db.d.js +0 -0
  463. package/dist/lib/email/factory.d.js +0 -0
  464. package/dist/lib/email/index.d.js +0 -3
  465. package/dist/lib/email/providers/console.d.js +0 -0
  466. package/dist/lib/email/providers/resend.d.js +0 -0
  467. package/dist/lib/email/templates.d.js +0 -0
  468. package/dist/lib/email/types.d.js +0 -0
  469. package/dist/lib/entities/types.d.js +0 -0
  470. package/dist/lib/permissions/system.d.js +0 -0
  471. package/dist/lib/permissions/types.d.js +0 -0
  472. package/dist/lib/plugins/hook-system.d.js +0 -0
  473. package/dist/lib/scheduled-actions/cleanup.d.js +0 -0
  474. package/dist/lib/scheduled-actions/index.d.js +0 -19
  475. package/dist/lib/scheduled-actions/initializer.d.js +0 -1
  476. package/dist/lib/scheduled-actions/processor.d.js +0 -0
  477. package/dist/lib/scheduled-actions/registry.d.js +0 -0
  478. package/dist/lib/scheduled-actions/scheduler.d.js +0 -0
  479. package/dist/lib/scheduled-actions/types.d.js +0 -0
  480. package/dist/lib/services/api-routes.service.d.js +0 -0
  481. package/dist/lib/services/entity-type.service.d.js +0 -0
  482. package/dist/lib/services/index.d.js +0 -40
  483. package/dist/lib/services/invoice.service.d.js +0 -0
  484. package/dist/lib/services/membership.service.d.js +0 -0
  485. package/dist/lib/services/meta.service.d.js +0 -0
  486. package/dist/lib/services/middleware.service.d.js +0 -0
  487. package/dist/lib/services/namespace.service.d.js +0 -0
  488. package/dist/lib/services/permission.service.d.js +0 -0
  489. package/dist/lib/services/plan.service.d.js +0 -0
  490. package/dist/lib/services/plugin.service.d.js +0 -0
  491. package/dist/lib/services/route-handler.service.d.js +0 -0
  492. package/dist/lib/services/scope.service.d.js +0 -0
  493. package/dist/lib/services/subscription.service.d.js +0 -0
  494. package/dist/lib/services/team-member.service.d.js +0 -0
  495. package/dist/lib/services/team.service.d.js +0 -0
  496. package/dist/lib/services/theme.service.d.js +0 -0
  497. package/dist/lib/services/usage.service.d.js +0 -0
  498. package/dist/lib/services/user-flags.service.d.js +0 -0
  499. package/dist/lib/services/user.service.d.js +0 -0
  500. package/dist/lib/teams/helpers.d.js +0 -0
  501. package/dist/lib/teams/permissions.d.js +0 -0
  502. package/dist/lib/teams/types.d.js +0 -0
  503. package/dist/lib/utils/config-merge.d.js +0 -0
  504. package/dist/lib/utils/file-logger.d.js +0 -0
  505. package/dist/lib/utils.d.js +0 -0
  506. package/dist/styles/classes.json +0 -1093
  507. package/dist/styles/ui.css +0 -2
  508. package/dist/types/auth.d.js +0 -0
  509. package/dist/types/blocks.d.js +0 -0
  510. package/dist/types/meta.types.d.js +0 -0
  511. package/dist/types/plugin.d.js +0 -0
  512. package/dist/types/testing.types.d.js +0 -0
  513. package/dist/types/theme.d.js +0 -0
  514. package/dist/types/user.types.d.js +0 -0
  515. package/scripts/build/build-ui-css.mjs +0 -351
  516. /package/dist/{components/app/layouts/PublicFooter.d.js → lib/auth/security-notifications/types.js} +0 -0
  517. /package/dist/{components/app/layouts/PublicNavbar.d.js → nextspark-registries.d.js} +0 -0
  518. /package/dist/{components/app/misc/ThemeToggle.d.js → types/use-intl-react-shim.d.js} +0 -0
@@ -28,7 +28,7 @@ import {
28
28
  handleCorsPreflightRequest,
29
29
  addCorsHeaders,
30
30
  } from '@nextsparkjs/core/lib/api/helpers'
31
- import { authenticateRequest, hasRequiredScope } from '@nextsparkjs/core/lib/api/auth/dual-auth'
31
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
32
32
  import { z } from 'zod'
33
33
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
34
34
 
@@ -41,8 +41,8 @@ const metadataValueSchema = z.object({
41
41
  })
42
42
 
43
43
  // Handle CORS preflight
44
- export async function OPTIONS() {
45
- return handleCorsPreflightRequest()
44
+ export async function OPTIONS(request: NextRequest) {
45
+ return handleCorsPreflightRequest(request)
46
46
  }
47
47
 
48
48
  /**
@@ -62,44 +62,29 @@ export const GET = withRateLimitTier(withApiLogging(async (
62
62
  { params }: { params: Promise<{ id: string; key: string }> }
63
63
  ): Promise<NextResponse> => {
64
64
  try {
65
- // Authenticate using dual auth
66
- const authResult = await authenticateRequest(req)
65
+ // Authenticate using dual auth; the API-key scope is declared at the entry
66
+ // point, which fails closed for keys that lack it (#93).
67
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:read' })
67
68
 
68
69
  if (!authResult.success || !authResult.user) {
69
- return NextResponse.json(
70
- createApiError('Authentication required', 401, null, 'AUTHENTICATION_FAILED'),
71
- { status: 401 }
72
- )
70
+ return createAuthFailureResponse(authResult)
73
71
  }
74
72
 
75
73
  if (authResult.rateLimitResponse) {
76
74
  return authResult.rateLimitResponse as NextResponse
77
75
  }
78
76
 
79
- // Check required permissions
80
- const hasPermission =
81
- authResult.type === 'session' ||
82
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:read'))
83
-
84
- if (!hasPermission) {
85
- const response = createApiError(
86
- 'Insufficient permissions. Admin access required for user metadata.',
87
- 403
88
- )
89
- return addCorsHeaders(response)
90
- }
91
-
92
77
  const { id, key } = await params
93
78
 
94
79
  // Validate parameters
95
80
  if (!id || id.trim() === '') {
96
81
  const response = createApiError('User ID is required', 400, null, 'MISSING_USER_ID')
97
- return addCorsHeaders(response)
82
+ return addCorsHeaders(response, req)
98
83
  }
99
84
 
100
85
  if (!key || key.trim() === '') {
101
86
  const response = createApiError('Metadata key is required', 400, null, 'MISSING_META_KEY')
102
- return addCorsHeaders(response)
87
+ return addCorsHeaders(response, req)
103
88
  }
104
89
 
105
90
  // Validate key length
@@ -110,7 +95,7 @@ export const GET = withRateLimitTier(withApiLogging(async (
110
95
  null,
111
96
  'INVALID_META_KEY'
112
97
  )
113
- return addCorsHeaders(response)
98
+ return addCorsHeaders(response, req)
114
99
  }
115
100
 
116
101
  // Get user metadata value using UserService
@@ -126,14 +111,14 @@ export const GET = withRateLimitTier(withApiLogging(async (
126
111
  key,
127
112
  value: metaValue ?? null,
128
113
  })
129
- return addCorsHeaders(response)
114
+ return addCorsHeaders(response, req)
130
115
  } catch (error) {
131
116
  const response = createApiError(
132
117
  'Failed to fetch user metadata',
133
118
  500,
134
119
  error instanceof Error ? error.message : undefined
135
120
  )
136
- return addCorsHeaders(response)
121
+ return addCorsHeaders(response, req)
137
122
  }
138
123
  }), 'read');
139
124
 
@@ -159,44 +144,29 @@ export const PUT = withRateLimitTier(withApiLogging(async (
159
144
  { params }: { params: Promise<{ id: string; key: string }> }
160
145
  ): Promise<NextResponse> => {
161
146
  try {
162
- // Authenticate using dual auth
163
- const authResult = await authenticateRequest(req)
147
+ // Authenticate using dual auth; the API-key scope is declared at the entry
148
+ // point, which fails closed for keys that lack it (#93).
149
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:write' })
164
150
 
165
151
  if (!authResult.success || !authResult.user) {
166
- return NextResponse.json(
167
- createApiError('Authentication required', 401, null, 'AUTHENTICATION_FAILED'),
168
- { status: 401 }
169
- )
152
+ return createAuthFailureResponse(authResult)
170
153
  }
171
154
 
172
155
  if (authResult.rateLimitResponse) {
173
156
  return authResult.rateLimitResponse as NextResponse
174
157
  }
175
158
 
176
- // Check required permissions
177
- const hasPermission =
178
- authResult.type === 'session' ||
179
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:write'))
180
-
181
- if (!hasPermission) {
182
- const response = createApiError(
183
- 'Insufficient permissions. Admin access required for user metadata.',
184
- 403
185
- )
186
- return addCorsHeaders(response)
187
- }
188
-
189
159
  const { id, key } = await params
190
160
 
191
161
  // Validate parameters
192
162
  if (!id || id.trim() === '') {
193
163
  const response = createApiError('User ID is required', 400, null, 'MISSING_USER_ID')
194
- return addCorsHeaders(response)
164
+ return addCorsHeaders(response, req)
195
165
  }
196
166
 
197
167
  if (!key || key.trim() === '') {
198
168
  const response = createApiError('Metadata key is required', 400, null, 'MISSING_META_KEY')
199
- return addCorsHeaders(response)
169
+ return addCorsHeaders(response, req)
200
170
  }
201
171
 
202
172
  // Validate key length
@@ -207,7 +177,7 @@ export const PUT = withRateLimitTier(withApiLogging(async (
207
177
  null,
208
178
  'INVALID_META_KEY'
209
179
  )
210
- return addCorsHeaders(response)
180
+ return addCorsHeaders(response, req)
211
181
  }
212
182
 
213
183
  // Parse and validate request body
@@ -223,7 +193,7 @@ export const PUT = withRateLimitTier(withApiLogging(async (
223
193
  null,
224
194
  'VALUE_TOO_LARGE'
225
195
  )
226
- return addCorsHeaders(response)
196
+ return addCorsHeaders(response, req)
227
197
  }
228
198
 
229
199
  // Update metadata using UserService
@@ -245,7 +215,7 @@ export const PUT = withRateLimitTier(withApiLogging(async (
245
215
  value: validatedData.value,
246
216
  updated: true,
247
217
  })
248
- return addCorsHeaders(response)
218
+ return addCorsHeaders(response, req)
249
219
  } catch (error) {
250
220
  // Handle validation errors
251
221
  if (error instanceof z.ZodError) {
@@ -255,7 +225,7 @@ export const PUT = withRateLimitTier(withApiLogging(async (
255
225
  error.issues,
256
226
  'VALIDATION_ERROR'
257
227
  )
258
- return addCorsHeaders(response)
228
+ return addCorsHeaders(response, req)
259
229
  }
260
230
 
261
231
  const response = createApiError(
@@ -263,7 +233,7 @@ export const PUT = withRateLimitTier(withApiLogging(async (
263
233
  500,
264
234
  error instanceof Error ? error.message : undefined
265
235
  )
266
- return addCorsHeaders(response)
236
+ return addCorsHeaders(response, req)
267
237
  }
268
238
  }), 'write');
269
239
 
@@ -287,44 +257,29 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
287
257
  { params }: { params: Promise<{ id: string; key: string }> }
288
258
  ): Promise<NextResponse> => {
289
259
  try {
290
- // Authenticate using dual auth
291
- const authResult = await authenticateRequest(req)
260
+ // Authenticate using dual auth; the API-key scope is declared at the entry
261
+ // point, which fails closed for keys that lack it (#93).
262
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:write' })
292
263
 
293
264
  if (!authResult.success || !authResult.user) {
294
- return NextResponse.json(
295
- createApiError('Authentication required', 401, null, 'AUTHENTICATION_FAILED'),
296
- { status: 401 }
297
- )
265
+ return createAuthFailureResponse(authResult)
298
266
  }
299
267
 
300
268
  if (authResult.rateLimitResponse) {
301
269
  return authResult.rateLimitResponse as NextResponse
302
270
  }
303
271
 
304
- // Check required permissions
305
- const hasPermission =
306
- authResult.type === 'session' ||
307
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:write'))
308
-
309
- if (!hasPermission) {
310
- const response = createApiError(
311
- 'Insufficient permissions. Admin access required for user metadata.',
312
- 403
313
- )
314
- return addCorsHeaders(response)
315
- }
316
-
317
272
  const { id, key } = await params
318
273
 
319
274
  // Validate parameters
320
275
  if (!id || id.trim() === '') {
321
276
  const response = createApiError('User ID is required', 400, null, 'MISSING_USER_ID')
322
- return addCorsHeaders(response)
277
+ return addCorsHeaders(response, req)
323
278
  }
324
279
 
325
280
  if (!key || key.trim() === '') {
326
281
  const response = createApiError('Metadata key is required', 400, null, 'MISSING_META_KEY')
327
- return addCorsHeaders(response)
282
+ return addCorsHeaders(response, req)
328
283
  }
329
284
 
330
285
  // Validate key length
@@ -335,7 +290,7 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
335
290
  null,
336
291
  'INVALID_META_KEY'
337
292
  )
338
- return addCorsHeaders(response)
293
+ return addCorsHeaders(response, req)
339
294
  }
340
295
 
341
296
  // Delete metadata using UserService
@@ -346,13 +301,13 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
346
301
  key,
347
302
  deleted: true,
348
303
  })
349
- return addCorsHeaders(response)
304
+ return addCorsHeaders(response, req)
350
305
  } catch (error) {
351
306
  const response = createApiError(
352
307
  'Failed to delete user metadata',
353
308
  500,
354
309
  error instanceof Error ? error.message : undefined
355
310
  )
356
- return addCorsHeaders(response)
311
+ return addCorsHeaders(response, req)
357
312
  }
358
313
  }), 'write');
@@ -11,7 +11,7 @@ import {
11
11
  handleEntityMetadataInResponse,
12
12
  processEntityMetadata
13
13
  } from '@nextsparkjs/core/lib/api/helpers';
14
- import { authenticateRequest, hasRequiredScope } from '@nextsparkjs/core/lib/api/auth/dual-auth';
14
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth';
15
15
  import { z } from 'zod';
16
16
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit';
17
17
 
@@ -24,8 +24,8 @@ const updateUserSchema = z.object({
24
24
  });
25
25
 
26
26
  // Handle CORS preflight
27
- export async function OPTIONS() {
28
- return handleCorsPreflightRequest();
27
+ export async function OPTIONS(request: NextRequest) {
28
+ return handleCorsPreflightRequest(request);
29
29
  }
30
30
 
31
31
  // GET /api/v1/users/:id - Get specific user
@@ -34,35 +34,24 @@ export const GET = withRateLimitTier(withApiLogging(async (
34
34
  { params }: { params: Promise<{ id: string }> }
35
35
  ): Promise<NextResponse> => {
36
36
  try {
37
- // Authenticate using dual auth
38
- const authResult = await authenticateRequest(req);
39
-
37
+ // Authenticate using dual auth; the API-key scope is declared at the entry
38
+ // point, which fails closed for keys that lack it (#93).
39
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:read' });
40
+
40
41
  if (!authResult.success) {
41
- return NextResponse.json(
42
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
43
- { status: 401 }
44
- );
42
+ return createAuthFailureResponse(authResult);
45
43
  }
46
44
 
47
45
  if (authResult.rateLimitResponse) {
48
46
  return authResult.rateLimitResponse as NextResponse;
49
47
  }
50
48
 
51
- // Check required permissions - session users need admin role, API key users need specific scope
52
- const hasPermission = authResult.type === 'session' ||
53
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:read'));
54
-
55
- if (!hasPermission) {
56
- const response = createApiError('Insufficient permissions. Admin access required for user management.', 403);
57
- return addCorsHeaders(response);
58
- }
59
-
60
49
  const { id } = await params;
61
50
 
62
51
  // Validate that id is not empty
63
52
  if (!id || id.trim() === '') {
64
53
  const response = createApiError('User ID or email is required', 400, null, 'MISSING_IDENTIFIER');
65
- return addCorsHeaders(response);
54
+ return addCorsHeaders(response, req);
66
55
  }
67
56
 
68
57
  // Search by ID or email
@@ -74,7 +63,7 @@ export const GET = withRateLimitTier(withApiLogging(async (
74
63
 
75
64
  if (!user) {
76
65
  const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
77
- return addCorsHeaders(response);
66
+ return addCorsHeaders(response, req);
78
67
  }
79
68
 
80
69
  // Handle metadata if requested (usando helper compartido)
@@ -82,11 +71,11 @@ export const GET = withRateLimitTier(withApiLogging(async (
82
71
  const userWithMeta = await includeEntityMetadataForSingle('user', user as { id: string }, metaParams, authResult.user!.id);
83
72
 
84
73
  const response = createApiResponse(userWithMeta);
85
- return addCorsHeaders(response);
74
+ return addCorsHeaders(response, req);
86
75
  } catch (error) {
87
76
  console.error('Error fetching user:', error);
88
77
  const response = createApiError('Internal server error', 500);
89
- return addCorsHeaders(response);
78
+ return addCorsHeaders(response, req);
90
79
  }
91
80
  }), 'read');
92
81
 
@@ -96,35 +85,24 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
96
85
  { params }: { params: Promise<{ id: string }> }
97
86
  ): Promise<NextResponse> => {
98
87
  try {
99
- // Authenticate using dual auth
100
- const authResult = await authenticateRequest(req);
101
-
88
+ // Authenticate using dual auth; the API-key scope is declared at the entry
89
+ // point, which fails closed for keys that lack it (#93).
90
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:write' });
91
+
102
92
  if (!authResult.success) {
103
- return NextResponse.json(
104
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
105
- { status: 401 }
106
- );
93
+ return createAuthFailureResponse(authResult);
107
94
  }
108
95
 
109
96
  if (authResult.rateLimitResponse) {
110
97
  return authResult.rateLimitResponse as NextResponse;
111
98
  }
112
99
 
113
- // Check required permissions - session users need admin role, API key users need specific scope
114
- const hasPermission = authResult.type === 'session' ||
115
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:write'));
116
-
117
- if (!hasPermission) {
118
- const response = createApiError('Insufficient permissions. Admin access required for user management.', 403);
119
- return addCorsHeaders(response);
120
- }
121
-
122
100
  const { id } = await params;
123
101
 
124
102
  // Validate that id is not empty
125
103
  if (!id || id.trim() === '') {
126
104
  const response = createApiError('User ID or email is required', 400, null, 'MISSING_IDENTIFIER');
127
- return addCorsHeaders(response);
105
+ return addCorsHeaders(response, req);
128
106
  }
129
107
 
130
108
  const body = await req.json();
@@ -162,7 +140,7 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
162
140
 
163
141
  if (!hasEntityFieldsToUpdate && !hasMetadataToUpdate) {
164
142
  const response = createApiError('No fields to update', 400, null, 'NO_FIELDS');
165
- return addCorsHeaders(response);
143
+ return addCorsHeaders(response, req);
166
144
  }
167
145
 
168
146
  let updatedUser;
@@ -183,7 +161,7 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
183
161
 
184
162
  if (result.rows.length === 0) {
185
163
  const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
186
- return addCorsHeaders(response);
164
+ return addCorsHeaders(response, req);
187
165
  }
188
166
 
189
167
  updatedUser = result.rows[0];
@@ -197,7 +175,7 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
197
175
 
198
176
  if (!user) {
199
177
  const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
200
- return addCorsHeaders(response);
178
+ return addCorsHeaders(response, req);
201
179
  }
202
180
 
203
181
  updatedUser = user;
@@ -214,16 +192,16 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
214
192
  const responseData = await handleEntityMetadataInResponse('user', updatedUser as { id: string }, metadataWasProvided, authResult.user!.id);
215
193
 
216
194
  const response = createApiResponse(responseData);
217
- return addCorsHeaders(response);
195
+ return addCorsHeaders(response, req);
218
196
  } catch (error) {
219
197
  if (error instanceof z.ZodError) {
220
198
  const response = createApiError('Validation error', 400, error.issues, 'VALIDATION_ERROR');
221
- return addCorsHeaders(response);
199
+ return addCorsHeaders(response, req);
222
200
  }
223
201
 
224
202
  console.error('Error updating user:', error);
225
203
  const response = createApiError('Internal server error', 500);
226
- return addCorsHeaders(response);
204
+ return addCorsHeaders(response, req);
227
205
  }
228
206
  }), 'write');
229
207
 
@@ -233,35 +211,24 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
233
211
  { params }: { params: Promise<{ id: string }> }
234
212
  ): Promise<NextResponse> => {
235
213
  try {
236
- // Authenticate using dual auth
237
- const authResult = await authenticateRequest(req);
238
-
214
+ // Authenticate using dual auth; the API-key scope is declared at the entry
215
+ // point, which fails closed for keys that lack it (#93).
216
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:delete' });
217
+
239
218
  if (!authResult.success) {
240
- return NextResponse.json(
241
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
242
- { status: 401 }
243
- );
219
+ return createAuthFailureResponse(authResult);
244
220
  }
245
221
 
246
222
  if (authResult.rateLimitResponse) {
247
223
  return authResult.rateLimitResponse as NextResponse;
248
224
  }
249
225
 
250
- // Check required permissions - session users need admin role, API key users need specific scope
251
- const hasPermission = authResult.type === 'session' ||
252
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:delete'));
253
-
254
- if (!hasPermission) {
255
- const response = createApiError('Insufficient permissions. Admin access required for user management.', 403);
256
- return addCorsHeaders(response);
257
- }
258
-
259
226
  const { id } = await params;
260
227
 
261
228
  // Validate that id is not empty
262
229
  if (!id || id.trim() === '') {
263
230
  const response = createApiError('User ID or email is required', 400, null, 'MISSING_IDENTIFIER');
264
- return addCorsHeaders(response);
231
+ return addCorsHeaders(response, req);
265
232
  }
266
233
 
267
234
  // First, get the user to check if it exists and prevent self-deletion
@@ -273,13 +240,13 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
273
240
 
274
241
  if (!targetUser) {
275
242
  const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
276
- return addCorsHeaders(response);
243
+ return addCorsHeaders(response, req);
277
244
  }
278
245
 
279
246
  // Prevent self-deletion
280
247
  if ((targetUser as Record<string, unknown>).id === authResult.user!.id) {
281
248
  const response = createApiError('Cannot delete your own account via API', 403, null, 'SELF_DELETE_FORBIDDEN');
282
- return addCorsHeaders(response);
249
+ return addCorsHeaders(response, req);
283
250
  }
284
251
 
285
252
  const result = await mutateWithRLS(
@@ -290,14 +257,14 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
290
257
 
291
258
  if (result.rows.length === 0) {
292
259
  const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
293
- return addCorsHeaders(response);
260
+ return addCorsHeaders(response, req);
294
261
  }
295
262
 
296
263
  const response = createApiResponse({ deleted: true, id });
297
- return addCorsHeaders(response);
264
+ return addCorsHeaders(response, req);
298
265
  } catch (error) {
299
266
  console.error('Error deleting user:', error);
300
267
  const response = createApiError('Internal server error', 500);
301
- return addCorsHeaders(response);
268
+ return addCorsHeaders(response, req);
302
269
  }
303
270
  }), 'write');
@@ -14,7 +14,7 @@ import {
14
14
  handleEntityMetadataInResponse,
15
15
  processEntityMetadata
16
16
  } from '@nextsparkjs/core/lib/api/helpers';
17
- import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth';
17
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth';
18
18
  import { hasAdminPermission } from '@nextsparkjs/core/lib/api/auth/permissions';
19
19
  import { z } from 'zod';
20
20
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit';
@@ -34,21 +34,19 @@ const createUserSchema = z.object({
34
34
 
35
35
 
36
36
  // Handle CORS preflight
37
- export async function OPTIONS() {
38
- return handleCorsPreflightRequest();
37
+ export async function OPTIONS(request: NextRequest) {
38
+ return handleCorsPreflightRequest(request);
39
39
  }
40
40
 
41
41
  // GET /api/v1/users - List users with dual auth
42
42
  export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
43
43
  try {
44
- // Authenticate using dual auth
45
- const authResult = await authenticateRequest(req);
46
-
44
+ // Authenticate using dual auth; the API-key scope is declared at the entry
45
+ // point, which fails closed for keys that lack it (#93).
46
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:read' });
47
+
47
48
  if (!authResult.success) {
48
- return NextResponse.json(
49
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
50
- { status: 401 }
51
- );
49
+ return createAuthFailureResponse(authResult);
52
50
  }
53
51
 
54
52
  if (authResult.rateLimitResponse) {
@@ -58,7 +56,7 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
58
56
  // SECURITY: Only superadmins can list all users
59
57
  if (!hasAdminPermission(authResult, 'users:read')) {
60
58
  const response = createApiError('Insufficient permissions. Superadmin access required.', 403);
61
- return addCorsHeaders(response);
59
+ return addCorsHeaders(response, req);
62
60
  }
63
61
 
64
62
  const metaParams = parseMetaParams(req);
@@ -103,25 +101,23 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
103
101
  const usersWithMeta = await includeEntityMetadata('user', users as { id: string }[], metaParams, authResult.user!.id);
104
102
 
105
103
  const response = createApiResponse(usersWithMeta, paginationMeta);
106
- return addCorsHeaders(response);
104
+ return addCorsHeaders(response, req);
107
105
  } catch (error) {
108
106
  console.error('Error fetching users:', error);
109
107
  const response = createApiError('Internal server error', 500);
110
- return addCorsHeaders(response);
108
+ return addCorsHeaders(response, req);
111
109
  }
112
110
  }), 'read');
113
111
 
114
112
  // POST /api/v1/users - Create user with dual auth
115
113
  export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
116
114
  try {
117
- // Authenticate using dual auth
118
- const authResult = await authenticateRequest(req);
119
-
115
+ // Authenticate using dual auth; the API-key scope is declared at the entry
116
+ // point, which fails closed for keys that lack it (#93).
117
+ const authResult = await authenticateRequest(req, { requiredScope: 'users:write' });
118
+
120
119
  if (!authResult.success) {
121
- return NextResponse.json(
122
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
123
- { status: 401 }
124
- );
120
+ return createAuthFailureResponse(authResult);
125
121
  }
126
122
 
127
123
  if (authResult.rateLimitResponse) {
@@ -131,7 +127,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
131
127
  // SECURITY: Only superadmins can create users
132
128
  if (!hasAdminPermission(authResult, 'users:write')) {
133
129
  const response = createApiError('Insufficient permissions. Superadmin access required.', 403);
134
- return addCorsHeaders(response);
130
+ return addCorsHeaders(response, req);
135
131
  }
136
132
 
137
133
  const body = await req.json();
@@ -147,7 +143,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
147
143
 
148
144
  if (existingUser.length > 0) {
149
145
  const response = createApiError('Email already exists', 409, null, 'EMAIL_EXISTS');
150
- return addCorsHeaders(response);
146
+ return addCorsHeaders(response, req);
151
147
  }
152
148
 
153
149
  const newUserId = globalThis.crypto.randomUUID();
@@ -183,16 +179,16 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
183
179
  const responseData = await handleEntityMetadataInResponse('user', createdUser as { id: string }, metadataWasProvided, authResult.user!.id);
184
180
 
185
181
  const response = createApiResponse(responseData, { created: true }, 201);
186
- return addCorsHeaders(response);
182
+ return addCorsHeaders(response, req);
187
183
  } catch (error) {
188
184
  if (error instanceof z.ZodError) {
189
185
  const response = createApiError('Validation error', 400, error.issues, 'VALIDATION_ERROR');
190
- return addCorsHeaders(response);
186
+ return addCorsHeaders(response, req);
191
187
  }
192
188
 
193
189
  console.error('Error creating user:', error);
194
190
  const response = createApiError('Internal server error', 500);
195
- return addCorsHeaders(response);
191
+ return addCorsHeaders(response, req);
196
192
  }
197
193
  }), 'write');
198
194
 
@@ -3,7 +3,7 @@
3
3
  import { useAuth } from '@nextsparkjs/core/hooks/useAuth'
4
4
  import { useRouter } from 'next/navigation'
5
5
  import { useEffect, Suspense } from 'react'
6
- import { Loader2 } from 'lucide-react'
6
+ import { DashboardAuthSkeleton } from '@nextsparkjs/core/components/dashboard/layouts/DashboardAuthSkeleton'
7
7
  import { DashboardProviders } from '@nextsparkjs/core/providers/DashboardProviders'
8
8
  import { DashboardTranslationPreloader } from '@nextsparkjs/core/lib/i18n/DashboardTranslationPreloader'
9
9
  import { TranslationDebugger } from '@nextsparkjs/core/utils/dev/TranslationDebugger'
@@ -35,11 +35,7 @@ function DashboardLayoutContent({ children }: { children: React.ReactNode }) {
35
35
  }, [user, isLoading, router])
36
36
 
37
37
  if (isLoading) {
38
- return (
39
- <div className="min-h-screen flex items-center justify-center">
40
- <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
41
- </div>
42
- )
38
+ return <DashboardAuthSkeleton />
43
39
  }
44
40
 
45
41
  if (!user) {