@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
@@ -20,12 +20,13 @@ import {
20
20
  handleCorsPreflightRequest,
21
21
  addCorsHeaders
22
22
  } from '@nextsparkjs/core/lib/api/helpers';
23
- import { authenticateRequest, hasRequiredScope } from '@nextsparkjs/core/lib/api/auth/dual-auth';
23
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth';
24
+ import { invalidateApiKeyCache } from '@nextsparkjs/core/lib/api/auth';
24
25
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit';
25
26
 
26
27
  // Handle CORS preflight
27
- export async function OPTIONS() {
28
- return handleCorsPreflightRequest();
28
+ export async function OPTIONS(request: NextRequest) {
29
+ return handleCorsPreflightRequest(request);
29
30
  }
30
31
 
31
32
  // GET /api/v1/api-keys/:id - Get specific API key details
@@ -34,34 +35,23 @@ export const GET = withRateLimitTier(withApiLogging(async (
34
35
  { params }: { params: Promise<{ id: string }> }
35
36
  ): Promise<NextResponse> => {
36
37
  try {
37
- // Authenticate using dual auth
38
- const authResult = await authenticateRequest(req);
39
-
38
+ // Authenticate using dual auth; the API-key scope is declared at the entry
39
+ // point, which fails closed for keys that lack it (#93).
40
+ const authResult = await authenticateRequest(req, { requiredScope: 'admin:api-keys' });
41
+
40
42
  if (!authResult.success) {
41
- return NextResponse.json(
42
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
43
- { status: 401 }
44
- );
43
+ return createAuthFailureResponse(authResult);
45
44
  }
46
45
 
47
46
  if (authResult.rateLimitResponse) {
48
47
  return authResult.rateLimitResponse as NextResponse;
49
48
  }
50
49
 
51
- // Check required permissions - session users have admin access, API key users need specific scope
52
- const hasPermission = authResult.type === 'session' ||
53
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'admin:api-keys'));
54
-
55
- if (!hasPermission) {
56
- const response = createApiError('Insufficient permissions. Admin access required for API key management.', 403);
57
- return addCorsHeaders(response);
58
- }
59
-
60
50
  const { id } = await params;
61
51
 
62
52
  if (!id || id.trim() === '') {
63
53
  const response = createApiError('API key ID is required', 400, null, 'MISSING_API_KEY_ID');
64
- return addCorsHeaders(response);
54
+ return addCorsHeaders(response, req);
65
55
  }
66
56
 
67
57
  const apiKey = await queryOneWithRLS(
@@ -74,7 +64,7 @@ export const GET = withRateLimitTier(withApiLogging(async (
74
64
 
75
65
  if (!apiKey) {
76
66
  const response = createApiError('API key not found', 404, null, 'API_KEY_NOT_FOUND');
77
- return addCorsHeaders(response);
67
+ return addCorsHeaders(response, req);
78
68
  }
79
69
 
80
70
  // Get detailed usage statistics
@@ -129,11 +119,11 @@ export const GET = withRateLimitTier(withApiLogging(async (
129
119
  success_rate: null
130
120
  }
131
121
  });
132
- return addCorsHeaders(response);
122
+ return addCorsHeaders(response, req);
133
123
  } catch (error) {
134
124
  console.error('Error fetching API key:', error);
135
125
  const response = createApiError('Internal server error', 500);
136
- return addCorsHeaders(response);
126
+ return addCorsHeaders(response, req);
137
127
  }
138
128
  }), 'strict');
139
129
 
@@ -143,34 +133,23 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
143
133
  { params }: { params: Promise<{ id: string }> }
144
134
  ): Promise<NextResponse> => {
145
135
  try {
146
- // Authenticate using dual auth
147
- const authResult = await authenticateRequest(req);
148
-
136
+ // Authenticate using dual auth; the API-key scope is declared at the entry
137
+ // point, which fails closed for keys that lack it (#93).
138
+ const authResult = await authenticateRequest(req, { requiredScope: 'admin:api-keys' });
139
+
149
140
  if (!authResult.success) {
150
- return NextResponse.json(
151
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
152
- { status: 401 }
153
- );
141
+ return createAuthFailureResponse(authResult);
154
142
  }
155
143
 
156
144
  if (authResult.rateLimitResponse) {
157
145
  return authResult.rateLimitResponse as NextResponse;
158
146
  }
159
147
 
160
- // Check required permissions - session users have admin access, API key users need specific scope
161
- const hasPermission = authResult.type === 'session' ||
162
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'admin:api-keys'));
163
-
164
- if (!hasPermission) {
165
- const response = createApiError('Insufficient permissions. Admin access required for API key management.', 403);
166
- return addCorsHeaders(response);
167
- }
168
-
169
148
  const { id } = await params;
170
149
 
171
150
  if (!id || id.trim() === '') {
172
151
  const response = createApiError('API key ID is required', 400, null, 'MISSING_API_KEY_ID');
173
- return addCorsHeaders(response);
152
+ return addCorsHeaders(response, req);
174
153
  }
175
154
 
176
155
  const body = await req.json();
@@ -185,11 +164,11 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
185
164
  if (body[field] !== undefined) {
186
165
  if (field === 'name' && typeof body[field] !== 'string') {
187
166
  const response = createApiError('Name must be a string', 400, null, 'INVALID_NAME_TYPE');
188
- return addCorsHeaders(response);
167
+ return addCorsHeaders(response, req);
189
168
  }
190
169
  if (field === 'status' && !['active', 'inactive', 'expired'].includes(body[field])) {
191
170
  const response = createApiError('status must be active, inactive, or expired', 400, null, 'INVALID_STATUS_TYPE');
192
- return addCorsHeaders(response);
171
+ return addCorsHeaders(response, req);
193
172
  }
194
173
 
195
174
  updates.push(`"${field}" = $${paramCount++}`);
@@ -199,7 +178,7 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
199
178
 
200
179
  if (updates.length === 0) {
201
180
  const response = createApiError('No valid fields to update', 400, null, 'NO_FIELDS');
202
- return addCorsHeaders(response);
181
+ return addCorsHeaders(response, req);
203
182
  }
204
183
 
205
184
  updates.push(`"updatedAt" = CURRENT_TIMESTAMP`);
@@ -209,22 +188,28 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
209
188
  UPDATE "api_key"
210
189
  SET ${updates.join(", ")}
211
190
  WHERE id = $${paramCount} AND "userId" = $${paramCount + 1}
212
- RETURNING id, "keyPrefix", name, scopes, status, "lastUsedAt", "expiresAt", "createdAt", "updatedAt"
191
+ RETURNING id, "keyPrefix", "keyHash", name, scopes, status, "lastUsedAt", "expiresAt", "createdAt", "updatedAt"
213
192
  `;
214
193
 
215
194
  const result = await mutateWithRLS(query, values, authResult.user!.id);
216
195
 
217
196
  if (result.rows.length === 0) {
218
197
  const response = createApiError('API key not found', 404, null, 'API_KEY_NOT_FOUND');
219
- return addCorsHeaders(response);
198
+ return addCorsHeaders(response, req);
220
199
  }
221
200
 
222
- const response = createApiResponse(result.rows[0]);
223
- return addCorsHeaders(response);
201
+ // A status change must take effect immediately: drop the cached validation
202
+ // entry so the next request re-reads the row (see #92). keyHash is only
203
+ // used for the invalidation and never returned to the client.
204
+ const { keyHash, ...updatedApiKey } = result.rows[0] as { keyHash: string } & Record<string, unknown>;
205
+ invalidateApiKeyCache(keyHash);
206
+
207
+ const response = createApiResponse(updatedApiKey);
208
+ return addCorsHeaders(response, req);
224
209
  } catch (error) {
225
210
  console.error('Error updating API key:', error);
226
211
  const response = createApiError('Internal server error', 500);
227
- return addCorsHeaders(response);
212
+ return addCorsHeaders(response, req);
228
213
  }
229
214
  }), 'strict');
230
215
 
@@ -234,34 +219,23 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
234
219
  { params }: { params: Promise<{ id: string }> }
235
220
  ): Promise<NextResponse> => {
236
221
  try {
237
- // Authenticate using dual auth
238
- const authResult = await authenticateRequest(req);
239
-
222
+ // Authenticate using dual auth; the API-key scope is declared at the entry
223
+ // point, which fails closed for keys that lack it (#93).
224
+ const authResult = await authenticateRequest(req, { requiredScope: 'admin:api-keys' });
225
+
240
226
  if (!authResult.success) {
241
- return NextResponse.json(
242
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
243
- { status: 401 }
244
- );
227
+ return createAuthFailureResponse(authResult);
245
228
  }
246
229
 
247
230
  if (authResult.rateLimitResponse) {
248
231
  return authResult.rateLimitResponse as NextResponse;
249
232
  }
250
233
 
251
- // Check required permissions - session users have admin access, API key users need specific scope
252
- const hasPermission = authResult.type === 'session' ||
253
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'admin:api-keys'));
254
-
255
- if (!hasPermission) {
256
- const response = createApiError('Insufficient permissions. Admin access required for API key management.', 403);
257
- return addCorsHeaders(response);
258
- }
259
-
260
234
  const { id } = await params;
261
235
 
262
236
  if (!id || id.trim() === '') {
263
237
  const response = createApiError('API key ID is required', 400, null, 'MISSING_API_KEY_ID');
264
- return addCorsHeaders(response);
238
+ return addCorsHeaders(response, req);
265
239
  }
266
240
 
267
241
  // For API key auth, prevent deletion of the current API key being used
@@ -272,7 +246,7 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
272
246
  null,
273
247
  'SELF_REVOKE_FORBIDDEN'
274
248
  );
275
- return addCorsHeaders(response);
249
+ return addCorsHeaders(response, req);
276
250
  }
277
251
 
278
252
  // Soft delete - just mark as inactive
@@ -280,25 +254,30 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
280
254
  `UPDATE "api_key"
281
255
  SET status = 'inactive', "updatedAt" = CURRENT_TIMESTAMP
282
256
  WHERE id = $1 AND "userId" = $2
283
- RETURNING id, name`,
257
+ RETURNING id, name, "keyHash"`,
284
258
  [id, authResult.user!.id],
285
259
  authResult.user!.id
286
260
  );
287
261
 
288
262
  if (result.rows.length === 0) {
289
263
  const response = createApiError('API key not found', 404, null, 'API_KEY_NOT_FOUND');
290
- return addCorsHeaders(response);
264
+ return addCorsHeaders(response, req);
291
265
  }
292
266
 
267
+ const revokedRow = result.rows[0] as { name: string; keyHash: string };
268
+
269
+ // Revocation must be effective right away, not after the 5 min cache TTL (see #92)
270
+ invalidateApiKeyCache(revokedRow.keyHash);
271
+
293
272
  const response = createApiResponse({
294
273
  revoked: true,
295
274
  id,
296
- name: (result.rows[0] as { name: string }).name
275
+ name: revokedRow.name
297
276
  });
298
- return addCorsHeaders(response);
277
+ return addCorsHeaders(response, req);
299
278
  } catch (error) {
300
279
  console.error('Error revoking API key:', error);
301
280
  const response = createApiError('Internal server error', 500);
302
- return addCorsHeaders(response);
281
+ return addCorsHeaders(response, req);
303
282
  }
304
283
  }), 'strict');
