@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
|
@@ -7,29 +7,27 @@ import {
|
|
|
7
7
|
handleCorsPreflightRequest,
|
|
8
8
|
addCorsHeaders,
|
|
9
9
|
} from '@nextsparkjs/core/lib/api/helpers'
|
|
10
|
-
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
10
|
+
import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
11
11
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
12
12
|
import { updateTeamSchema } from '@nextsparkjs/core/lib/teams/schema'
|
|
13
13
|
import { TeamService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
14
14
|
import type { Team, TeamRole } from '@nextsparkjs/core/lib/teams/types'
|
|
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
|
// GET /api/v1/teams/:teamId - Get team details
|
|
22
22
|
export const GET = withRateLimitTier(withApiLogging(
|
|
23
23
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
24
24
|
try {
|
|
25
|
-
// Authenticate using dual auth
|
|
26
|
-
|
|
25
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
26
|
+
// point, which fails closed for keys that lack it (#93).
|
|
27
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'teams:read' })
|
|
27
28
|
|
|
28
29
|
if (!authResult.success) {
|
|
29
|
-
return
|
|
30
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
31
|
-
{ status: 401 }
|
|
32
|
-
)
|
|
30
|
+
return createAuthFailureResponse(authResult)
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
if (authResult.rateLimitResponse) {
|
|
@@ -41,7 +39,7 @@ export const GET = withRateLimitTier(withApiLogging(
|
|
|
41
39
|
// Validate that teamId is not empty
|
|
42
40
|
if (!teamId || teamId.trim() === '') {
|
|
43
41
|
const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
|
|
44
|
-
return addCorsHeaders(response)
|
|
42
|
+
return addCorsHeaders(response, req)
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
// Check if user is a member of the team
|
|
@@ -53,7 +51,7 @@ export const GET = withRateLimitTier(withApiLogging(
|
|
|
53
51
|
|
|
54
52
|
if (!userRole) {
|
|
55
53
|
const response = createApiError('Team not found or access denied', 404, null, 'TEAM_NOT_FOUND')
|
|
56
|
-
return addCorsHeaders(response)
|
|
54
|
+
return addCorsHeaders(response, req)
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
// Fetch team with member count
|
|
@@ -72,7 +70,7 @@ export const GET = withRateLimitTier(withApiLogging(
|
|
|
72
70
|
|
|
73
71
|
if (!team) {
|
|
74
72
|
const response = createApiError('Team not found', 404, null, 'TEAM_NOT_FOUND')
|
|
75
|
-
return addCorsHeaders(response)
|
|
73
|
+
return addCorsHeaders(response, req)
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
const responseData = {
|
|
@@ -81,11 +79,11 @@ export const GET = withRateLimitTier(withApiLogging(
|
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
const response = createApiResponse(responseData)
|
|
84
|
-
return addCorsHeaders(response)
|
|
82
|
+
return addCorsHeaders(response, req)
|
|
85
83
|
} catch (error) {
|
|
86
84
|
console.error('Error fetching team:', error)
|
|
87
85
|
const response = createApiError('Internal server error', 500)
|
|
88
|
-
return addCorsHeaders(response)
|
|
86
|
+
return addCorsHeaders(response, req)
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
89
|
), 'read')
|
|
@@ -94,14 +92,12 @@ export const GET = withRateLimitTier(withApiLogging(
|
|
|
94
92
|
export const PATCH = withRateLimitTier(withApiLogging(
|
|
95
93
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
96
94
|
try {
|
|
97
|
-
// Authenticate using dual auth
|
|
98
|
-
|
|
95
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
96
|
+
// point, which fails closed for keys that lack it (#93).
|
|
97
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'teams:write' })
|
|
99
98
|
|
|
100
99
|
if (!authResult.success) {
|
|
101
|
-
return
|
|
102
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
103
|
-
{ status: 401 }
|
|
104
|
-
)
|
|
100
|
+
return createAuthFailureResponse(authResult)
|
|
105
101
|
}
|
|
106
102
|
|
|
107
103
|
if (authResult.rateLimitResponse) {
|
|
@@ -113,12 +109,12 @@ export const PATCH = withRateLimitTier(withApiLogging(
|
|
|
113
109
|
// Validate that teamId is not empty
|
|
114
110
|
if (!teamId || teamId.trim() === '') {
|
|
115
111
|
const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
|
|
116
|
-
return addCorsHeaders(response)
|
|
112
|
+
return addCorsHeaders(response, req)
|
|
117
113
|
}
|
|
118
114
|
|
|
119
115
|
// Check if user has permission to edit team using MembershipService
|
|
120
116
|
const membership = await MembershipService.get(authResult.user!.id, teamId)
|
|
121
|
-
const actionResult = membership.canPerformAction('
|
|
117
|
+
const actionResult = membership.canPerformAction('team.edit')
|
|
122
118
|
|
|
123
119
|
if (!actionResult.allowed) {
|
|
124
120
|
const response = NextResponse.json(
|
|
@@ -130,7 +126,7 @@ export const PATCH = withRateLimitTier(withApiLogging(
|
|
|
130
126
|
},
|
|
131
127
|
{ status: 403 }
|
|
132
128
|
)
|
|
133
|
-
return addCorsHeaders(response)
|
|
129
|
+
return addCorsHeaders(response, req)
|
|
134
130
|
}
|
|
135
131
|
|
|
136
132
|
const body = await req.json()
|
|
@@ -141,7 +137,7 @@ export const PATCH = withRateLimitTier(withApiLogging(
|
|
|
141
137
|
const slugAvailable = await TeamService.isSlugAvailable(validatedData.slug, teamId)
|
|
142
138
|
if (!slugAvailable) {
|
|
143
139
|
const response = createApiError('Team slug already exists', 409, null, 'SLUG_EXISTS')
|
|
144
|
-
return addCorsHeaders(response)
|
|
140
|
+
return addCorsHeaders(response, req)
|
|
145
141
|
}
|
|
146
142
|
}
|
|
147
143
|
|
|
@@ -177,7 +173,7 @@ export const PATCH = withRateLimitTier(withApiLogging(
|
|
|
177
173
|
|
|
178
174
|
if (updates.length === 0) {
|
|
179
175
|
const response = createApiError('No fields to update', 400, null, 'NO_FIELDS')
|
|
180
|
-
return addCorsHeaders(response)
|
|
176
|
+
return addCorsHeaders(response, req)
|
|
181
177
|
}
|
|
182
178
|
|
|
183
179
|
updates.push(`"updatedAt" = CURRENT_TIMESTAMP`)
|
|
@@ -194,7 +190,7 @@ export const PATCH = withRateLimitTier(withApiLogging(
|
|
|
194
190
|
|
|
195
191
|
if (result.rows.length === 0) {
|
|
196
192
|
const response = createApiError('Team not found', 404, null, 'TEAM_NOT_FOUND')
|
|
197
|
-
return addCorsHeaders(response)
|
|
193
|
+
return addCorsHeaders(response, req)
|
|
198
194
|
}
|
|
199
195
|
|
|
200
196
|
// Fetch team with member count
|
|
@@ -216,17 +212,17 @@ export const PATCH = withRateLimitTier(withApiLogging(
|
|
|
216
212
|
}
|
|
217
213
|
|
|
218
214
|
const response = createApiResponse(responseData)
|
|
219
|
-
return addCorsHeaders(response)
|
|
215
|
+
return addCorsHeaders(response, req)
|
|
220
216
|
} catch (error) {
|
|
221
217
|
if (error instanceof Error && error.name === 'ZodError') {
|
|
222
218
|
const zodError = error as { issues?: unknown[] }
|
|
223
219
|
const response = createApiError('Validation error', 400, zodError.issues, 'VALIDATION_ERROR')
|
|
224
|
-
return addCorsHeaders(response)
|
|
220
|
+
return addCorsHeaders(response, req)
|
|
225
221
|
}
|
|
226
222
|
|
|
227
223
|
console.error('Error updating team:', error)
|
|
228
224
|
const response = createApiError('Internal server error', 500)
|
|
229
|
-
return addCorsHeaders(response)
|
|
225
|
+
return addCorsHeaders(response, req)
|
|
230
226
|
}
|
|
231
227
|
}
|
|
232
228
|
), 'write')
|
|
@@ -235,14 +231,12 @@ export const PATCH = withRateLimitTier(withApiLogging(
|
|
|
235
231
|
export const DELETE = withRateLimitTier(withApiLogging(
|
|
236
232
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
237
233
|
try {
|
|
238
|
-
// Authenticate using dual auth
|
|
239
|
-
|
|
234
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
235
|
+
// point, which fails closed for keys that lack it (#93).
|
|
236
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'teams:delete' })
|
|
240
237
|
|
|
241
238
|
if (!authResult.success) {
|
|
242
|
-
return
|
|
243
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
244
|
-
{ status: 401 }
|
|
245
|
-
)
|
|
239
|
+
return createAuthFailureResponse(authResult)
|
|
246
240
|
}
|
|
247
241
|
|
|
248
242
|
if (authResult.rateLimitResponse) {
|
|
@@ -254,7 +248,7 @@ export const DELETE = withRateLimitTier(withApiLogging(
|
|
|
254
248
|
// Validate that teamId is not empty
|
|
255
249
|
if (!teamId || teamId.trim() === '') {
|
|
256
250
|
const response = createApiError('Team ID is required', 400, null, 'MISSING_TEAM_ID')
|
|
257
|
-
return addCorsHeaders(response)
|
|
251
|
+
return addCorsHeaders(response, req)
|
|
258
252
|
}
|
|
259
253
|
|
|
260
254
|
// Use the TeamService.delete (handles owner check and personal team protection)
|
|
@@ -262,7 +256,7 @@ export const DELETE = withRateLimitTier(withApiLogging(
|
|
|
262
256
|
await TeamService.delete(teamId, authResult.user!.id)
|
|
263
257
|
|
|
264
258
|
const response = createApiResponse({ deleted: true, id: teamId })
|
|
265
|
-
return addCorsHeaders(response)
|
|
259
|
+
return addCorsHeaders(response, req)
|
|
266
260
|
} catch (error) {
|
|
267
261
|
if (error instanceof Error && error.message === 'Only team owner can delete the team') {
|
|
268
262
|
const response = createApiError(
|
|
@@ -271,17 +265,17 @@ export const DELETE = withRateLimitTier(withApiLogging(
|
|
|
271
265
|
null,
|
|
272
266
|
'INSUFFICIENT_PERMISSIONS'
|
|
273
267
|
)
|
|
274
|
-
return addCorsHeaders(response)
|
|
268
|
+
return addCorsHeaders(response, req)
|
|
275
269
|
}
|
|
276
270
|
|
|
277
271
|
if (error instanceof Error && error.message === 'Team not found') {
|
|
278
272
|
const response = createApiError('Team not found', 404, null, 'TEAM_NOT_FOUND')
|
|
279
|
-
return addCorsHeaders(response)
|
|
273
|
+
return addCorsHeaders(response, req)
|
|
280
274
|
}
|
|
281
275
|
|
|
282
276
|
if (error instanceof Error && error.message === 'Personal teams cannot be deleted') {
|
|
283
277
|
const response = createApiError('Personal teams cannot be deleted', 403, null, 'PERSONAL_TEAM_DELETE_FORBIDDEN')
|
|
284
|
-
return addCorsHeaders(response)
|
|
278
|
+
return addCorsHeaders(response, req)
|
|
285
279
|
}
|
|
286
280
|
|
|
287
281
|
throw error
|
|
@@ -289,7 +283,7 @@ export const DELETE = withRateLimitTier(withApiLogging(
|
|
|
289
283
|
} catch (error) {
|
|
290
284
|
console.error('Error deleting team:', error)
|
|
291
285
|
const response = createApiError('Internal server error', 500)
|
|
292
|
-
return addCorsHeaders(response)
|
|
286
|
+
return addCorsHeaders(response, req)
|
|
293
287
|
}
|
|
294
288
|
}
|
|
295
289
|
), 'write')
|
|
@@ -14,16 +14,18 @@ interface RouteParams {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export const GET = withRateLimitTier(async function GET(request: NextRequest, props: RouteParams) {
|
|
17
|
-
// Authenticate request
|
|
18
|
-
|
|
17
|
+
// Authenticate request; the API-key scope is declared at the entry point,
|
|
18
|
+
// which fails closed for keys that lack it (#93).
|
|
19
|
+
const { auth, rateLimitResponse, errorResponse } = await validateAndAuthenticateRequest(request, { requiredScope: 'billing:read' })
|
|
19
20
|
if (rateLimitResponse) return rateLimitResponse
|
|
21
|
+
if (!auth) return errorResponse ?? createApiError('Authentication required', 401, undefined, 'AUTHENTICATION_REQUIRED')
|
|
20
22
|
|
|
21
23
|
const { teamId } = await props.params
|
|
22
24
|
|
|
23
25
|
try {
|
|
24
26
|
// Check if user has permission to view subscription using MembershipService
|
|
25
27
|
const membership = await MembershipService.get(auth.userId, teamId)
|
|
26
|
-
const actionResult = membership.canPerformAction('billing.view')
|
|
28
|
+
const actionResult = membership.canPerformAction('team.billing.view')
|
|
27
29
|
|
|
28
30
|
if (!actionResult.allowed) {
|
|
29
31
|
return NextResponse.json(
|
|
@@ -16,16 +16,18 @@ interface RouteParams {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export const GET = withRateLimitTier(async function GET(request: NextRequest, props: RouteParams) {
|
|
19
|
-
// Authenticate request
|
|
20
|
-
|
|
19
|
+
// Authenticate request; the API-key scope is declared at the entry point,
|
|
20
|
+
// which fails closed for keys that lack it (#93).
|
|
21
|
+
const { auth, rateLimitResponse, errorResponse } = await validateAndAuthenticateRequest(request, { requiredScope: 'billing:read' })
|
|
21
22
|
if (rateLimitResponse) return rateLimitResponse
|
|
23
|
+
if (!auth) return errorResponse ?? createApiError('Authentication required', 401, undefined, 'AUTHENTICATION_REQUIRED')
|
|
22
24
|
|
|
23
25
|
const { teamId, limitSlug } = await props.params
|
|
24
26
|
|
|
25
27
|
try {
|
|
26
28
|
// Check if user has permission to view usage using MembershipService
|
|
27
29
|
const membership = await MembershipService.get(auth.userId, teamId)
|
|
28
|
-
const actionResult = membership.canPerformAction('billing.view')
|
|
30
|
+
const actionResult = membership.canPerformAction('team.billing.view')
|
|
29
31
|
|
|
30
32
|
if (!actionResult.allowed) {
|
|
31
33
|
return NextResponse.json(
|
|
@@ -48,16 +50,18 @@ export const GET = withRateLimitTier(async function GET(request: NextRequest, pr
|
|
|
48
50
|
}, 'read')
|
|
49
51
|
|
|
50
52
|
export const POST = withRateLimitTier(async function POST(request: NextRequest, props: RouteParams) {
|
|
51
|
-
// Authenticate request
|
|
52
|
-
|
|
53
|
+
// Authenticate request; the API-key scope is declared at the entry point,
|
|
54
|
+
// which fails closed for keys that lack it (#93).
|
|
55
|
+
const { auth, rateLimitResponse, errorResponse } = await validateAndAuthenticateRequest(request, { requiredScope: 'billing:write' })
|
|
53
56
|
if (rateLimitResponse) return rateLimitResponse
|
|
57
|
+
if (!auth) return errorResponse ?? createApiError('Authentication required', 401, undefined, 'AUTHENTICATION_REQUIRED')
|
|
54
58
|
|
|
55
59
|
const { teamId, limitSlug } = await props.params
|
|
56
60
|
|
|
57
61
|
try {
|
|
58
62
|
// Check if user has permission to track usage using MembershipService
|
|
59
63
|
const membership = await MembershipService.get(auth.userId, teamId)
|
|
60
|
-
const actionResult = membership.canPerformAction('billing.manage')
|
|
64
|
+
const actionResult = membership.canPerformAction('team.billing.manage')
|
|
61
65
|
|
|
62
66
|
if (!actionResult.allowed) {
|
|
63
67
|
return NextResponse.json(
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
handleCorsPreflightRequest,
|
|
9
9
|
addCorsHeaders,
|
|
10
10
|
} from '@nextsparkjs/core/lib/api/helpers'
|
|
11
|
-
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
11
|
+
import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
12
12
|
import { isSuperAdmin } from '@nextsparkjs/core/lib/api/auth/permissions'
|
|
13
13
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
14
14
|
import { createTeamSchema, teamListQuerySchema } from '@nextsparkjs/core/lib/teams/schema'
|
|
@@ -17,21 +17,19 @@ import type { Team } from '@nextsparkjs/core/lib/teams/types'
|
|
|
17
17
|
import { APP_CONFIG_MERGED } from '@nextsparkjs/core/lib/config/config-sync'
|
|
18
18
|
|
|
19
19
|
// Handle CORS preflight
|
|
20
|
-
export async function OPTIONS() {
|
|
21
|
-
return handleCorsPreflightRequest()
|
|
20
|
+
export async function OPTIONS(request: NextRequest) {
|
|
21
|
+
return handleCorsPreflightRequest(request)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// GET /api/v1/teams - List user's teams
|
|
25
25
|
export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
26
26
|
try {
|
|
27
|
-
// Authenticate using dual auth
|
|
28
|
-
|
|
27
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
28
|
+
// point, which fails closed for keys that lack it (#93).
|
|
29
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'teams:read' })
|
|
29
30
|
|
|
30
31
|
if (!authResult.success) {
|
|
31
|
-
return
|
|
32
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
33
|
-
{ status: 401 }
|
|
34
|
-
)
|
|
32
|
+
return createAuthFailureResponse(authResult)
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
if (authResult.rateLimitResponse) {
|
|
@@ -62,7 +60,7 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
|
|
|
62
60
|
'Insufficient permissions. Superadmin access required for scope=all.',
|
|
63
61
|
403
|
|
64
62
|
)
|
|
65
|
-
return addCorsHeaders(response)
|
|
63
|
+
return addCorsHeaders(response, req)
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
// Build WHERE clause based on filters
|
|
@@ -93,7 +91,7 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
|
|
|
93
91
|
// Add pagination params
|
|
94
92
|
queryValues.push(limit, offset)
|
|
95
93
|
|
|
96
|
-
let teams: (Team & { userRole: string | null; memberCount: string })[]
|
|
94
|
+
let teams: (Team & { userRole: string | null; joinedAt?: string; memberCount: string })[]
|
|
97
95
|
let totalResult: { count: string }[]
|
|
98
96
|
|
|
99
97
|
if (requestAllTeams) {
|
|
@@ -122,17 +120,27 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
|
|
|
122
120
|
)
|
|
123
121
|
} else {
|
|
124
122
|
// Regular user query: filter by membership
|
|
125
|
-
|
|
123
|
+
// tm."joinedAt" is unambiguous here (not an aggregation concern): the
|
|
124
|
+
// WHERE clause already pins tm."userId" = the caller, so tm is exactly
|
|
125
|
+
// this user's own membership row in each team — the same "joinedAt"
|
|
126
|
+
// dual-auth.ts's getUserDefaultTeamId() uses to pick a default team.
|
|
127
|
+
// Its absence here (never selected, even though the client sorted by
|
|
128
|
+
// it) made TeamContext's #115 fix a no-op in production: every
|
|
129
|
+
// `joinedAt` came back undefined, so `new Date(undefined).getTime()`
|
|
130
|
+
// compared NaN to NaN and the client silently kept the API's row
|
|
131
|
+
// order instead of the earliest-joined team.
|
|
132
|
+
teams = await queryWithRLS<Team & { userRole: string; joinedAt: string; memberCount: string }>(
|
|
126
133
|
`SELECT
|
|
127
134
|
t.*,
|
|
128
135
|
tm.role as "userRole",
|
|
136
|
+
tm."joinedAt" as "joinedAt",
|
|
129
137
|
COUNT(DISTINCT tm2.id) as "memberCount"
|
|
130
138
|
FROM "teams" t
|
|
131
139
|
INNER JOIN "team_members" tm ON t.id = tm."teamId"
|
|
132
140
|
LEFT JOIN "team_members" tm2 ON t.id = tm2."teamId"
|
|
133
141
|
${whereClause}
|
|
134
142
|
GROUP BY t.id, t.name, t.slug, t.description, t."ownerId", t."avatarUrl",
|
|
135
|
-
t.settings, t.metadata, t."createdAt", t."updatedAt", tm.role
|
|
143
|
+
t.settings, t.metadata, t."createdAt", t."updatedAt", tm.role, tm."joinedAt"
|
|
136
144
|
ORDER BY ${orderByClause}
|
|
137
145
|
LIMIT $${paramCount++} OFFSET $${paramCount++}`,
|
|
138
146
|
queryValues,
|
|
@@ -160,25 +168,23 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
|
|
|
160
168
|
}))
|
|
161
169
|
|
|
162
170
|
const response = createApiResponse(teamsWithMembers, paginationMeta)
|
|
163
|
-
return addCorsHeaders(response)
|
|
171
|
+
return addCorsHeaders(response, req)
|
|
164
172
|
} catch (error) {
|
|
165
173
|
console.error('Error fetching teams:', error)
|
|
166
174
|
const response = createApiError('Internal server error', 500)
|
|
167
|
-
return addCorsHeaders(response)
|
|
175
|
+
return addCorsHeaders(response, req)
|
|
168
176
|
}
|
|
169
177
|
}), 'read')
|
|
170
178
|
|
|
171
179
|
// POST /api/v1/teams - Create new team
|
|
172
180
|
export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
173
181
|
try {
|
|
174
|
-
// Authenticate using dual auth
|
|
175
|
-
|
|
182
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
183
|
+
// point, which fails closed for keys that lack it (#93).
|
|
184
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'teams:write' })
|
|
176
185
|
|
|
177
186
|
if (!authResult.success) {
|
|
178
|
-
return
|
|
179
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
180
|
-
{ status: 401 }
|
|
181
|
-
)
|
|
187
|
+
return createAuthFailureResponse(authResult)
|
|
182
188
|
}
|
|
183
189
|
|
|
184
190
|
if (authResult.rateLimitResponse) {
|
|
@@ -199,7 +205,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
199
205
|
null,
|
|
200
206
|
'TEAM_CREATION_DISABLED'
|
|
201
207
|
)
|
|
202
|
-
return addCorsHeaders(response)
|
|
208
|
+
return addCorsHeaders(response, req)
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
// Validate allowCreateTeams option
|
|
@@ -220,7 +226,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
220
226
|
null,
|
|
221
227
|
'MAX_TEAMS_REACHED'
|
|
222
228
|
)
|
|
223
|
-
return addCorsHeaders(response)
|
|
229
|
+
return addCorsHeaders(response, req)
|
|
224
230
|
}
|
|
225
231
|
}
|
|
226
232
|
|
|
@@ -228,7 +234,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
228
234
|
const slugAvailable = await TeamService.isSlugAvailable(validatedData.slug)
|
|
229
235
|
if (!slugAvailable) {
|
|
230
236
|
const response = createApiError('Team slug already exists', 409, null, 'SLUG_EXISTS')
|
|
231
|
-
return addCorsHeaders(response)
|
|
237
|
+
return addCorsHeaders(response, req)
|
|
232
238
|
}
|
|
233
239
|
|
|
234
240
|
// Use transaction to ensure atomicity
|
|
@@ -280,7 +286,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
280
286
|
}
|
|
281
287
|
|
|
282
288
|
const response = createApiResponse(responseData, { created: true }, 201)
|
|
283
|
-
return addCorsHeaders(response)
|
|
289
|
+
return addCorsHeaders(response, req)
|
|
284
290
|
} catch (error) {
|
|
285
291
|
await tx.rollback()
|
|
286
292
|
throw error
|
|
@@ -289,11 +295,11 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
289
295
|
if (error instanceof Error && error.name === 'ZodError') {
|
|
290
296
|
const zodError = error as { issues?: unknown[] }
|
|
291
297
|
const response = createApiError('Validation error', 400, zodError.issues, 'VALIDATION_ERROR')
|
|
292
|
-
return addCorsHeaders(response)
|
|
298
|
+
return addCorsHeaders(response, req)
|
|
293
299
|
}
|
|
294
300
|
|
|
295
301
|
console.error('Error creating team:', error)
|
|
296
302
|
const response = createApiError('Internal server error', 500)
|
|
297
|
-
return addCorsHeaders(response)
|
|
303
|
+
return addCorsHeaders(response, req)
|
|
298
304
|
}
|
|
299
305
|
}), 'write')
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
handleCorsPreflightRequest,
|
|
7
7
|
addCorsHeaders,
|
|
8
8
|
} from '@nextsparkjs/core/lib/api/helpers'
|
|
9
|
-
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
9
|
+
import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
10
10
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
11
11
|
import { TeamService } from '@nextsparkjs/core/lib/services'
|
|
12
12
|
import { z } from 'zod'
|
|
@@ -16,21 +16,19 @@ const switchTeamSchema = z.object({
|
|
|
16
16
|
})
|
|
17
17
|
|
|
18
18
|
// Handle CORS preflight
|
|
19
|
-
export async function OPTIONS() {
|
|
20
|
-
return handleCorsPreflightRequest()
|
|
19
|
+
export async function OPTIONS(request: NextRequest) {
|
|
20
|
+
return handleCorsPreflightRequest(request)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// POST /api/v1/teams/switch - Switch active team context
|
|
24
24
|
export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
25
25
|
try {
|
|
26
|
-
// Authenticate using dual auth
|
|
27
|
-
|
|
26
|
+
// Authenticate using dual auth. Only teams:read: this endpoint changes
|
|
27
|
+
// which team the caller sees, it never writes team data (#93).
|
|
28
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'teams:read' })
|
|
28
29
|
|
|
29
30
|
if (!authResult.success) {
|
|
30
|
-
return
|
|
31
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
32
|
-
{ status: 401 }
|
|
33
|
-
)
|
|
31
|
+
return createAuthFailureResponse(authResult)
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
if (authResult.rateLimitResponse) {
|
|
@@ -61,7 +59,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
61
59
|
maxAge: 60 * 60 * 24 * 365 // 1 year
|
|
62
60
|
})
|
|
63
61
|
|
|
64
|
-
return addCorsHeaders(response)
|
|
62
|
+
return addCorsHeaders(response, req)
|
|
65
63
|
} catch (error) {
|
|
66
64
|
if (error instanceof Error && error.message === 'User is not a member of this team') {
|
|
67
65
|
const response = createApiError(
|
|
@@ -70,7 +68,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
70
68
|
null,
|
|
71
69
|
'NOT_TEAM_MEMBER'
|
|
72
70
|
)
|
|
73
|
-
return addCorsHeaders(response)
|
|
71
|
+
return addCorsHeaders(response, req)
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
throw error
|
|
@@ -79,11 +77,11 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
79
77
|
if (error instanceof Error && error.name === 'ZodError') {
|
|
80
78
|
const zodError = error as { issues?: unknown[] }
|
|
81
79
|
const response = createApiError('Validation error', 400, zodError.issues, 'VALIDATION_ERROR')
|
|
82
|
-
return addCorsHeaders(response)
|
|
80
|
+
return addCorsHeaders(response, req)
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
console.error('Error switching team:', error)
|
|
86
84
|
const response = createApiError('Internal server error', 500)
|
|
87
|
-
return addCorsHeaders(response)
|
|
85
|
+
return addCorsHeaders(response, req)
|
|
88
86
|
}
|
|
89
87
|
}), 'write')
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type ThemeRegistryEntry
|
|
13
13
|
} from '@nextsparkjs/registries/theme-registry'
|
|
14
14
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
15
|
+
import { validateAndAuthenticateRequest } from '@nextsparkjs/core/lib/api/helpers'
|
|
15
16
|
|
|
16
17
|
export const GET = withRateLimitTier(async (
|
|
17
18
|
request: NextRequest,
|
|
@@ -111,6 +112,12 @@ async function handleThemeRequest(
|
|
|
111
112
|
console.log(`[Theme API] No route found for ${requestPath}, method: ${method}`)
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
// The endpoint catalog is only disclosed to authenticated callers.
|
|
116
|
+
// Anonymous requests still get theme info / 404, but never the route map.
|
|
117
|
+
const endpointCatalog = (await isAuthenticatedRequest(request))
|
|
118
|
+
? { availableEndpoints: getThemeEndpoints(themeName) }
|
|
119
|
+
: {}
|
|
120
|
+
|
|
114
121
|
// Default theme info endpoint
|
|
115
122
|
if (!endpointPath && method === 'GET') {
|
|
116
123
|
return NextResponse.json({
|
|
@@ -124,7 +131,7 @@ async function handleThemeRequest(
|
|
|
124
131
|
entities: themeEntry.entities?.length || 0,
|
|
125
132
|
routeFiles: themeEntry.routeFiles?.length || 0,
|
|
126
133
|
plugins: themeEntry.plugins || [],
|
|
127
|
-
|
|
134
|
+
...endpointCatalog
|
|
128
135
|
}
|
|
129
136
|
})
|
|
130
137
|
}
|
|
@@ -136,7 +143,7 @@ async function handleThemeRequest(
|
|
|
136
143
|
theme: themeName,
|
|
137
144
|
path: endpointPath,
|
|
138
145
|
method,
|
|
139
|
-
|
|
146
|
+
...endpointCatalog
|
|
140
147
|
},
|
|
141
148
|
{ status: 404 }
|
|
142
149
|
)
|
|
@@ -154,6 +161,20 @@ async function handleThemeRequest(
|
|
|
154
161
|
}
|
|
155
162
|
}
|
|
156
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Check whether the request carries a valid session or API key.
|
|
166
|
+
* Used to gate the endpoint catalog (see #104 / #109): the list of registered
|
|
167
|
+
* theme routes must not be enumerable by anonymous callers.
|
|
168
|
+
*/
|
|
169
|
+
async function isAuthenticatedRequest(request: NextRequest): Promise<boolean> {
|
|
170
|
+
try {
|
|
171
|
+
const { auth, rateLimitResponse } = await validateAndAuthenticateRequest(request)
|
|
172
|
+
return !rateLimitResponse && Boolean(auth?.userId)
|
|
173
|
+
} catch {
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
157
178
|
/**
|
|
158
179
|
* Match route pattern with dynamic parameters
|
|
159
180
|
* Example: 'orders/[orderId]/items' matches 'orders/123/items'
|