@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.
- package/dist/components/app/guards/DeveloperGuard.d.ts.map +1 -1
- package/dist/components/app/guards/DeveloperGuard.js +8 -4
- package/dist/components/app/guards/SuperAdminGuard.d.ts.map +1 -1
- package/dist/components/app/guards/SuperAdminGuard.js +8 -4
- package/dist/components/app/layouts/PublicFooter.d.ts.map +1 -1
- package/dist/components/app/layouts/PublicFooter.js +2 -2
- package/dist/components/app/layouts/PublicNavbar.d.ts.map +1 -1
- package/dist/components/app/layouts/PublicNavbar.js +2 -2
- package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
- package/dist/components/app/misc/ThemeToggle.js +4 -1
- package/dist/components/auth/DevKeyring.d.ts.map +1 -1
- package/dist/components/auth/DevKeyring.js +10 -2
- package/dist/components/auth/SessionCookieRefresher.d.ts +21 -0
- package/dist/components/auth/SessionCookieRefresher.d.ts.map +1 -0
- package/dist/components/auth/SessionCookieRefresher.js +11 -0
- package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
- package/dist/components/auth/forms/LoginForm.js +300 -6
- package/dist/components/auth/index.d.ts +1 -0
- package/dist/components/auth/index.d.ts.map +1 -1
- package/dist/components/auth/index.js +2 -0
- package/dist/components/billing/InvoicesTable.d.ts.map +1 -1
- package/dist/components/billing/InvoicesTable.js +4 -3
- package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/AuthGuard.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/AuthGuard.js +2 -2
- package/dist/components/dashboard/layouts/DashboardAuthLayout.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/DashboardAuthLayout.js +2 -2
- package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts +17 -0
- package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts.map +1 -0
- package/dist/components/dashboard/layouts/DashboardAuthSkeleton.js +52 -0
- package/dist/components/dashboard/layouts/Sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/Sidebar.js +2 -3
- package/dist/components/dashboard/layouts/TopNavbar.js +2 -2
- package/dist/components/dashboard/layouts/index.d.ts +1 -0
- package/dist/components/dashboard/layouts/index.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/index.js +2 -0
- package/dist/components/devtools/api-explorer/ApiExplorer.js +4 -4
- package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
- package/dist/components/entities/EntityFieldRenderer.js +3 -0
- package/dist/components/entities/EntityTable.d.ts.map +1 -1
- package/dist/components/entities/EntityTable.js +2 -1
- package/dist/components/entities/wrappers/EntityFormWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityFormWrapper.js +7 -2
- package/dist/components/patterns/pages/PatternEditPage.d.ts +5 -1
- package/dist/components/patterns/pages/PatternEditPage.d.ts.map +1 -1
- package/dist/components/patterns/pages/PatternEditPage.js +16 -5
- package/dist/components/patterns/pages/PatternsCreatePage.d.ts +2 -1
- package/dist/components/patterns/pages/PatternsCreatePage.d.ts.map +1 -1
- package/dist/components/patterns/pages/PatternsCreatePage.js +17 -5
- package/dist/components/ui/combobox.d.ts +8 -1
- package/dist/components/ui/combobox.d.ts.map +1 -1
- package/dist/components/ui/combobox.js +37 -44
- package/dist/components/ui/country-select.d.ts +3 -1
- package/dist/components/ui/country-select.d.ts.map +1 -1
- package/dist/components/ui/country-select.js +3 -1
- package/dist/components/ui/currency-select.d.ts +3 -1
- package/dist/components/ui/currency-select.d.ts.map +1 -1
- package/dist/components/ui/currency-select.js +3 -1
- package/dist/components/ui/sheet.d.ts +5 -0
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js +46 -26
- package/dist/components/ui/timezone-select.d.ts +3 -1
- package/dist/components/ui/timezone-select.d.ts.map +1 -1
- package/dist/components/ui/timezone-select.js +3 -1
- package/dist/contexts/TeamContext.d.ts +6 -0
- package/dist/contexts/TeamContext.d.ts.map +1 -1
- package/dist/contexts/TeamContext.js +8 -1
- package/dist/emails/otp-verification.d.ts.map +1 -1
- package/dist/emails/reset-password.d.ts.map +1 -1
- package/dist/emails/team-invitation.d.ts.map +1 -1
- package/dist/emails/team-invitation.js +3 -2
- package/dist/emails/verify-email.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAuth.d.ts +29 -0
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +24 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts +22 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
- package/dist/hooks/useEnsureUserMetadata.js +18 -5
- package/dist/hooks/useSessionCookieRefresh.d.ts +35 -0
- package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -0
- package/dist/hooks/useSessionCookieRefresh.js +49 -0
- package/dist/lib/api/api-error.d.ts +3 -1
- package/dist/lib/api/api-error.d.ts.map +1 -1
- package/dist/lib/api/api-error.js +4 -2
- package/dist/lib/api/auth/dual-auth.d.ts +38 -3
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +42 -7
- package/dist/lib/api/auth/permissions.d.ts +7 -2
- package/dist/lib/api/auth/permissions.d.ts.map +1 -1
- package/dist/lib/api/auth/permissions.js +2 -2
- package/dist/lib/api/auth/scope-policy.d.ts +71 -0
- package/dist/lib/api/auth/scope-policy.d.ts.map +1 -0
- package/dist/lib/api/auth/scope-policy.js +53 -0
- package/dist/lib/api/auth.d.ts +15 -0
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +17 -4
- package/dist/lib/api/entities.js +2 -1
- package/dist/lib/api/entity/audit-log.d.ts +35 -0
- package/dist/lib/api/entity/audit-log.d.ts.map +1 -0
- package/dist/lib/api/entity/audit-log.js +35 -0
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +333 -138
- package/dist/lib/api/entity-handler.js +2 -2
- package/dist/lib/api/helpers.d.ts +29 -4
- package/dist/lib/api/helpers.d.ts.map +1 -1
- package/dist/lib/api/helpers.js +30 -6
- package/dist/lib/api/keys.d.ts +16 -0
- package/dist/lib/api/keys.d.ts.map +1 -1
- package/dist/lib/api/keys.js +19 -1
- package/dist/lib/api/routes/patterns-usages.d.ts.map +1 -1
- package/dist/lib/api/routes/patterns-usages.js +3 -11
- package/dist/lib/auth/auth-methods.d.ts +52 -0
- package/dist/lib/auth/auth-methods.d.ts.map +1 -0
- package/dist/lib/auth/auth-methods.js +63 -0
- package/dist/lib/auth/registration-guard-plugin.d.ts.map +1 -1
- package/dist/lib/auth/registration-helpers.d.ts +9 -0
- package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
- package/dist/lib/auth/registration-helpers.js +8 -2
- package/dist/lib/auth/security-notifications/device-fingerprint.d.ts +52 -0
- package/dist/lib/auth/security-notifications/device-fingerprint.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/device-fingerprint.js +42 -0
- package/dist/lib/auth/security-notifications/dispatcher.d.ts +37 -0
- package/dist/lib/auth/security-notifications/dispatcher.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/dispatcher.js +157 -0
- package/dist/lib/auth/security-notifications/index.d.ts +20 -0
- package/dist/lib/auth/security-notifications/index.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/index.js +22 -0
- package/dist/lib/auth/security-notifications/registry.d.ts +36 -0
- package/dist/lib/auth/security-notifications/registry.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/registry.js +26 -0
- package/dist/lib/auth/security-notifications/templates/email-changed.d.ts +12 -0
- package/dist/lib/auth/security-notifications/templates/email-changed.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/email-changed.js +43 -0
- package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts +7 -0
- package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/new-device-login.js +46 -0
- package/dist/lib/auth/security-notifications/templates/password-changed.d.ts +7 -0
- package/dist/lib/auth/security-notifications/templates/password-changed.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/password-changed.js +35 -0
- package/dist/lib/auth/security-notifications/templates/shared.d.ts +36 -0
- package/dist/lib/auth/security-notifications/templates/shared.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/shared.js +68 -0
- package/dist/lib/auth/security-notifications/types.d.ts +74 -0
- package/dist/lib/auth/security-notifications/types.d.ts.map +1 -0
- package/dist/lib/auth/session-config.d.ts +37 -0
- package/dist/lib/auth/session-config.d.ts.map +1 -0
- package/dist/lib/auth/session-config.js +59 -0
- package/dist/lib/auth/session-refresh.d.ts +46 -0
- package/dist/lib/auth/session-refresh.d.ts.map +1 -0
- package/dist/lib/auth/session-refresh.js +11 -0
- package/dist/lib/auth-client.d.ts +1639 -0
- package/dist/lib/auth-client.d.ts.map +1 -0
- package/dist/lib/auth-client.js +13 -1
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +45 -11
- package/dist/lib/billing/gateways/factory.d.ts +8 -3
- package/dist/lib/billing/gateways/factory.d.ts.map +1 -1
- package/dist/lib/billing/gateways/factory.js +37 -23
- package/dist/lib/billing/stripe-webhook.d.ts.map +1 -1
- package/dist/lib/billing/stripe-webhook.js +2 -1
- package/dist/lib/config/app.config.d.ts.map +1 -1
- package/dist/lib/config/app.config.js +71 -0
- package/dist/lib/config/config-sync.d.ts +1 -4
- package/dist/lib/config/config-sync.d.ts.map +1 -1
- package/dist/lib/config/config-sync.js +6 -2
- package/dist/lib/config/types.d.ts +130 -0
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/email/templates.d.ts +3 -3
- package/dist/lib/email/templates.d.ts.map +1 -1
- package/dist/lib/entities/api-generator.js +5 -5
- package/dist/lib/entities/entity-hooks.d.ts +9 -1
- package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
- package/dist/lib/entities/entity-hooks.js +77 -4
- package/dist/lib/entities/schema-generator.d.ts.map +1 -1
- package/dist/lib/entities/schema-generator.js +20 -4
- package/dist/lib/entities/serialization.d.ts +2 -2
- package/dist/lib/entities/serialization.d.ts.map +1 -1
- package/dist/lib/entities/serialization.js +3 -2
- package/dist/lib/entities/types.d.ts +20 -0
- package/dist/lib/entities/types.d.ts.map +1 -1
- package/dist/lib/locale.d.ts.map +1 -1
- package/dist/lib/locale.js +4 -1
- package/dist/lib/mcp/audit.d.ts +8 -4
- package/dist/lib/mcp/audit.d.ts.map +1 -1
- package/dist/lib/plugins/hook-system.d.ts +13 -0
- package/dist/lib/plugins/hook-system.d.ts.map +1 -1
- package/dist/lib/plugins/hook-system.js +32 -2
- package/dist/lib/scheduled-actions/handlers/index.d.ts +1 -0
- package/dist/lib/scheduled-actions/handlers/index.d.ts.map +1 -1
- package/dist/lib/scheduled-actions/handlers/index.js +3 -1
- package/dist/lib/scheduled-actions/handlers/security-notification.d.ts +26 -0
- package/dist/lib/scheduled-actions/handlers/security-notification.d.ts.map +1 -0
- package/dist/lib/scheduled-actions/handlers/security-notification.js +54 -0
- package/dist/lib/selectors/core-selectors.d.ts +12 -0
- package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.d.ts +12 -0
- package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.js +14 -1
- package/dist/lib/selectors/selectors.d.ts +24 -0
- package/dist/lib/selectors/selectors.d.ts.map +1 -1
- package/dist/lib/services/api-docs.service.d.ts +5 -1
- package/dist/lib/services/api-docs.service.d.ts.map +1 -1
- package/dist/lib/services/api-presets.service.d.ts +6 -1
- package/dist/lib/services/api-presets.service.d.ts.map +1 -1
- package/dist/lib/services/feature.service.d.ts +58 -4
- package/dist/lib/services/feature.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.js +16 -32
- package/dist/lib/services/membership.service.d.ts +10 -2
- package/dist/lib/services/membership.service.d.ts.map +1 -1
- package/dist/lib/services/membership.service.js +37 -16
- package/dist/lib/services/permission.service.d.ts +10 -4
- package/dist/lib/services/permission.service.d.ts.map +1 -1
- package/dist/lib/services/plugin.service.d.ts +1 -1
- package/dist/lib/services/plugin.service.d.ts.map +1 -1
- package/dist/lib/services/subscription.service.d.ts.map +1 -1
- package/dist/lib/services/subscription.service.js +2 -1
- package/dist/lib/services/team-member.service.d.ts +15 -3
- package/dist/lib/services/team-member.service.d.ts.map +1 -1
- package/dist/lib/services/team-member.service.js +27 -6
- package/dist/lib/services/team.service.d.ts.map +1 -1
- package/dist/lib/teams/permissions.d.ts +3 -1
- package/dist/lib/teams/permissions.d.ts.map +1 -1
- package/dist/lib/teams/permissions.js +6 -0
- package/dist/lib/teams/schema.d.ts.map +1 -1
- package/dist/lib/teams/schema.js +3 -0
- package/dist/lib/template-resolver.d.ts +3 -3
- package/dist/lib/theme/forced-theme.d.ts +34 -0
- package/dist/lib/theme/forced-theme.d.ts.map +1 -0
- package/dist/lib/theme/forced-theme.js +27 -0
- package/dist/lib/theme/get-default-theme-mode.d.ts +3 -0
- package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
- package/dist/lib/theme/get-default-theme-mode.js +5 -3
- package/dist/lib/utils/cors.d.ts +13 -0
- package/dist/lib/utils/cors.d.ts.map +1 -1
- package/dist/lib/utils/cors.js +20 -0
- package/dist/messages/de/auth.json +25 -2
- package/dist/messages/de/index.d.ts +83 -0
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/index.js +2 -0
- package/dist/messages/de/index.ts +2 -0
- package/dist/messages/de/navigation.json +3 -1
- package/dist/messages/en/auth.json +25 -2
- package/dist/messages/en/index.d.ts +83 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/index.js +2 -0
- package/dist/messages/en/index.ts +2 -0
- package/dist/messages/en/navigation.json +3 -1
- package/dist/messages/es/auth.json +25 -2
- package/dist/messages/es/index.d.ts +83 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/index.js +2 -0
- package/dist/messages/es/index.ts +2 -0
- package/dist/messages/es/navigation.json +3 -1
- package/dist/messages/fr/auth.json +25 -2
- package/dist/messages/fr/index.d.ts +83 -0
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/index.js +2 -0
- package/dist/messages/fr/index.ts +2 -0
- package/dist/messages/fr/navigation.json +3 -1
- package/dist/messages/it/auth.json +25 -2
- package/dist/messages/it/index.d.ts +83 -0
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/index.js +2 -0
- package/dist/messages/it/index.ts +2 -0
- package/dist/messages/it/navigation.json +3 -1
- package/dist/messages/pt/auth.json +25 -2
- package/dist/messages/pt/index.d.ts +83 -0
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/index.js +2 -0
- package/dist/messages/pt/index.ts +2 -0
- package/dist/messages/pt/navigation.json +3 -1
- package/dist/migrations/005_api_audit_log.sql +2 -1
- package/dist/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
- package/dist/migrations/026_login_events.sql +91 -0
- package/dist/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
- package/dist/nextspark-registries.d.ts +580 -236
- package/dist/providers/DashboardProviders.d.ts +12 -3
- package/dist/providers/DashboardProviders.d.ts.map +1 -1
- package/dist/providers/DashboardProviders.js +6 -2
- package/dist/providers/theme-provider.d.ts +19 -2
- package/dist/providers/theme-provider.d.ts.map +1 -1
- package/dist/providers/theme-provider.js +6 -2
- package/dist/templates/app/(auth)/signup/page.tsx +8 -0
- package/dist/templates/app/(public)/[...slug]/page.tsx +9 -14
- package/dist/templates/app/api/auth/[...all]/route.ts +23 -1
- package/dist/templates/app/api/health/route.ts +4 -2
- package/dist/templates/app/api/internal/user-metadata/route.ts +5 -3
- package/dist/templates/app/api/mcp/route.ts +4 -1
- package/dist/templates/app/api/superadmin/subscriptions/route.ts +2 -1
- package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
- package/dist/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
- package/dist/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
- package/dist/templates/app/api/v1/api-keys/route.ts +21 -43
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
- package/dist/templates/app/api/v1/billing/cancel/route.ts +7 -6
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +5 -4
- package/dist/templates/app/api/v1/billing/check-action/route.ts +6 -4
- package/dist/templates/app/api/v1/billing/checkout/route.ts +7 -5
- package/dist/templates/app/api/v1/billing/plans/route.ts +8 -12
- package/dist/templates/app/api/v1/billing/portal/route.ts +7 -5
- package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
- package/dist/templates/app/api/v1/devtools/blocks/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/features/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/flows/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
- package/dist/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/testing/route.ts +5 -4
- package/dist/templates/app/api/v1/media/[id]/route.ts +13 -25
- package/dist/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
- package/dist/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
- package/dist/templates/app/api/v1/media/route.ts +5 -9
- package/dist/templates/app/api/v1/media/upload/route.ts +9 -21
- package/dist/templates/app/api/v1/media-tags/route.ts +9 -13
- package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
- package/dist/templates/app/api/v1/post-categories/route.ts +5 -4
- package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
- package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
- package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
- package/dist/templates/app/api/v1/team-invitations/route.ts +9 -11
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
- package/dist/templates/app/api/v1/teams/route.ts +33 -27
- package/dist/templates/app/api/v1/teams/switch/route.ts +11 -13
- package/dist/templates/app/api/v1/theme/[...path]/route.ts +23 -2
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
- package/dist/templates/app/api/v1/users/[id]/route.ts +35 -68
- package/dist/templates/app/api/v1/users/route.ts +21 -25
- package/dist/templates/app/dashboard/layout.tsx +2 -6
- package/dist/templates/app/layout.ppr.tsx +15 -15
- package/dist/templates/app/layout.tsx +46 -20
- package/dist/templates/contents/themes/starter/config/app.config.ts +19 -0
- package/dist/templates/contents/themes/starter/config/theme.config.ts +6 -0
- package/dist/templates/contents/themes/starter/styles/globals.css +6 -2
- package/dist/templates/proxy.ts +46 -10
- package/dist/templates/tsconfig.json +4 -0
- package/dist/types/theme.d.ts +9 -0
- package/dist/types/theme.d.ts.map +1 -1
- package/migrations/005_api_audit_log.sql +2 -1
- package/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
- package/migrations/026_login_events.sql +91 -0
- package/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
- package/package.json +14 -11
- package/scripts/build/registry/__tests__/scope-registry.test.mjs +18 -0
- package/scripts/build/registry/generators/scope-registry.mjs +9 -2
- package/templates/app/(auth)/signup/page.tsx +8 -0
- package/templates/app/(public)/[...slug]/page.tsx +9 -14
- package/templates/app/api/auth/[...all]/route.ts +23 -1
- package/templates/app/api/health/route.ts +4 -2
- package/templates/app/api/internal/user-metadata/route.ts +5 -3
- package/templates/app/api/mcp/route.ts +4 -1
- package/templates/app/api/superadmin/subscriptions/route.ts +2 -1
- package/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
- package/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
- package/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
- package/templates/app/api/v1/api-keys/route.ts +21 -43
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
- package/templates/app/api/v1/billing/cancel/route.ts +7 -6
- package/templates/app/api/v1/billing/change-plan/route.ts +5 -4
- package/templates/app/api/v1/billing/check-action/route.ts +6 -4
- package/templates/app/api/v1/billing/checkout/route.ts +7 -5
- package/templates/app/api/v1/billing/plans/route.ts +8 -12
- package/templates/app/api/v1/billing/portal/route.ts +7 -5
- package/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
- package/templates/app/api/v1/devtools/blocks/route.ts +5 -4
- package/templates/app/api/v1/devtools/features/route.ts +5 -4
- package/templates/app/api/v1/devtools/flows/route.ts +5 -4
- package/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
- package/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
- package/templates/app/api/v1/devtools/testing/route.ts +5 -4
- package/templates/app/api/v1/media/[id]/route.ts +13 -25
- package/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
- package/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
- package/templates/app/api/v1/media/route.ts +5 -9
- package/templates/app/api/v1/media/upload/route.ts +9 -21
- package/templates/app/api/v1/media-tags/route.ts +9 -13
- package/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
- package/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
- package/templates/app/api/v1/post-categories/route.ts +5 -4
- package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
- package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
- package/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
- package/templates/app/api/v1/team-invitations/route.ts +9 -11
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
- package/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
- package/templates/app/api/v1/teams/route.ts +33 -27
- package/templates/app/api/v1/teams/switch/route.ts +11 -13
- package/templates/app/api/v1/theme/[...path]/route.ts +23 -2
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
- package/templates/app/api/v1/users/[id]/route.ts +35 -68
- package/templates/app/api/v1/users/route.ts +21 -25
- package/templates/app/dashboard/layout.tsx +2 -6
- package/templates/app/layout.ppr.tsx +15 -15
- package/templates/app/layout.tsx +46 -20
- package/templates/contents/themes/starter/config/app.config.ts +19 -0
- package/templates/contents/themes/starter/config/theme.config.ts +6 -0
- package/templates/contents/themes/starter/styles/globals.css +6 -2
- package/templates/proxy.ts +46 -10
- package/templates/tsconfig.json +4 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +23 -0
- package/tests/jest/__mocks__/next-server.js +24 -0
- package/dist/components/teams/TeamSwitchModal.d.js +0 -0
- package/dist/components/ui/accordion.d.js +0 -0
- package/dist/components/ui/avatar.d.js +0 -0
- package/dist/components/ui/badge.d.js +0 -0
- package/dist/components/ui/button.d.js +0 -0
- package/dist/components/ui/card.d.js +0 -0
- package/dist/components/ui/dialog.d.js +0 -0
- package/dist/components/ui/dropdown-menu.d.js +0 -0
- package/dist/components/ui/input.d.js +0 -0
- package/dist/components/ui/progress.d.js +0 -0
- package/dist/components/ui/scroll-area.d.js +0 -0
- package/dist/components/ui/select.d.js +0 -0
- package/dist/components/ui/separator.d.js +0 -0
- package/dist/components/ui/table.d.js +0 -0
- package/dist/components/ui/textarea.d.js +0 -0
- package/dist/contexts/TeamContext.d.js +0 -0
- package/dist/hooks/useAuth.d.js +0 -0
- package/dist/hooks/useLastAuthMethod.d.js +0 -0
- package/dist/hooks/useOrigin.d.js +0 -0
- package/dist/lib/api/auth/dual-auth.d.js +0 -0
- package/dist/lib/api/auth.d.js +0 -0
- package/dist/lib/api/cache.d.js +0 -0
- package/dist/lib/api/keys.d.js +0 -0
- package/dist/lib/auth-client.d.js +0 -0
- package/dist/lib/auth-context.d.js +0 -0
- package/dist/lib/auth.d.js +0 -0
- package/dist/lib/billing/config-types.d.js +0 -0
- package/dist/lib/billing/enforcement.d.js +0 -0
- package/dist/lib/billing/helpers.d.js +0 -0
- package/dist/lib/billing/jobs.d.js +0 -0
- package/dist/lib/billing/types.d.js +0 -0
- package/dist/lib/blocks/loader.d.js +0 -0
- package/dist/lib/config/app.config.d.js +0 -0
- package/dist/lib/config/config-loader.d.js +0 -0
- package/dist/lib/config/config-sync.d.js +0 -0
- package/dist/lib/config/config-types.d.js +0 -0
- package/dist/lib/config/config-utils.d.js +0 -0
- package/dist/lib/config/dashboard-types.d.js +0 -0
- package/dist/lib/config/dashboard.config.d.js +0 -0
- package/dist/lib/config/icon-map.d.js +0 -0
- package/dist/lib/config/index.d.js +0 -7
- package/dist/lib/config/nextspark-types.d.js +0 -0
- package/dist/lib/config/roles-merge.d.js +0 -0
- package/dist/lib/config/types.d.js +0 -0
- package/dist/lib/db.d.js +0 -0
- package/dist/lib/email/factory.d.js +0 -0
- package/dist/lib/email/index.d.js +0 -3
- package/dist/lib/email/providers/console.d.js +0 -0
- package/dist/lib/email/providers/resend.d.js +0 -0
- package/dist/lib/email/templates.d.js +0 -0
- package/dist/lib/email/types.d.js +0 -0
- package/dist/lib/entities/types.d.js +0 -0
- package/dist/lib/permissions/system.d.js +0 -0
- package/dist/lib/permissions/types.d.js +0 -0
- package/dist/lib/plugins/hook-system.d.js +0 -0
- package/dist/lib/scheduled-actions/cleanup.d.js +0 -0
- package/dist/lib/scheduled-actions/index.d.js +0 -19
- package/dist/lib/scheduled-actions/initializer.d.js +0 -1
- package/dist/lib/scheduled-actions/processor.d.js +0 -0
- package/dist/lib/scheduled-actions/registry.d.js +0 -0
- package/dist/lib/scheduled-actions/scheduler.d.js +0 -0
- package/dist/lib/scheduled-actions/types.d.js +0 -0
- package/dist/lib/services/api-routes.service.d.js +0 -0
- package/dist/lib/services/entity-type.service.d.js +0 -0
- package/dist/lib/services/index.d.js +0 -40
- package/dist/lib/services/invoice.service.d.js +0 -0
- package/dist/lib/services/membership.service.d.js +0 -0
- package/dist/lib/services/meta.service.d.js +0 -0
- package/dist/lib/services/middleware.service.d.js +0 -0
- package/dist/lib/services/namespace.service.d.js +0 -0
- package/dist/lib/services/permission.service.d.js +0 -0
- package/dist/lib/services/plan.service.d.js +0 -0
- package/dist/lib/services/plugin.service.d.js +0 -0
- package/dist/lib/services/route-handler.service.d.js +0 -0
- package/dist/lib/services/scope.service.d.js +0 -0
- package/dist/lib/services/subscription.service.d.js +0 -0
- package/dist/lib/services/team-member.service.d.js +0 -0
- package/dist/lib/services/team.service.d.js +0 -0
- package/dist/lib/services/theme.service.d.js +0 -0
- package/dist/lib/services/usage.service.d.js +0 -0
- package/dist/lib/services/user-flags.service.d.js +0 -0
- package/dist/lib/services/user.service.d.js +0 -0
- package/dist/lib/teams/helpers.d.js +0 -0
- package/dist/lib/teams/permissions.d.js +0 -0
- package/dist/lib/teams/types.d.js +0 -0
- package/dist/lib/utils/config-merge.d.js +0 -0
- package/dist/lib/utils/file-logger.d.js +0 -0
- package/dist/lib/utils.d.js +0 -0
- package/dist/styles/classes.json +0 -1093
- package/dist/styles/ui.css +0 -2
- package/dist/types/auth.d.js +0 -0
- package/dist/types/blocks.d.js +0 -0
- package/dist/types/meta.types.d.js +0 -0
- package/dist/types/plugin.d.js +0 -0
- package/dist/types/testing.types.d.js +0 -0
- package/dist/types/theme.d.js +0 -0
- package/dist/types/user.types.d.js +0 -0
- package/scripts/build/build-ui-css.mjs +0 -351
- /package/dist/{components/app/layouts/PublicFooter.d.js → lib/auth/security-notifications/types.js} +0 -0
- /package/dist/{components/app/layouts/PublicNavbar.d.js → nextspark-registries.d.js} +0 -0
- /package/dist/{components/app/misc/ThemeToggle.d.js → types/use-intl-react-shim.d.js} +0 -0
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DashboardProviders — Client providers needed
|
|
2
|
+
* DashboardProviders — Client providers needed by authenticated routes.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Since #115 the generated root layout mounts QueryProvider, TeamProvider,
|
|
5
|
+
* SubscriptionProvider and Toaster itself (matching layout.ppr.tsx) so the
|
|
6
|
+
* activeTeamId cookie is synced from any route, not only from the dashboard.
|
|
7
|
+
* When a TeamProvider is already mounted above, this component renders its
|
|
8
|
+
* children as-is: nesting a second TeamProvider would duplicate the teams
|
|
9
|
+
* fetch, the cookie sync and the TeamSwitchModal, and a second Toaster would
|
|
10
|
+
* show every toast twice.
|
|
11
|
+
*
|
|
12
|
+
* The full provider stack is kept as a fallback for apps whose generated root
|
|
13
|
+
* layout predates that change (it originally lived here to keep ~500KB of
|
|
14
|
+
* client JS off public landing pages).
|
|
6
15
|
*
|
|
7
16
|
* Used by: dashboard/layout.tsx, superadmin/layout.tsx, devtools/layout.tsx
|
|
8
17
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardProviders.d.ts","sourceRoot":"","sources":["../../src/providers/DashboardProviders.tsx"],"names":[],"mappings":"AAQA
|
|
1
|
+
{"version":3,"file":"DashboardProviders.d.ts","sourceRoot":"","sources":["../../src/providers/DashboardProviders.tsx"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAiB7E"}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Suspense } from "react";
|
|
4
4
|
import { QueryProvider } from "./query-provider.js";
|
|
5
|
-
import { TeamProvider } from "../contexts/TeamContext.js";
|
|
5
|
+
import { TeamProvider, useOptionalTeamContext } from "../contexts/TeamContext.js";
|
|
6
6
|
import { SubscriptionProvider } from "../contexts/SubscriptionContext.js";
|
|
7
7
|
import { Toaster } from "../components/ui/sonner.js";
|
|
8
8
|
function DashboardProviders({ children }) {
|
|
9
|
+
const rootTeamContext = useOptionalTeamContext();
|
|
10
|
+
if (rootTeamContext) {
|
|
11
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
12
|
+
}
|
|
9
13
|
return /* @__PURE__ */ jsx(QueryProvider, { children: /* @__PURE__ */ jsx(TeamProvider, { children: /* @__PURE__ */ jsxs(SubscriptionProvider, { children: [
|
|
10
14
|
children,
|
|
11
15
|
/* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(Toaster, { position: "bottom-left" }) })
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
import { type ThemeProviderProps } from "next-themes";
|
|
2
|
-
|
|
1
|
+
import { type ThemeProviderProps as NextThemesProviderProps } from "next-themes";
|
|
2
|
+
import { type ForcedThemeRoutes } from "../lib/theme/forced-theme";
|
|
3
|
+
export interface ThemeProviderProps extends NextThemesProviderProps {
|
|
4
|
+
/**
|
|
5
|
+
* Routes whose theme is forced regardless of user/system preference
|
|
6
|
+
* (`theme.config.ts` → `forcedThemeRoutes`).
|
|
7
|
+
*
|
|
8
|
+
* Resolved here, on the client, against `usePathname()` instead of once in
|
|
9
|
+
* the root layout from request headers: the root layout does not re-render
|
|
10
|
+
* on client-side navigations, so a server-computed value would go stale as
|
|
11
|
+
* soon as the user navigated into or out of a forced route. On the initial
|
|
12
|
+
* request this component is still server-rendered, so next-themes' blocking
|
|
13
|
+
* script receives the forced value and there is no flash.
|
|
14
|
+
*
|
|
15
|
+
* Takes precedence over the plain `forcedTheme` prop.
|
|
16
|
+
*/
|
|
17
|
+
forcedThemeRoutes?: ForcedThemeRoutes;
|
|
18
|
+
}
|
|
19
|
+
export declare function ThemeProvider({ children, forcedThemeRoutes, forcedTheme, ...props }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
3
20
|
//# sourceMappingURL=theme-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/providers/theme-provider.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/providers/theme-provider.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,kBAAkB,IAAI,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAChF,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAEtF,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IACjE;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACtC;AAED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,2CASvG"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
3
4
|
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
import { resolveForcedTheme } from "../lib/theme/forced-theme.js";
|
|
6
|
+
function ThemeProvider({ children, forcedThemeRoutes, forcedTheme, ...props }) {
|
|
7
|
+
const pathname = usePathname();
|
|
8
|
+
const routeForcedTheme = resolveForcedTheme(pathname, forcedThemeRoutes);
|
|
9
|
+
return /* @__PURE__ */ jsx(NextThemesProvider, { ...props, forcedTheme: routeForcedTheme ?? forcedTheme, children });
|
|
6
10
|
}
|
|
7
11
|
export {
|
|
8
12
|
ThemeProvider
|
|
@@ -4,6 +4,7 @@ import { redirect } from 'next/navigation'
|
|
|
4
4
|
import { SignupForm } from '@nextsparkjs/core/components/auth/forms/SignupForm'
|
|
5
5
|
import { getTemplateOrDefault, getMetadataOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
6
6
|
import { AUTH_CONFIG } from '@nextsparkjs/core/lib/config'
|
|
7
|
+
import { resolveAuthMethods } from '@nextsparkjs/core/lib/auth/auth-methods'
|
|
7
8
|
import { TeamService } from '@nextsparkjs/core/lib/services'
|
|
8
9
|
|
|
9
10
|
// This page reads DB state (TeamService.hasGlobal) and redirect()s during render,
|
|
@@ -23,6 +24,13 @@ export const metadata: Metadata = getMetadataOrDefault(
|
|
|
23
24
|
async function SignupPageContent() {
|
|
24
25
|
const registrationMode = AUTH_CONFIG?.registration?.mode ?? 'open'
|
|
25
26
|
|
|
27
|
+
// Passwordless preset (no 'email-password' in auth.methods): the account is
|
|
28
|
+
// created by the first one-time-code sign-in, so there is no password signup
|
|
29
|
+
// form to show — send people to /login instead.
|
|
30
|
+
if (!resolveAuthMethods(AUTH_CONFIG).includes('email-password')) {
|
|
31
|
+
redirect('/login')
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
// In invitation-only mode, allow the first user to register
|
|
27
35
|
// (when no global team exists yet). Subsequent users need invitations.
|
|
28
36
|
if (registrationMode === 'invitation-only') {
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
* 2. Falls back to default PageRenderer if no template
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { Suspense } from 'react'
|
|
17
16
|
import { notFound } from 'next/navigation'
|
|
18
17
|
import { query } from '@nextsparkjs/core/lib/db'
|
|
19
18
|
import { PageRenderer } from '@nextsparkjs/core/components/public/pageBuilder'
|
|
@@ -249,9 +248,16 @@ export async function generateMetadata({
|
|
|
249
248
|
}
|
|
250
249
|
|
|
251
250
|
/**
|
|
252
|
-
* Main catch-all page
|
|
251
|
+
* Main catch-all page component
|
|
252
|
+
*
|
|
253
|
+
* Not wrapped in Suspense: this route's entire content depends on whether the
|
|
254
|
+
* slug resolves to a published item, so notFound() must be able to run before
|
|
255
|
+
* any part of the response commits to a 200. A Suspense boundary here would
|
|
256
|
+
* let Next.js flush the (null) fallback with a 200 status ahead of the
|
|
257
|
+
* DB-backed lookup, so a later notFound() inside the boundary could no longer
|
|
258
|
+
* change the status code — see #129.
|
|
253
259
|
*/
|
|
254
|
-
async function
|
|
260
|
+
export default async function DynamicPublicPage({
|
|
255
261
|
params,
|
|
256
262
|
searchParams,
|
|
257
263
|
}: PageProps) {
|
|
@@ -378,14 +384,3 @@ async function DynamicPublicPageContent({
|
|
|
378
384
|
// No match found
|
|
379
385
|
notFound()
|
|
380
386
|
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Main catch-all page component — wraps async content in Suspense for PPR compatibility
|
|
384
|
-
*/
|
|
385
|
-
export default function DynamicPublicPage(props: PageProps) {
|
|
386
|
-
return (
|
|
387
|
-
<Suspense fallback={null}>
|
|
388
|
-
<DynamicPublicPageContent {...props} />
|
|
389
|
-
</Suspense>
|
|
390
|
-
)
|
|
391
|
-
}
|
|
@@ -9,6 +9,7 @@ import { TeamService } from "@nextsparkjs/core/lib/services";
|
|
|
9
9
|
import { wrapAuthHandlerWithCors, handleCorsPreflightRequest, addCorsHeaders } from "@nextsparkjs/core/lib/api/helpers";
|
|
10
10
|
import { checkDistributedRateLimit } from "@nextsparkjs/core/lib/api/rate-limit";
|
|
11
11
|
import { withSignupContext } from "@nextsparkjs/core/lib/auth-context";
|
|
12
|
+
import { dispatchSecurityNotificationsForRequest } from "@nextsparkjs/core/lib/auth/security-notifications";
|
|
12
13
|
|
|
13
14
|
const handlers = toNextJsHandler(auth);
|
|
14
15
|
|
|
@@ -181,11 +182,32 @@ export async function POST(req: NextRequest) {
|
|
|
181
182
|
? (req.cookies.get('signup-intent')?.value || undefined)
|
|
182
183
|
: undefined;
|
|
183
184
|
|
|
185
|
+
// Clone the request BEFORE the handler consumes its body. The
|
|
186
|
+
// security-notifications dispatcher (below) needs the original request's
|
|
187
|
+
// headers (UA/IP) and, for change-email, its body (newEmail) — but
|
|
188
|
+
// handlers.POST(req) reads req's body exactly once.
|
|
189
|
+
const securityReq = req.clone() as NextRequest;
|
|
190
|
+
|
|
184
191
|
// Wrap with CORS headers for cross-origin requests (mobile apps, etc.)
|
|
185
|
-
|
|
192
|
+
const response = await wrapAuthHandlerWithCors(
|
|
186
193
|
signupIntent
|
|
187
194
|
? () => withSignupContext({ signupIntent }, () => handlers.POST(req))
|
|
188
195
|
: () => handlers.POST(req),
|
|
189
196
|
req
|
|
190
197
|
);
|
|
198
|
+
|
|
199
|
+
// Built-in security email notifications (issue #75): after a successful
|
|
200
|
+
// sign-in / change-password / change-email, queue the matching security
|
|
201
|
+
// email. Guarded by AUTH_CONFIG.securityNotifications.enabled and wrapped in
|
|
202
|
+
// try/catch — a notification failure must NEVER change the auth response
|
|
203
|
+
// status (the dispatcher is itself best-effort, this is defense in depth).
|
|
204
|
+
if (AUTH_CONFIG.securityNotifications?.enabled) {
|
|
205
|
+
try {
|
|
206
|
+
await dispatchSecurityNotificationsForRequest(securityReq, response);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error("[auth] security-notifications dispatch error:", error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return response;
|
|
191
213
|
}
|
|
@@ -24,8 +24,10 @@ export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
|
24
24
|
timestamp: new Date().toISOString(),
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
// Check if caller is authenticated for detailed info
|
|
28
|
-
|
|
27
|
+
// Check if caller is authenticated for detailed info. Public endpoint: any
|
|
28
|
+
// valid API key (whatever its scopes) may see the detailed status, so the
|
|
29
|
+
// scope-agnostic opt-out is explicit here (#93).
|
|
30
|
+
const authResult = await authenticateRequest(request, { allowAnyScope: true });
|
|
29
31
|
const isAuthenticated = authResult.success && authResult.user;
|
|
30
32
|
|
|
31
33
|
if (!isAuthenticated) {
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
2
2
|
import { MetaService } from '@nextsparkjs/core/lib/services/meta.service'
|
|
3
3
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
4
|
-
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
4
|
+
import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
5
5
|
|
|
6
6
|
// Endpoint interno para crear metadata default después del signup
|
|
7
7
|
export const POST = withRateLimitTier(async (req: NextRequest) => {
|
|
8
8
|
try {
|
|
9
|
-
|
|
9
|
+
// The API-key scope is declared at the entry point, which fails closed
|
|
10
|
+
// for keys that lack it (#93).
|
|
11
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'users:write' })
|
|
10
12
|
if (!authResult.success || !authResult.user) {
|
|
11
|
-
return
|
|
13
|
+
return createAuthFailureResponse(authResult)
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
const body = await req.json()
|
|
@@ -106,7 +106,10 @@ async function handleMcpPost(request: NextRequest): Promise<NextResponse> {
|
|
|
106
106
|
return jsonRpcError(403, -32000, 'Origin not allowed')
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
// Transport-level auth only: per-tool scopes (`<entity>:read/write/delete`)
|
|
110
|
+
// are enforced by the generic entity handlers the executor invokes for each
|
|
111
|
+
// tool call, so this entry point accepts any valid key explicitly (#93).
|
|
112
|
+
const authResult = await authenticateRequest(request, { allowAnyScope: true })
|
|
110
113
|
if (!authResult.success || authResult.type !== 'api-key' || !authResult.user) {
|
|
111
114
|
return jsonRpcError(401, -32001, API_KEY_HELP)
|
|
112
115
|
}
|
|
@@ -230,6 +230,7 @@ export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
|
230
230
|
const totalPages = Math.ceil(total / limit);
|
|
231
231
|
|
|
232
232
|
// Format subscriptions
|
|
233
|
+
const billingGateway = await getBillingGateway()
|
|
233
234
|
const subscriptions = subscriptionsResult.map(sub => ({
|
|
234
235
|
id: sub.id,
|
|
235
236
|
team: {
|
|
@@ -257,7 +258,7 @@ export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
|
257
258
|
cancelAtPeriodEnd: sub.cancelAtPeriodEnd,
|
|
258
259
|
externalSubscriptionId: sub.externalSubscriptionId,
|
|
259
260
|
paymentProvider: sub.paymentProvider,
|
|
260
|
-
providerDashboardUrl:
|
|
261
|
+
providerDashboardUrl: billingGateway.getSubscriptionDashboardUrl(sub.externalSubscriptionId),
|
|
261
262
|
createdAt: sub.createdAt
|
|
262
263
|
}));
|
|
263
264
|
|
|
@@ -204,6 +204,7 @@ export const GET = withRateLimitTier(async (
|
|
|
204
204
|
const subscription = subscriptionResult[0] || null;
|
|
205
205
|
|
|
206
206
|
// Prepare response data
|
|
207
|
+
const billingGateway = subscription ? await getBillingGateway() : null
|
|
207
208
|
const responseData = {
|
|
208
209
|
team: {
|
|
209
210
|
id: team.id,
|
|
@@ -225,7 +226,7 @@ export const GET = withRateLimitTier(async (
|
|
|
225
226
|
role: member.role,
|
|
226
227
|
joinedAt: member.joinedAt
|
|
227
228
|
})),
|
|
228
|
-
subscription: subscription ? {
|
|
229
|
+
subscription: subscription && billingGateway ? {
|
|
229
230
|
id: subscription.id,
|
|
230
231
|
plan: {
|
|
231
232
|
id: subscription.planId,
|
|
@@ -246,8 +247,8 @@ export const GET = withRateLimitTier(async (
|
|
|
246
247
|
externalSubscriptionId: subscription.externalSubscriptionId,
|
|
247
248
|
externalCustomerId: subscription.externalCustomerId,
|
|
248
249
|
paymentProvider: subscription.paymentProvider,
|
|
249
|
-
providerName:
|
|
250
|
-
providerDashboardUrl:
|
|
250
|
+
providerName: billingGateway.getProviderName(),
|
|
251
|
+
providerDashboardUrl: billingGateway.getSubscriptionDashboardUrl(subscription.externalSubscriptionId),
|
|
251
252
|
createdAt: subscription.createdAt
|
|
252
253
|
} : null,
|
|
253
254
|
billingHistory: billingEventsResult.map((event) => ({
|
|
@@ -129,11 +129,18 @@ export const GET = withRateLimitTier(async (request: NextRequest, { params }: Ro
|
|
|
129
129
|
ORDER BY "metaKey"
|
|
130
130
|
`;
|
|
131
131
|
|
|
132
|
-
// Execute all queries in parallel
|
|
132
|
+
// Execute all queries in parallel.
|
|
133
|
+
//
|
|
134
|
+
// The team_members queries run on the service (RLS-bypass) pool: team_members
|
|
135
|
+
// has no superadmin/developer bypass policy, so with the acting admin's own
|
|
136
|
+
// id as RLS context the SELECT policy silently ANDs "teams the actor belongs
|
|
137
|
+
// to" on top of WHERE "userId" = $1 and under-reports multi-team users.
|
|
138
|
+
// Authorization is already enforced above (superadmin/developer role check)
|
|
139
|
+
// and the WHERE clause scopes the rows to the subject user.
|
|
133
140
|
const [userResults, teamsResults, countsResults, metasResults] = await Promise.all([
|
|
134
141
|
queryWithRLS(userQuery, [userId], session.user.id) as Promise<UserResult[]>,
|
|
135
|
-
queryWithRLS(teamsQuery, [userId], session.user.id) as Promise<TeamMembershipResult[]>,
|
|
136
|
-
queryWithRLS(countsQuery, [userId], session.user.id) as Promise<{
|
|
142
|
+
queryWithRLS(teamsQuery, [userId], session.user.id, { service: true }) as Promise<TeamMembershipResult[]>,
|
|
143
|
+
queryWithRLS(countsQuery, [userId], session.user.id, { service: true }) as Promise<{
|
|
137
144
|
totalTeams: number;
|
|
138
145
|
ownedTeams: number;
|
|
139
146
|
}[]>,
|
|
@@ -20,12 +20,13 @@ import {
|
|
|
20
20
|
handleCorsPreflightRequest,
|
|
21
21
|
addCorsHeaders
|
|
22
22
|
} from '@nextsparkjs/core/lib/api/helpers';
|
|
23
|
-
import { authenticateRequest,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
223
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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');
|