@@ -7,7 +7,7 @@ import {
7
7
  handleCorsPreflightRequest,
8
8
  addCorsHeaders
9
9
  } from '@nextsparkjs/core/lib/api/helpers';
10
- import { authenticateRequest, hasRequiredScope, resolveTeamContext } from '@nextsparkjs/core/lib/api/auth/dual-auth';
10
+ import { authenticateRequest, createAuthFailureResponse, resolveTeamContext } from '@nextsparkjs/core/lib/api/auth/dual-auth';
11
11
  import { ApiKeyManager, API_SCOPES, API_KEY_LIMITS } from '@nextsparkjs/core/lib/api/keys';
12
12
  import { validateScopesForUser } from '@nextsparkjs/core/lib/api/auth';
13
13
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit';
@@ -20,36 +20,25 @@ const createApiKeySchema = z.object({
20
20
  });
21
21
 
22
22
  // Handle CORS preflight
23
- export async function OPTIONS() {
24
- return handleCorsPreflightRequest();
23
+ export async function OPTIONS(request: NextRequest) {
24
+ return handleCorsPreflightRequest(request);
25
25
  }
26
26
 
27
27
  // GET /api/v1/api-keys - List user's API keys with dual auth
28
28
  export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
29
29
  try {
30
- // Authenticate using dual auth
31
- const authResult = await authenticateRequest(req);
32
-
30
+ // Authenticate using dual auth. Session users have admin access; API keys
31
+ // need the scope declared here, enforced at the entry point (#93).
32
+ const authResult = await authenticateRequest(req, { requiredScope: 'admin:api-keys' });
33
+
33
34
  if (!authResult.success) {
34
- return NextResponse.json(
35
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
36
- { status: 401 }
37
- );
35
+ return createAuthFailureResponse(authResult);
38
36
  }
39
37
 
40
38
  if (authResult.rateLimitResponse) {
41
39
  return authResult.rateLimitResponse as NextResponse;
42
40
  }
43
41
 
44
- // Check required permissions - session users have admin access, API key users need specific scope
45
- const hasPermission = authResult.type === 'session' ||
46
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'admin:api-keys'));
47
-
48
- if (!hasPermission) {
49
- const response = createApiError('Insufficient permissions. Admin access required for API key management.', 403);
50
- return addCorsHeaders(response);
51
- }
52
-
53
42
  const apiKeys = await queryWithRLS<{
54
43
  id: string;
55
44
  keyPrefix: string;
@@ -125,40 +114,29 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
125
114
  }));
