@nextsparkjs/core 0.1.0-beta.184 → 0.1.0-beta.186
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/SuperAdminGuard.d.ts.map +1 -1
- 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/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/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 +103 -25
- 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/hooks/useValidationSchemas.d.ts +7 -7
- 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 +45 -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 +35 -3
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +54 -23
- 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 -155
- 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/security-notifications/types.js +0 -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 +1489 -3445
- package/dist/lib/auth-client.d.ts.map +1 -1
- package/dist/lib/auth-client.js +4 -0
- package/dist/lib/auth.d.ts +1 -3057
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +21 -8
- 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/schema.d.ts +1 -1
- 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/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 +8 -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 +21 -0
- package/dist/lib/mcp/audit.d.ts.map +1 -0
- package/dist/lib/mcp/audit.js +48 -0
- package/dist/lib/mcp/engine.d.ts +28 -0
- package/dist/lib/mcp/engine.d.ts.map +1 -0
- package/dist/lib/mcp/engine.js +158 -0
- package/dist/lib/mcp/errors.d.ts +16 -0
- package/dist/lib/mcp/errors.d.ts.map +1 -0
- package/dist/lib/mcp/errors.js +66 -0
- package/dist/lib/mcp/executor.d.ts +15 -0
- package/dist/lib/mcp/executor.d.ts.map +1 -0
- package/dist/lib/mcp/executor.js +63 -0
- package/dist/lib/mcp/index.d.ts +9 -0
- package/dist/lib/mcp/index.d.ts.map +1 -0
- package/dist/lib/mcp/index.js +32 -0
- package/dist/lib/mcp/schema-builder.d.ts +45 -0
- package/dist/lib/mcp/schema-builder.d.ts.map +1 -0
- package/dist/lib/mcp/schema-builder.js +212 -0
- package/dist/lib/mcp/tool-generator.d.ts +31 -0
- package/dist/lib/mcp/tool-generator.d.ts.map +1 -0
- package/dist/lib/mcp/tool-generator.js +271 -0
- package/dist/lib/mcp/transport.d.ts +31 -0
- package/dist/lib/mcp/transport.d.ts.map +1 -0
- package/dist/lib/mcp/transport.js +58 -0
- package/dist/lib/mcp/types.d.ts +184 -0
- package/dist/lib/mcp/types.d.ts.map +1 -0
- package/dist/lib/mcp/types.js +11 -0
- 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/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/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/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/messages/de/auth.json +25 -2
- package/dist/messages/de/index.d.ts +81 -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/en/auth.json +25 -2
- package/dist/messages/en/index.d.ts +81 -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/es/auth.json +25 -2
- package/dist/messages/es/index.d.ts +81 -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/fr/auth.json +25 -2
- package/dist/messages/fr/index.d.ts +81 -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/it/auth.json +25 -2
- package/dist/messages/it/index.d.ts +81 -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/pt/auth.json +25 -2
- package/dist/messages/pt/index.d.ts +81 -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/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/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 +176 -0
- 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 +35 -45
- 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 +9 -14
- package/dist/templates/app/layout.tsx +39 -19
- 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 +20 -12
- package/scripts/build/registry/__tests__/mcp-registry.test.mjs +64 -0
- package/scripts/build/registry/__tests__/scope-registry.test.mjs +106 -0
- package/scripts/build/registry/discovery/mcp-overrides.mjs +72 -0
- package/scripts/build/registry/discovery/mcp-overrides.test.mjs +86 -0
- package/scripts/build/registry/generators/mcp-registry.mjs +61 -0
- package/scripts/build/registry/generators/scope-registry.mjs +140 -88
- package/scripts/build/registry.mjs +8 -4
- 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 +176 -0
- 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 +35 -45
- 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 +9 -14
- package/templates/app/layout.tsx +39 -19
- 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__/@nextsparkjs/registries/scope-registry.ts +56 -6
- package/tests/jest/__mocks__/next-server.js +24 -0
- package/dist/styles/classes.json +0 -1093
- package/dist/styles/ui.css +0 -2
- package/scripts/build/build-ui-css.mjs +0 -351
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
handleEntityMetadataInResponse,
|
|
12
12
|
processEntityMetadata
|
|
13
13
|
} from '@nextsparkjs/core/lib/api/helpers';
|
|
14
|
-
import { authenticateRequest,
|
|
14
|
+
import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth';
|
|
15
15
|
import { z } from 'zod';
|
|
16
16
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit';
|
|
17
17
|
|
|
@@ -24,8 +24,8 @@ const updateUserSchema = z.object({
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
// Handle CORS preflight
|
|
27
|
-
export async function OPTIONS() {
|
|
28
|
-
return handleCorsPreflightRequest();
|
|
27
|
+
export async function OPTIONS(request: NextRequest) {
|
|
28
|
+
return handleCorsPreflightRequest(request);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// GET /api/v1/users/:id - Get specific user
|
|
@@ -34,35 +34,24 @@ export const GET = withRateLimitTier(withApiLogging(async (
|
|
|
34
34
|
{ params }: { params: Promise<{ id: string }> }
|
|
35
35
|
): Promise<NextResponse> => {
|
|
36
36
|
try {
|
|
37
|
-
// Authenticate using dual auth
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
38
|
+
// point, which fails closed for keys that lack it (#93).
|
|
39
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'users:read' });
|
|
40
|
+
|
|
40
41
|
if (!authResult.success) {
|
|
41
|
-
return
|
|
42
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
43
|
-
{ status: 401 }
|
|
44
|
-
);
|
|
42
|
+
return createAuthFailureResponse(authResult);
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
if (authResult.rateLimitResponse) {
|
|
48
46
|
return authResult.rateLimitResponse as NextResponse;
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
// Check required permissions - session users need admin role, API key users need specific scope
|
|
52
|
-
const hasPermission = authResult.type === 'session' ||
|
|
53
|
-
(authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:read'));
|
|
54
|
-
|
|
55
|
-
if (!hasPermission) {
|
|
56
|
-
const response = createApiError('Insufficient permissions. Admin access required for user management.', 403);
|
|
57
|
-
return addCorsHeaders(response);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
49
|
const { id } = await params;
|
|
61
50
|
|
|
62
51
|
// Validate that id is not empty
|
|
63
52
|
if (!id || id.trim() === '') {
|
|
64
53
|
const response = createApiError('User ID or email is required', 400, null, 'MISSING_IDENTIFIER');
|
|
65
|
-
return addCorsHeaders(response);
|
|
54
|
+
return addCorsHeaders(response, req);
|
|
66
55
|
}
|
|
67
56
|
|
|
68
57
|
// Search by ID or email
|
|
@@ -74,7 +63,7 @@ export const GET = withRateLimitTier(withApiLogging(async (
|
|
|
74
63
|
|
|
75
64
|
if (!user) {
|
|
76
65
|
const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
|
|
77
|
-
return addCorsHeaders(response);
|
|
66
|
+
return addCorsHeaders(response, req);
|
|
78
67
|
}
|
|
79
68
|
|
|
80
69
|
// Handle metadata if requested (usando helper compartido)
|
|
@@ -82,11 +71,11 @@ export const GET = withRateLimitTier(withApiLogging(async (
|
|
|
82
71
|
const userWithMeta = await includeEntityMetadataForSingle('user', user as { id: string }, metaParams, authResult.user!.id);
|
|
83
72
|
|
|
84
73
|
const response = createApiResponse(userWithMeta);
|
|
85
|
-
return addCorsHeaders(response);
|
|
74
|
+
return addCorsHeaders(response, req);
|
|
86
75
|
} catch (error) {
|
|
87
76
|
console.error('Error fetching user:', error);
|
|
88
77
|
const response = createApiError('Internal server error', 500);
|
|
89
|
-
return addCorsHeaders(response);
|
|
78
|
+
return addCorsHeaders(response, req);
|
|
90
79
|
}
|
|
91
80
|
}), 'read');
|
|
92
81
|
|
|
@@ -96,35 +85,24 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
|
|
|
96
85
|
{ params }: { params: Promise<{ id: string }> }
|
|
97
86
|
): Promise<NextResponse> => {
|
|
98
87
|
try {
|
|
99
|
-
// Authenticate using dual auth
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
89
|
+
// point, which fails closed for keys that lack it (#93).
|
|
90
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'users:write' });
|
|
91
|
+
|
|
102
92
|
if (!authResult.success) {
|
|
103
|
-
return
|
|
104
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
105
|
-
{ status: 401 }
|
|
106
|
-
);
|
|
93
|
+
return createAuthFailureResponse(authResult);
|
|
107
94
|
}
|
|
108
95
|
|
|
109
96
|
if (authResult.rateLimitResponse) {
|
|
110
97
|
return authResult.rateLimitResponse as NextResponse;
|
|
111
98
|
}
|
|
112
99
|
|
|
113
|
-
// Check required permissions - session users need admin role, API key users need specific scope
|
|
114
|
-
const hasPermission = authResult.type === 'session' ||
|
|
115
|
-
(authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:write'));
|
|
116
|
-
|
|
117
|
-
if (!hasPermission) {
|
|
118
|
-
const response = createApiError('Insufficient permissions. Admin access required for user management.', 403);
|
|
119
|
-
return addCorsHeaders(response);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
100
|
const { id } = await params;
|
|
123
101
|
|
|
124
102
|
// Validate that id is not empty
|
|
125
103
|
if (!id || id.trim() === '') {
|
|
126
104
|
const response = createApiError('User ID or email is required', 400, null, 'MISSING_IDENTIFIER');
|
|
127
|
-
return addCorsHeaders(response);
|
|
105
|
+
return addCorsHeaders(response, req);
|
|
128
106
|
}
|
|
129
107
|
|
|
130
108
|
const body = await req.json();
|
|
@@ -162,7 +140,7 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
|
|
|
162
140
|
|
|
163
141
|
if (!hasEntityFieldsToUpdate && !hasMetadataToUpdate) {
|
|
164
142
|
const response = createApiError('No fields to update', 400, null, 'NO_FIELDS');
|
|
165
|
-
return addCorsHeaders(response);
|
|
143
|
+
return addCorsHeaders(response, req);
|
|
166
144
|
}
|
|
167
145
|
|
|
168
146
|
let updatedUser;
|
|
@@ -183,7 +161,7 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
|
|
|
183
161
|
|
|
184
162
|
if (result.rows.length === 0) {
|
|
185
163
|
const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
|
|
186
|
-
return addCorsHeaders(response);
|
|
164
|
+
return addCorsHeaders(response, req);
|
|
187
165
|
}
|
|
188
166
|
|
|
189
167
|
updatedUser = result.rows[0];
|
|
@@ -197,7 +175,7 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
|
|
|
197
175
|
|
|
198
176
|
if (!user) {
|
|
199
177
|
const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
|
|
200
|
-
return addCorsHeaders(response);
|
|
178
|
+
return addCorsHeaders(response, req);
|
|
201
179
|
}
|
|
202
180
|
|
|
203
181
|
updatedUser = user;
|
|
@@ -214,16 +192,16 @@ export const PATCH = withRateLimitTier(withApiLogging(async (
|
|
|
214
192
|
const responseData = await handleEntityMetadataInResponse('user', updatedUser as { id: string }, metadataWasProvided, authResult.user!.id);
|
|
215
193
|
|
|
216
194
|
const response = createApiResponse(responseData);
|
|
217
|
-
return addCorsHeaders(response);
|
|
195
|
+
return addCorsHeaders(response, req);
|
|
218
196
|
} catch (error) {
|
|
219
197
|
if (error instanceof z.ZodError) {
|
|
220
198
|
const response = createApiError('Validation error', 400, error.issues, 'VALIDATION_ERROR');
|
|
221
|
-
return addCorsHeaders(response);
|
|
199
|
+
return addCorsHeaders(response, req);
|
|
222
200
|
}
|
|
223
201
|
|
|
224
202
|
console.error('Error updating user:', error);
|
|
225
203
|
const response = createApiError('Internal server error', 500);
|
|
226
|
-
return addCorsHeaders(response);
|
|
204
|
+
return addCorsHeaders(response, req);
|
|
227
205
|
}
|
|
228
206
|
}), 'write');
|
|
229
207
|
|
|
@@ -233,35 +211,24 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
|
|
|
233
211
|
{ params }: { params: Promise<{ id: string }> }
|
|
234
212
|
): Promise<NextResponse> => {
|
|
235
213
|
try {
|
|
236
|
-
// Authenticate using dual auth
|
|
237
|
-
|
|
238
|
-
|
|
214
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
215
|
+
// point, which fails closed for keys that lack it (#93).
|
|
216
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'users:delete' });
|
|
217
|
+
|
|
239
218
|
if (!authResult.success) {
|
|
240
|
-
return
|
|
241
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
242
|
-
{ status: 401 }
|
|
243
|
-
);
|
|
219
|
+
return createAuthFailureResponse(authResult);
|
|
244
220
|
}
|
|
245
221
|
|
|
246
222
|
if (authResult.rateLimitResponse) {
|
|
247
223
|
return authResult.rateLimitResponse as NextResponse;
|
|
248
224
|
}
|
|
249
225
|
|
|
250
|
-
// Check required permissions - session users need admin role, API key users need specific scope
|
|
251
|
-
const hasPermission = authResult.type === 'session' ||
|
|
252
|
-
(authResult.type === 'api-key' && hasRequiredScope(authResult, 'users:delete'));
|
|
253
|
-
|
|
254
|
-
if (!hasPermission) {
|
|
255
|
-
const response = createApiError('Insufficient permissions. Admin access required for user management.', 403);
|
|
256
|
-
return addCorsHeaders(response);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
226
|
const { id } = await params;
|
|
260
227
|
|
|
261
228
|
// Validate that id is not empty
|
|
262
229
|
if (!id || id.trim() === '') {
|
|
263
230
|
const response = createApiError('User ID or email is required', 400, null, 'MISSING_IDENTIFIER');
|
|
264
|
-
return addCorsHeaders(response);
|
|
231
|
+
return addCorsHeaders(response, req);
|
|
265
232
|
}
|
|
266
233
|
|
|
267
234
|
// First, get the user to check if it exists and prevent self-deletion
|
|
@@ -273,13 +240,13 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
|
|
|
273
240
|
|
|
274
241
|
if (!targetUser) {
|
|
275
242
|
const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
|
|
276
|
-
return addCorsHeaders(response);
|
|
243
|
+
return addCorsHeaders(response, req);
|
|
277
244
|
}
|
|
278
245
|
|
|
279
246
|
// Prevent self-deletion
|
|
280
247
|
if ((targetUser as Record<string, unknown>).id === authResult.user!.id) {
|
|
281
248
|
const response = createApiError('Cannot delete your own account via API', 403, null, 'SELF_DELETE_FORBIDDEN');
|
|
282
|
-
return addCorsHeaders(response);
|
|
249
|
+
return addCorsHeaders(response, req);
|
|
283
250
|
}
|
|
284
251
|
|
|
285
252
|
const result = await mutateWithRLS(
|
|
@@ -290,14 +257,14 @@ export const DELETE = withRateLimitTier(withApiLogging(async (
|
|
|
290
257
|
|
|
291
258
|
if (result.rows.length === 0) {
|
|
292
259
|
const response = createApiError('User not found', 404, null, 'USER_NOT_FOUND');
|
|
293
|
-
return addCorsHeaders(response);
|
|
260
|
+
return addCorsHeaders(response, req);
|
|
294
261
|
}
|
|
295
262
|
|
|
296
263
|
const response = createApiResponse({ deleted: true, id });
|
|
297
|
-
return addCorsHeaders(response);
|
|
264
|
+
return addCorsHeaders(response, req);
|
|
298
265
|
} catch (error) {
|
|
299
266
|
console.error('Error deleting user:', error);
|
|
300
267
|
const response = createApiError('Internal server error', 500);
|
|
301
|
-
return addCorsHeaders(response);
|
|
268
|
+
return addCorsHeaders(response, req);
|
|
302
269
|
}
|
|
303
270
|
}), 'write');
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
handleEntityMetadataInResponse,
|
|
15
15
|
processEntityMetadata
|
|
16
16
|
} from '@nextsparkjs/core/lib/api/helpers';
|
|
17
|
-
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth';
|
|
17
|
+
import { authenticateRequest, createAuthFailureResponse } from '@nextsparkjs/core/lib/api/auth/dual-auth';
|
|
18
18
|
import { hasAdminPermission } from '@nextsparkjs/core/lib/api/auth/permissions';
|
|
19
19
|
import { z } from 'zod';
|
|
20
20
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit';
|
|
@@ -34,21 +34,19 @@ const createUserSchema = z.object({
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
// Handle CORS preflight
|
|
37
|
-
export async function OPTIONS() {
|
|
38
|
-
return handleCorsPreflightRequest();
|
|
37
|
+
export async function OPTIONS(request: NextRequest) {
|
|
38
|
+
return handleCorsPreflightRequest(request);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// GET /api/v1/users - List users with dual auth
|
|
42
42
|
export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
43
43
|
try {
|
|
44
|
-
// Authenticate using dual auth
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
45
|
+
// point, which fails closed for keys that lack it (#93).
|
|
46
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'users:read' });
|
|
47
|
+
|
|
47
48
|
if (!authResult.success) {
|
|
48
|
-
return
|
|
49
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
50
|
-
{ status: 401 }
|
|
51
|
-
);
|
|
49
|
+
return createAuthFailureResponse(authResult);
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
if (authResult.rateLimitResponse) {
|
|
@@ -58,7 +56,7 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
|
|
|
58
56
|
// SECURITY: Only superadmins can list all users
|
|
59
57
|
if (!hasAdminPermission(authResult, 'users:read')) {
|
|
60
58
|
const response = createApiError('Insufficient permissions. Superadmin access required.', 403);
|
|
61
|
-
return addCorsHeaders(response);
|
|
59
|
+
return addCorsHeaders(response, req);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
const metaParams = parseMetaParams(req);
|
|
@@ -103,25 +101,23 @@ export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Pr
|
|
|
103
101
|
const usersWithMeta = await includeEntityMetadata('user', users as { id: string }[], metaParams, authResult.user!.id);
|
|
104
102
|
|
|
105
103
|
const response = createApiResponse(usersWithMeta, paginationMeta);
|
|
106
|
-
return addCorsHeaders(response);
|
|
104
|
+
return addCorsHeaders(response, req);
|
|
107
105
|
} catch (error) {
|
|
108
106
|
console.error('Error fetching users:', error);
|
|
109
107
|
const response = createApiError('Internal server error', 500);
|
|
110
|
-
return addCorsHeaders(response);
|
|
108
|
+
return addCorsHeaders(response, req);
|
|
111
109
|
}
|
|
112
110
|
}), 'read');
|
|
113
111
|
|
|
114
112
|
// POST /api/v1/users - Create user with dual auth
|
|
115
113
|
export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
116
114
|
try {
|
|
117
|
-
// Authenticate using dual auth
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
// Authenticate using dual auth; the API-key scope is declared at the entry
|
|
116
|
+
// point, which fails closed for keys that lack it (#93).
|
|
117
|
+
const authResult = await authenticateRequest(req, { requiredScope: 'users:write' });
|
|
118
|
+
|
|
120
119
|
if (!authResult.success) {
|
|
121
|
-
return
|
|
122
|
-
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
123
|
-
{ status: 401 }
|
|
124
|
-
);
|
|
120
|
+
return createAuthFailureResponse(authResult);
|
|
125
121
|
}
|
|
126
122
|
|
|
127
123
|
if (authResult.rateLimitResponse) {
|
|
@@ -131,7 +127,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
131
127
|
// SECURITY: Only superadmins can create users
|
|
132
128
|
if (!hasAdminPermission(authResult, 'users:write')) {
|
|
133
129
|
const response = createApiError('Insufficient permissions. Superadmin access required.', 403);
|
|
134
|
-
return addCorsHeaders(response);
|
|
130
|
+
return addCorsHeaders(response, req);
|
|
135
131
|
}
|
|
136
132
|
|
|
137
133
|
const body = await req.json();
|
|
@@ -147,7 +143,7 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
147
143
|
|
|
148
144
|
if (existingUser.length > 0) {
|
|
149
145
|
const response = createApiError('Email already exists', 409, null, 'EMAIL_EXISTS');
|
|
150
|
-
return addCorsHeaders(response);
|
|
146
|
+
return addCorsHeaders(response, req);
|
|
151
147
|
}
|
|
152
148
|
|
|
153
149
|
const newUserId = globalThis.crypto.randomUUID();
|
|
@@ -183,16 +179,16 @@ export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): P
|
|
|
183
179
|
const responseData = await handleEntityMetadataInResponse('user', createdUser as { id: string }, metadataWasProvided, authResult.user!.id);
|
|
184
180
|
|
|
185
181
|
const response = createApiResponse(responseData, { created: true }, 201);
|
|
186
|
-
return addCorsHeaders(response);
|
|
182
|
+
return addCorsHeaders(response, req);
|
|
187
183
|
} catch (error) {
|
|
188
184
|
if (error instanceof z.ZodError) {
|
|
189
185
|
const response = createApiError('Validation error', 400, error.issues, 'VALIDATION_ERROR');
|
|
190
|
-
return addCorsHeaders(response);
|
|
186
|
+
return addCorsHeaders(response, req);
|
|
191
187
|
}
|
|
192
188
|
|
|
193
189
|
console.error('Error creating user:', error);
|
|
194
190
|
const response = createApiError('Internal server error', 500);
|
|
195
|
-
return addCorsHeaders(response);
|
|
191
|
+
return addCorsHeaders(response, req);
|
|
196
192
|
}
|
|
197
193
|
}), 'write');
|
|
198
194
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useAuth } from '@nextsparkjs/core/hooks/useAuth'
|
|
4
4
|
import { useRouter } from 'next/navigation'
|
|
5
5
|
import { useEffect, Suspense } from 'react'
|
|
6
|
-
import {
|
|
6
|
+
import { DashboardAuthSkeleton } from '@nextsparkjs/core/components/dashboard/layouts/DashboardAuthSkeleton'
|
|
7
7
|
import { DashboardProviders } from '@nextsparkjs/core/providers/DashboardProviders'
|
|
8
8
|
import { DashboardTranslationPreloader } from '@nextsparkjs/core/lib/i18n/DashboardTranslationPreloader'
|
|
9
9
|
import { TranslationDebugger } from '@nextsparkjs/core/utils/dev/TranslationDebugger'
|
|
@@ -35,11 +35,7 @@ function DashboardLayoutContent({ children }: { children: React.ReactNode }) {
|
|
|
35
35
|
}, [user, isLoading, router])
|
|
36
36
|
|
|
37
37
|
if (isLoading) {
|
|
38
|
-
return
|
|
39
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
40
|
-
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
41
|
-
</div>
|
|
42
|
-
)
|
|
38
|
+
return <DashboardAuthSkeleton />
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
if (!user) {
|
|
@@ -55,27 +55,22 @@ export const metadata: Metadata = getMetadataOrDefault(
|
|
|
55
55
|
defaultMetadata
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
-
export default function RootLayout({
|
|
58
|
+
export default async function RootLayout({
|
|
59
59
|
children,
|
|
60
60
|
}: Readonly<{
|
|
61
61
|
children: React.ReactNode
|
|
62
62
|
}>) {
|
|
63
|
+
const billingHints = await getBillingResourceHints()
|
|
64
|
+
|
|
63
65
|
return (
|
|
64
66
|
<html lang={DEFAULT_LOCALE} suppressHydrationWarning>
|
|
65
67
|
<head>
|
|
66
|
-
{(() =>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
))}
|
|
73
|
-
{[...hints.preconnect, ...hints.dnsPrefetch].map((domain) => (
|
|
74
|
-
<link key={`dns-${domain}`} rel="dns-prefetch" href={domain} />
|
|
75
|
-
))}
|
|
76
|
-
</>
|
|
77
|
-
)
|
|
78
|
-
})()}
|
|
68
|
+
{billingHints.preconnect.map((domain) => (
|
|
69
|
+
<link key={`pre-${domain}`} rel="preconnect" href={domain} />
|
|
70
|
+
))}
|
|
71
|
+
{[...billingHints.preconnect, ...billingHints.dnsPrefetch].map((domain) => (
|
|
72
|
+
<link key={`dns-${domain}`} rel="dns-prefetch" href={domain} />
|
|
73
|
+
))}
|
|
79
74
|
</head>
|
|
80
75
|
<body
|
|
81
76
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
package/templates/app/layout.tsx
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { Metadata } from "next"
|
|
8
|
+
import { Suspense } from "react"
|
|
8
9
|
import { Geist, Geist_Mono } from "next/font/google"
|
|
9
10
|
import { NextIntlClientProvider } from 'next-intl'
|
|
10
11
|
import { getMessages } from 'next-intl/server'
|
|
@@ -13,11 +14,16 @@ import "./globals.css"
|
|
|
13
14
|
import { getBillingResourceHints } from "@nextsparkjs/core/lib/billing/gateways/factory"
|
|
14
15
|
import { ThemeProvider as NextThemeProvider } from "@nextsparkjs/core/providers/theme-provider"
|
|
15
16
|
import { ThemeProvider as CustomThemeProvider } from "@nextsparkjs/core/lib/theme/ThemeProvider"
|
|
17
|
+
import { Toaster } from "@nextsparkjs/core/components/ui/sonner"
|
|
18
|
+
import { QueryProvider } from "@nextsparkjs/core/providers/query-provider"
|
|
19
|
+
import { TeamProvider } from "@nextsparkjs/core/contexts/TeamContext"
|
|
20
|
+
import { SubscriptionProvider } from "@nextsparkjs/core/contexts/SubscriptionContext"
|
|
16
21
|
import { getUserLocale } from '@nextsparkjs/core/lib/locale'
|
|
17
22
|
import { TranslationContextManager } from "@nextsparkjs/core/providers/TranslationContextManager"
|
|
23
|
+
import { SessionCookieRefresher } from "@nextsparkjs/core/components/auth/SessionCookieRefresher"
|
|
18
24
|
import { PluginService } from '@nextsparkjs/core/lib/services'
|
|
19
25
|
import { getMetadataOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
20
|
-
import {
|
|
26
|
+
import { getThemeSettings } from '@nextsparkjs/core/lib/theme/get-default-theme-mode'
|
|
21
27
|
|
|
22
28
|
const geistSans = Geist({
|
|
23
29
|
variable: "--font-geist-sans",
|
|
@@ -53,24 +59,18 @@ export default async function RootLayout({
|
|
|
53
59
|
|
|
54
60
|
const locale = await getUserLocale()
|
|
55
61
|
const messages = await getMessages({ locale })
|
|
56
|
-
const
|
|
62
|
+
const { defaultMode, allowUserToggle, forcedThemeRoutes } = await getThemeSettings()
|
|
63
|
+
const billingHints = await getBillingResourceHints()
|
|
57
64
|
|
|
58
65
|
return (
|
|
59
66
|
<html lang={locale} suppressHydrationWarning>
|
|
60
67
|
<head>
|
|
61
|
-
{(() =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
))}
|
|
68
|
-
{[...hints.preconnect, ...hints.dnsPrefetch].map((domain) => (
|
|
69
|
-
<link key={`dns-${domain}`} rel="dns-prefetch" href={domain} />
|
|
70
|
-
))}
|
|
71
|
-
</>
|
|
72
|
-
)
|
|
73
|
-
})()}
|
|
68
|
+
{billingHints.preconnect.map((domain) => (
|
|
69
|
+
<link key={`pre-${domain}`} rel="preconnect" href={domain} />
|
|
70
|
+
))}
|
|
71
|
+
{[...billingHints.preconnect, ...billingHints.dnsPrefetch].map((domain) => (
|
|
72
|
+
<link key={`dns-${domain}`} rel="dns-prefetch" href={domain} />
|
|
73
|
+
))}
|
|
74
74
|
</head>
|
|
75
75
|
<body
|
|
76
76
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
@@ -78,13 +78,33 @@ export default async function RootLayout({
|
|
|
78
78
|
<NextIntlClientProvider locale={locale} messages={messages}>
|
|
79
79
|
<NextThemeProvider
|
|
80
80
|
attribute="class"
|
|
81
|
-
defaultTheme={
|
|
82
|
-
|
|
81
|
+
defaultTheme={defaultMode}
|
|
82
|
+
// When allowUserToggle is false, force the theme and ignore localStorage/system
|
|
83
|
+
forcedTheme={!allowUserToggle ? defaultMode : undefined}
|
|
84
|
+
// Only detect OS preference when theme configures defaultMode: 'system' AND user can toggle
|
|
85
|
+
enableSystem={allowUserToggle && defaultMode === 'system'}
|
|
86
|
+
// Force a theme on the routes declared in theme.config.ts (forcedThemeRoutes)
|
|
87
|
+
forcedThemeRoutes={forcedThemeRoutes}
|
|
83
88
|
disableTransitionOnChange
|
|
84
89
|
>
|
|
85
90
|
<CustomThemeProvider>
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
{/* Team providers live here (not only in DashboardProviders) so the activeTeamId
|
|
92
|
+
cookie is synced from any route, matching layout.ppr.tsx — see #115.
|
|
93
|
+
DashboardProviders skips re-mounting them when already present. */}
|
|
94
|
+
<QueryProvider>
|
|
95
|
+
<TeamProvider>
|
|
96
|
+
<SubscriptionProvider>
|
|
97
|
+
<TranslationContextManager />
|
|
98
|
+
{/* Real session-cookie renewal for installed PWAs: the render-time
|
|
99
|
+
session reads above (getUserLocale/getThemeSettings) cannot
|
|
100
|
+
write cookies, so the rolling refresh is triggered from the
|
|
101
|
+
client through the auth Route Handler instead. */}
|
|
102
|
+
<SessionCookieRefresher />
|
|
103
|
+
<main>{children}</main>
|
|
104
|
+
<Suspense><Toaster position="bottom-left" /></Suspense>
|
|
105
|
+
</SubscriptionProvider>
|
|
106
|
+
</TeamProvider>
|
|
107
|
+
</QueryProvider>
|
|
88
108
|
</CustomThemeProvider>
|
|
89
109
|
</NextThemeProvider>
|
|
90
110
|
</NextIntlClientProvider>
|
|
@@ -115,6 +115,25 @@ export const APP_CONFIG_OVERRIDES = {
|
|
|
115
115
|
enabled: false,
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
|
+
/**
|
|
119
|
+
* Login methods, in priority order. Core default = passwordless preset:
|
|
120
|
+
* a one-time code by email + Google, no password field (needs an email
|
|
121
|
+
* provider — RESEND_API_KEY — and GOOGLE_CLIENT_ID/SECRET for Google).
|
|
122
|
+
*
|
|
123
|
+
* Uncomment to switch to the classic email + password preset, or list both
|
|
124
|
+
* (the first email method is the form shown first):
|
|
125
|
+
*/
|
|
126
|
+
// methods: ['email-password', 'google'],
|
|
127
|
+
// methods: ['email-otp', 'email-password', 'google'],
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Session duration / renewal in seconds (defaults: 7 days, renewed daily).
|
|
131
|
+
* Long-lived sessions for an installed PWA, for example:
|
|
132
|
+
*/
|
|
133
|
+
// session: {
|
|
134
|
+
// expiresIn: 60 * 60 * 24 * 90, // 90 days
|
|
135
|
+
// updateAge: 60 * 60 * 24 * 7, // renew weekly
|
|
136
|
+
// },
|
|
118
137
|
/**
|
|
119
138
|
* Whether Better Auth automatically sends the verification email on signup.
|
|
120
139
|
*
|
|
@@ -22,6 +22,12 @@ export const starterThemeConfig: ThemeConfig = {
|
|
|
22
22
|
// ==========================================
|
|
23
23
|
plugins: [],
|
|
24
24
|
|
|
25
|
+
// ==========================================
|
|
26
|
+
// THEME MODE (Optional)
|
|
27
|
+
// ==========================================
|
|
28
|
+
// defaultMode: 'system', // 'light' | 'dark' | 'system'
|
|
29
|
+
// forcedThemeRoutes: { '/login': 'light' }, // force a theme on route prefixes (longest wins)
|
|
30
|
+
|
|
25
31
|
// ==========================================
|
|
26
32
|
// STYLES CONFIGURATION
|
|
27
33
|
// ==========================================
|
|
@@ -200,8 +200,12 @@
|
|
|
200
200
|
/* =============================================
|
|
201
201
|
BASE STYLES
|
|
202
202
|
============================================= */
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
/* Must live in @layer base: un-layered rules beat every Tailwind v4 utility
|
|
204
|
+
(border-primary, border-destructive, ...) regardless of specificity. */
|
|
205
|
+
@layer base {
|
|
206
|
+
* {
|
|
207
|
+
border-color: var(--border);
|
|
208
|
+
}
|
|
205
209
|
}
|
|
206
210
|
|
|
207
211
|
body {
|