126
115
 
127
116
  const response = createApiResponse(keysWithStats);
128
- return addCorsHeaders(response);
117
+ return addCorsHeaders(response, req);
129
118
  } catch (error) {
130
119
  console.error('Error fetching API keys:', error);
131
120
  const response = createApiError('Internal server error', 500);
132
- return addCorsHeaders(response);
121
+ return addCorsHeaders(response, req);
133
122
  }
134
123
  }), 'strict');
135
124
 
136
125
  // POST /api/v1/api-keys - Create new API key with dual auth
137
126
  export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
138
127
  try {
139
- // Authenticate using dual auth
140
- const authResult = await authenticateRequest(req);
141
-
128
+ // Authenticate using dual auth. Session users have admin access; API keys
129
+ // need the scope declared here, enforced at the entry point (#93).
130
+ const authResult = await authenticateRequest(req, { requiredScope: 'admin:api-keys' });
131
+
142
132
  if (!authResult.success) {
143
- return NextResponse.json(
144
- { success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
145
- { status: 401 }
146
- );
133
+ return createAuthFailureResponse(authResult);
147
134
  }
148
135
 
149
136
  if (authResult.rateLimitResponse) {
150
137
  return authResult.rateLimitResponse as NextResponse;
151
138
  }
152
139
 
153
- // Check required permissions - session users have admin access, API key users need specific scope
154
- const hasPermission = authResult.type === 'session' ||
155
- (authResult.type === 'api-key' && hasRequiredScope(authResult, 'admin:api-keys'));
156
-
157
- if (!hasPermission) {
158
- const response = createApiError('Insufficient permissions. Admin access required for API key management.', 403);
159
- return addCorsHeaders(response);
160
- }
161
-
162
140
  const body = await req.json();
163
141
  const validatedData = createApiKeySchema.parse(body);
164
142
 
@@ -174,7 +152,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
174
152
  },
175
153
  'INVALID_SCOPES'
176
154
  );
177
- return addCorsHeaders(response);
155
+ return addCorsHeaders(response, req);
178
156
  }
179
157
 
180
158
  // Validar que el usuario pueda crear API keys con estos scopes.
@@ -201,7 +179,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
201
179
  },
202
180
  'INSUFFICIENT_SCOPE_PERMISSIONS'
203
181
  );
204
- return addCorsHeaders(response);
182
+ return addCorsHeaders(response, req);
205
183
  }
206
184
 
207
185
  // Verificar límite de API keys por usuario (máximo 10)
@@ -218,7 +196,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
218
196
  null,
219
197
  'API_KEY_LIMIT_REACHED'
220
198
  );
221
- return addCorsHeaders(response);
199
+ return addCorsHeaders(response, req);
222
200
  }
223
201
 
224
202
  // Generar API key
@@ -249,15 +227,15 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
249
227
  };
250
228
 
251
229
  const response = createApiResponse(responseData, { created: true }, 201);
252
- return addCorsHeaders(response);
230
+ return addCorsHeaders(response, req);
253
231
  } catch (error) {
254
232
  if (error instanceof z.ZodError) {
255
233
  const response = createApiError('Validation error', 400, error.issues, 'VALIDATION_ERROR');
256
- return addCorsHeaders(response);
234
+ return addCorsHeaders(response, req);
257
235
  }
258
236
 
259
237
  console.error('Error creating API key:', error);
260
238
  const response = createApiError('Internal server error', 500);
261
- return addCorsHeaders(response);
239
+ return addCorsHeaders(response, req);
262
240
  }
263
241
  }), 'strict');
@@ -14,8 +14,8 @@ import { withSignupContext } from '@nextsparkjs/core/lib/auth-context'
14
14
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
15
15
 
16
16
  // Handle CORS preflight
17
- export async function OPTIONS() {
18
- return handleCorsPreflightRequest()
17
+ export async function OPTIONS(request: NextRequest) {
18
+ return handleCorsPreflightRequest(request)
19
19
  }
20
20
 
21
21
  interface SignupWithInviteBody {
@@ -41,14 +41,14 @@ export const POST = withRateLimitTier(withApiLogging(
41
41
  null,
42
42
  'MISSING_FIELDS'
43
43
  )
44
- return addCorsHeaders(response)
44
+ return addCorsHeaders(response, req)
45
45
  }
46
46
 
47
47
  // Validate email format
48
48
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
49
49
  if (!emailRegex.test(email)) {
50
50
  const response = createApiError('Invalid email format', 400, null, 'INVALID_EMAIL')
51
- return addCorsHeaders(response)
51
+ return addCorsHeaders(response, req)
52
52
  }
53
53
 
54
54
  // Validate password length (min 8 characters as per Better Auth config)
@@ -59,7 +59,7 @@ export const POST = withRateLimitTier(withApiLogging(
59
59
  null,
60
60
  'INVALID_PASSWORD'
61
61
  )
62
- return addCorsHeaders(response)
62
+ return addCorsHeaders(response, req)
63
63
  }
64
64
 
65
65
  // Step 1: Validate the invitation token via the service pool (RLS bypass).
@@ -76,7 +76,7 @@ export const POST = withRateLimitTier(withApiLogging(
76
76
 
77
77
  if (!invitation) {
78
78
  const response = createApiError('Invitation not found', 404, null, 'INVITATION_NOT_FOUND')
79
- return addCorsHeaders(response)
79
+ return addCorsHeaders(response, req)
80
80
  }
81
81
 
82
82
  // Verify invitation is for the correct email
@@ -87,7 +87,7 @@ export const POST = withRateLimitTier(withApiLogging(
87
87
  null,
88
88
  'EMAIL_MISMATCH'
89
89
  )
90
- return addCorsHeaders(response)
90
+ return addCorsHeaders(response, req)
91
91
  }
92
92
 
93
93
  // Check if invitation is pending
@@ -98,14 +98,14 @@ export const POST = withRateLimitTier(withApiLogging(
98
98
  null,
99
99
  'INVITATION_NOT_PENDING'
100
100
  )
101
- return addCorsHeaders(response)
101
+ return addCorsHeaders(response, req)
102
102
  }
103
103
 
104
104
  // Check if invitation has expired
105
105
  const expiresAt = new Date(invitation.expiresAt)
106
106
  if (expiresAt < new Date()) {
107
107
  const response = createApiError('Invitation has expired', 410, null, 'INVITATION_EXPIRED')
108
- return addCorsHeaders(response)
108
+ return addCorsHeaders(response, req)
109
109
  }
110
110
 
111
111
  // Step 2: Create user using Better Auth's internal API
@@ -144,7 +144,7 @@ export const POST = withRateLimitTier(withApiLogging(
144
144
  null,
145
145
  'USER_ALREADY_EXISTS'
146
146
  )
147
- return addCorsHeaders(response)
147
+ return addCorsHeaders(response, req)
148
148
  }
149
149
 
150
150
  const response = createApiError(
@@ -153,7 +153,7 @@ export const POST = withRateLimitTier(withApiLogging(
153
153
  null,
154
154
  'SIGNUP_FAILED'
155
155
  )
156
- return addCorsHeaders(response)
156
+ return addCorsHeaders(response, req)
157
157
  }
158
158
 
159
159
  // Parse response to get user data
@@ -167,7 +167,7 @@ export const POST = withRateLimitTier(withApiLogging(
167
167
  null,
168
168
  'SIGNUP_FAILED'
169
169
  )
170
- return addCorsHeaders(response)
170
+ return addCorsHeaders(response, req)
171
171
  }
172
172
 
173
173
  // Steps 3-4 run via the service pool (RLS bypass). This is a trusted
@@ -228,7 +228,7 @@ export const POST = withRateLimitTier(withApiLogging(
228
228
  { created: true },
229
229
  201
230
230
  )
231
- return addCorsHeaders(response)
231
+ return addCorsHeaders(response, req)
232
232
  } catch (error) {
233
233
  await tx.rollback()
234
234
  throw error
@@ -236,7 +236,7 @@ export const POST = withRateLimitTier(withApiLogging(
236
236
  } catch (error) {
237
237
  console.error('Error in signup-with-invite:', error)
238
238
  const response = createApiError('Internal server error', 500)
239
- return addCorsHeaders(response)
239
+ return addCorsHeaders(response, req)
240
240
  }
241
241
  }
242
242
  ), 'auth')
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { NextRequest, NextResponse } from 'next/server'
11
11
  import { z } from 'zod'
12
- import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
13
13
  import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
14
14
  import { getBillingGateway } from '@nextsparkjs/core/lib/billing/gateways/factory'
15
15
  import { queryWithRLS } from '@nextsparkjs/core/lib/db'
@@ -29,11 +29,12 @@ const cancelSchema = z.object({
29
29
  * - reason: string (optional) - Reason for cancellation (stored in metadata)
30
30
  */
31
31
  export const POST = withRateLimitTier(async (request: NextRequest) => {
32
- // 1. Dual authentication
33
- const authResult = await authenticateRequest(request)
32
+ // 1. Dual authentication; the API-key scope is declared at the entry
33
+ // point, which fails closed for keys that lack it (#93).
34
+ const authResult = await authenticateRequest(request, { requiredScope: 'billing:write' })
34
35
 
35
36
  if (!authResult.success || !authResult.user) {
36
- return createAuthError('Unauthorized', 401)
37
+ return createAuthFailureResponse(authResult)
37
38
  }
38
39
 
39
40
  // 2. Get team context
@@ -104,7 +105,7 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
104
105
 
105
106
  // 6. Cancel via billing gateway
106
107
  try {
107
- const gateway = getBillingGateway()
108
+ const gateway = await getBillingGateway()
108
109
  if (immediate) {
109
110
  await gateway.cancelSubscriptionImmediately(subscription.externalSubscriptionId)
110
111
  } else {
@@ -171,7 +172,7 @@ async function handleReactivation(teamId: string) {
171
172
  }
172
173
 
173
174
  try {
174
- const gateway = getBillingGateway()
175
+ const gateway = await getBillingGateway()
175
176
  await gateway.reactivateSubscription(subscription.externalSubscriptionId)
176
177
 
177
178
  // Update local DB
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { NextRequest, NextResponse } from 'next/server'
11
11
  import { z } from 'zod'
12
- import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
13
13
  import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
14
14
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
15
15
 
@@ -27,11 +27,12 @@ const changePlanSchema = z.object({
27
27
  * - billingInterval: 'monthly' | 'yearly' (default: monthly)
28
28
  */
29
29
  export const POST = withRateLimitTier(async (request: NextRequest) => {
30
- // 1. Dual authentication
31
- const authResult = await authenticateRequest(request)
30
+ // 1. Dual authentication; the API-key scope is declared at the entry
31
+ // point, which fails closed for keys that lack it (#93).
32
+ const authResult = await authenticateRequest(request, { requiredScope: 'billing:write' })
32
33
 
33
34
  if (!authResult.success || !authResult.user) {
34
- return createAuthError('Unauthorized', 401)
35
+ return createAuthFailureResponse(authResult)
35
36
  }
36
37
 
37
38
  // 2. Get team context
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { NextRequest, NextResponse } from 'next/server'
13
- import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
13
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
14
14
  import { SubscriptionService } from '@nextsparkjs/core/lib/services'
15
15
  import { z } from 'zod'
16
16
  import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
@@ -21,11 +21,13 @@ const checkActionSchema = z.object({
21
21
  })
22
22
 
23
23
  export const POST = withRateLimitTier(async (request: NextRequest) => {
24
- // 1. Dual authentication (API Key OR Session)
25
- const authResult = await authenticateRequest(request)
24
+ // 1. Dual authentication (API Key OR Session); the API-key scope is
25
+ // declared at the entry point, which fails closed for keys that lack it
26
+ // (#93).
27
+ const authResult = await authenticateRequest(request, { requiredScope: 'billing:read' })
26
28
 
27
29
  if (!authResult.success || !authResult.user) {
28
- return createAuthError('Unauthorized', 401)
30
+ return createAuthFailureResponse(authResult)
29
31
  }
30
32
 
31
33
  // 2. Parse and validate request body
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { NextRequest, NextResponse } from 'next/server'
11
- import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
11
+ import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
12
12
  import { getBillingGateway } from '@nextsparkjs/core/lib/billing/gateways/factory'
13
13
  import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
14
14
  import { z } from 'zod'
@@ -20,11 +20,12 @@ const checkoutSchema = z.object({
20
20
  })
21
21
 
22
22
  export const POST = withRateLimitTier(async (request: NextRequest) => {
23
- // 1. Dual authentication
24
- const authResult = await authenticateRequest(request)
23
+ // 1. Dual authentication; the API-key scope is declared at the entry
24
+ // point, which fails closed for keys that lack it (#93).
25
+ const authResult = await authenticateRequest(request, { requiredScope: 'billing:write' })
25
26
 
26
27
  if (!authResult.success || !authResult.user) {
27
- return createAuthError('Unauthorized', 401)
28
+ return createAuthFailureResponse(authResult)
28
29
  }
29
30
 
30
31
  // 2. Parse and validate request body
@@ -121,7 +122,8 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
121
122
  const successUrl = `${appUrl}/dashboard/settings/billing?success=true`
122
123
  const cancelUrl = `${appUrl}/dashboard/settings/billing?canceled=true`
123
124
 
124
- const session = await getBillingGateway().createCheckoutSession({
125
+ const gateway = await getBillingGateway()
126
+ const session = await gateway.createCheckoutSession({
125
127
  teamId,
126
128
  planSlug,
127
129
  billingPeriod,