@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
|
@@ -19,7 +19,21 @@
|
|
|
19
19
|
"submitHelp": "Pressione Enter ou clique para entrar",
|
|
20
20
|
"lastUsed": "Ultimo Usado",
|
|
21
21
|
"loginWithEmail": "Entrar com Email",
|
|
22
|
-
"backToGoogle": "Voltar para opcoes principais"
|
|
22
|
+
"backToGoogle": "Voltar para opcoes principais",
|
|
23
|
+
"useOtp": "Prefiro um código por email",
|
|
24
|
+
"usePassword": "Prefiro entrar com senha",
|
|
25
|
+
"otp": {
|
|
26
|
+
"emailHelp": "Sem senha: enviamos um código de uso único para o seu email.",
|
|
27
|
+
"sendCode": "Enviar um código de acesso",
|
|
28
|
+
"sendingCode": "Enviando código...",
|
|
29
|
+
"codeSent": "Enviamos um código de 6 dígitos para {email}. Ele expira em 5 minutos.",
|
|
30
|
+
"codeLabel": "Código de acesso",
|
|
31
|
+
"codePlaceholder": "123456",
|
|
32
|
+
"verifyCode": "Entrar com o código",
|
|
33
|
+
"verifyingCode": "Verificando código...",
|
|
34
|
+
"resendCode": "Reenviar código",
|
|
35
|
+
"changeEmail": "Usar outro email"
|
|
36
|
+
}
|
|
23
37
|
},
|
|
24
38
|
"footer": {
|
|
25
39
|
"noAccount": "Nao tem uma conta?",
|
|
@@ -34,7 +48,12 @@
|
|
|
34
48
|
"googleSigningIn": "Redirecionando para Google...",
|
|
35
49
|
"googleSignInSuccess": "Login com Google realizado com sucesso",
|
|
36
50
|
"googleSignInFailed": "Erro com Google",
|
|
37
|
-
"googleSignInError": "Erro com Google: {error}"
|
|
51
|
+
"googleSignInError": "Erro com Google: {error}",
|
|
52
|
+
"otpSending": "Enviando código de acesso...",
|
|
53
|
+
"otpSent": "Código enviado. Verifique sua caixa de entrada.",
|
|
54
|
+
"otpSendFailed": "Não foi possível enviar o código de acesso",
|
|
55
|
+
"otpVerifying": "Verificando código...",
|
|
56
|
+
"otpVerifyFailed": "Não foi possível verificar o código"
|
|
38
57
|
},
|
|
39
58
|
"errors": {
|
|
40
59
|
"invalidCredentials": "Email ou senha invalidos",
|
|
@@ -46,6 +65,10 @@
|
|
|
46
65
|
"serverError": "Erro do servidor. Por favor tente novamente mais tarde",
|
|
47
66
|
"invalidEmail": "Email invalido",
|
|
48
67
|
"passwordTooShort": "A senha deve ter pelo menos 6 caracteres",
|
|
68
|
+
"otpInvalidCode": "Digite o código de 6 dígitos do seu email",
|
|
69
|
+
"otpInvalid": "Código inválido. Confira e tente de novo",
|
|
70
|
+
"otpExpired": "Este código expirou. Peça um novo",
|
|
71
|
+
"otpTooMany": "Muitas tentativas. Peça um novo código",
|
|
49
72
|
"google": {
|
|
50
73
|
"invalidCredentials": "Erro de autenticacao com Google",
|
|
51
74
|
"userNotFound": "Nenhuma conta encontrada para este usuario do Google",
|
|
@@ -248,6 +248,20 @@ declare const _default: {
|
|
|
248
248
|
lastUsed: string;
|
|
249
249
|
loginWithEmail: string;
|
|
250
250
|
backToGoogle: string;
|
|
251
|
+
useOtp: string;
|
|
252
|
+
usePassword: string;
|
|
253
|
+
otp: {
|
|
254
|
+
emailHelp: string;
|
|
255
|
+
sendCode: string;
|
|
256
|
+
sendingCode: string;
|
|
257
|
+
codeSent: string;
|
|
258
|
+
codeLabel: string;
|
|
259
|
+
codePlaceholder: string;
|
|
260
|
+
verifyCode: string;
|
|
261
|
+
verifyingCode: string;
|
|
262
|
+
resendCode: string;
|
|
263
|
+
changeEmail: string;
|
|
264
|
+
};
|
|
251
265
|
};
|
|
252
266
|
footer: {
|
|
253
267
|
noAccount: string;
|
|
@@ -263,6 +277,11 @@ declare const _default: {
|
|
|
263
277
|
googleSignInSuccess: string;
|
|
264
278
|
googleSignInFailed: string;
|
|
265
279
|
googleSignInError: string;
|
|
280
|
+
otpSending: string;
|
|
281
|
+
otpSent: string;
|
|
282
|
+
otpSendFailed: string;
|
|
283
|
+
otpVerifying: string;
|
|
284
|
+
otpVerifyFailed: string;
|
|
266
285
|
};
|
|
267
286
|
errors: {
|
|
268
287
|
invalidCredentials: string;
|
|
@@ -274,6 +293,10 @@ declare const _default: {
|
|
|
274
293
|
serverError: string;
|
|
275
294
|
invalidEmail: string;
|
|
276
295
|
passwordTooShort: string;
|
|
296
|
+
otpInvalidCode: string;
|
|
297
|
+
otpInvalid: string;
|
|
298
|
+
otpExpired: string;
|
|
299
|
+
otpTooMany: string;
|
|
277
300
|
google: {
|
|
278
301
|
invalidCredentials: string;
|
|
279
302
|
userNotFound: string;
|
|
@@ -1131,6 +1154,64 @@ declare const _default: {
|
|
|
1131
1154
|
description: string;
|
|
1132
1155
|
};
|
|
1133
1156
|
};
|
|
1157
|
+
readonly email: {
|
|
1158
|
+
verifyEmail: {
|
|
1159
|
+
subject: string;
|
|
1160
|
+
headerSubtitle: string;
|
|
1161
|
+
title: string;
|
|
1162
|
+
greetingPrefix: string;
|
|
1163
|
+
intro: string;
|
|
1164
|
+
buttonLabel: string;
|
|
1165
|
+
helpHeading: string;
|
|
1166
|
+
helpInstructions: string;
|
|
1167
|
+
copyright: string;
|
|
1168
|
+
footerLine1: string;
|
|
1169
|
+
footerLine2: string;
|
|
1170
|
+
};
|
|
1171
|
+
resetPassword: {
|
|
1172
|
+
subject: string;
|
|
1173
|
+
headerSubtitle: string;
|
|
1174
|
+
title: string;
|
|
1175
|
+
greetingPrefix: string;
|
|
1176
|
+
intro: string;
|
|
1177
|
+
buttonLabel: string;
|
|
1178
|
+
securityNoticeTitle: string;
|
|
1179
|
+
securityNoticeExpiry: string;
|
|
1180
|
+
securityNoticeIgnore: string;
|
|
1181
|
+
defaultExpiresIn: string;
|
|
1182
|
+
helpHeading: string;
|
|
1183
|
+
helpInstructions: string;
|
|
1184
|
+
copyright: string;
|
|
1185
|
+
footerLine1: string;
|
|
1186
|
+
footerLine2: string;
|
|
1187
|
+
};
|
|
1188
|
+
otpVerification: {
|
|
1189
|
+
subject: string;
|
|
1190
|
+
headerSubtitle: string;
|
|
1191
|
+
title: string;
|
|
1192
|
+
intro: string;
|
|
1193
|
+
expiresNotice: string;
|
|
1194
|
+
ignoreNotice: string;
|
|
1195
|
+
copyright: string;
|
|
1196
|
+
footerLine1: string;
|
|
1197
|
+
};
|
|
1198
|
+
teamInvitation: {
|
|
1199
|
+
subject: string;
|
|
1200
|
+
headerSubtitle: string;
|
|
1201
|
+
title: string;
|
|
1202
|
+
bodyIntro: string;
|
|
1203
|
+
bodyAction: string;
|
|
1204
|
+
buttonLabel: string;
|
|
1205
|
+
detailsTitle: string;
|
|
1206
|
+
detailsExpiry: string;
|
|
1207
|
+
detailsCreateAccount: string;
|
|
1208
|
+
helpHeading: string;
|
|
1209
|
+
helpInstructions: string;
|
|
1210
|
+
copyright: string;
|
|
1211
|
+
footerSentTo: string;
|
|
1212
|
+
footerUnexpected: string;
|
|
1213
|
+
};
|
|
1214
|
+
};
|
|
1134
1215
|
readonly entities: {
|
|
1135
1216
|
actions: {
|
|
1136
1217
|
create: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/pt/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/pt/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,wBAqBU"}
|
|
@@ -6,6 +6,7 @@ import common from "./common.json";
|
|
|
6
6
|
import dashboard from "./dashboard.json";
|
|
7
7
|
import devtools from "./devtools.json";
|
|
8
8
|
import docs from "./docs.json";
|
|
9
|
+
import email from "./email.json";
|
|
9
10
|
import entities from "./entities.json";
|
|
10
11
|
import footer from "./footer.json";
|
|
11
12
|
import home from "./home.json";
|
|
@@ -26,6 +27,7 @@ var pt_default = {
|
|
|
26
27
|
dashboard,
|
|
27
28
|
devtools,
|
|
28
29
|
docs,
|
|
30
|
+
email,
|
|
29
31
|
entities,
|
|
30
32
|
footer,
|
|
31
33
|
home,
|
|
@@ -6,6 +6,7 @@ import common from './common.json'
|
|
|
6
6
|
import dashboard from './dashboard.json'
|
|
7
7
|
import devtools from './devtools.json'
|
|
8
8
|
import docs from './docs.json'
|
|
9
|
+
import email from './email.json'
|
|
9
10
|
import entities from './entities.json'
|
|
10
11
|
import footer from './footer.json'
|
|
11
12
|
import home from './home.json'
|
|
@@ -27,6 +28,7 @@ export default {
|
|
|
27
28
|
dashboard,
|
|
28
29
|
devtools,
|
|
29
30
|
docs,
|
|
31
|
+
email,
|
|
30
32
|
entities,
|
|
31
33
|
footer,
|
|
32
34
|
home,
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
CREATE TABLE IF NOT EXISTS "api_audit_log" (
|
|
5
5
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
6
|
-
|
|
6
|
+
-- NULL for session-authenticated requests (see 025_api_audit_log_nullable_api_key.sql)
|
|
7
|
+
"apiKeyId" TEXT REFERENCES public."api_key"(id) ON DELETE CASCADE,
|
|
7
8
|
"userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
|
|
8
9
|
endpoint TEXT NOT NULL,
|
|
9
10
|
method TEXT NOT NULL,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- Migration: 025_api_audit_log_nullable_api_key.sql
|
|
2
|
+
-- Description: Allow api_audit_log rows without an API key (#105)
|
|
3
|
+
--
|
|
4
|
+
-- The generic entity routes (/api/v1/[entity], /api/v1/[entity]/[id]) now
|
|
5
|
+
-- write an audit row for every authenticated request, whether it was
|
|
6
|
+
-- authenticated with an API key or with a browser session. Session requests
|
|
7
|
+
-- have no key, so "apiKeyId" must accept NULL. The foreign key stays as-is:
|
|
8
|
+
-- a FK only constrains non-null values.
|
|
9
|
+
--
|
|
10
|
+
-- Idempotent: DROP NOT NULL on an already-nullable column is a no-op.
|
|
11
|
+
|
|
12
|
+
ALTER TABLE "api_audit_log" ALTER COLUMN "apiKeyId" DROP NOT NULL;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
-- Migration: 026_login_events.sql
|
|
2
|
+
-- Description: login_events — per-user device/login history for built-in
|
|
3
|
+
-- security email notifications (new-device login alerts).
|
|
4
|
+
-- Date: 2026-09-01
|
|
5
|
+
-- Issue: #75
|
|
6
|
+
--
|
|
7
|
+
-- Why 026 (and not 022 as the issue text proposed, nor 025):
|
|
8
|
+
-- The issue was written when the migration sequence ended earlier. By the
|
|
9
|
+
-- time this branch was authored the sequence already went through
|
|
10
|
+
-- 024_teams_soft_delete.sql (022 and 023 are taken by rls_runtime_roles and
|
|
11
|
+
-- the invitations index), so this migration was originally 025. It was
|
|
12
|
+
-- renumbered to 026 when merging to main, because 025 had in the meantime
|
|
13
|
+
-- been taken by 025_api_audit_log_nullable_api_key.sql (issue #105).
|
|
14
|
+
--
|
|
15
|
+
-- What this backs:
|
|
16
|
+
-- The security-notifications dispatcher records one row per successful sign-in
|
|
17
|
+
-- with a device fingerprint = sha256(normalizedUA | ip/24). A sign-in whose
|
|
18
|
+
-- (userId, deviceFingerprint) pair has never been seen is flagged isNew=true
|
|
19
|
+
-- and triggers a "new device" email via the auth:security-notification
|
|
20
|
+
-- scheduled action. Known fingerprints are still logged (isNew=false) but send
|
|
21
|
+
-- no email. The table is also the substrate for a future login-history UI
|
|
22
|
+
-- (out of scope here — see issue #75 "Out of scope").
|
|
23
|
+
--
|
|
24
|
+
-- Append-only: rows are never updated, so there is no "updatedAt"/set_updated_at
|
|
25
|
+
-- trigger (unlike mutable entity tables). city/country are nullable for a future
|
|
26
|
+
-- geolocation enhancement (also out of scope).
|
|
27
|
+
|
|
28
|
+
-- ============================================
|
|
29
|
+
-- TABLE
|
|
30
|
+
-- ============================================
|
|
31
|
+
DROP TABLE IF EXISTS public."login_events" CASCADE;
|
|
32
|
+
|
|
33
|
+
CREATE TABLE IF NOT EXISTS public."login_events" (
|
|
34
|
+
-- Primary Key (TEXT not UUID — Better Auth convention)
|
|
35
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
36
|
+
|
|
37
|
+
-- Relational Fields
|
|
38
|
+
"userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
|
|
39
|
+
|
|
40
|
+
-- Entity-specific fields
|
|
41
|
+
"ipAddress" TEXT,
|
|
42
|
+
"userAgent" TEXT,
|
|
43
|
+
"deviceFingerprint" TEXT NOT NULL,
|
|
44
|
+
city TEXT,
|
|
45
|
+
country TEXT,
|
|
46
|
+
"isNew" BOOLEAN NOT NULL DEFAULT false,
|
|
47
|
+
|
|
48
|
+
-- System fields (append-only: created only, never updated)
|
|
49
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
COMMENT ON TABLE public."login_events" IS 'Per-user login/device history backing built-in new-device security email notifications (issue #75).';
|
|
53
|
+
COMMENT ON COLUMN public."login_events"."userId" IS 'Owner of the login event.';
|
|
54
|
+
COMMENT ON COLUMN public."login_events"."ipAddress" IS 'Best-effort client IP captured at sign-in (may be a proxy address).';
|
|
55
|
+
COMMENT ON COLUMN public."login_events"."userAgent" IS 'Raw User-Agent header captured at sign-in.';
|
|
56
|
+
COMMENT ON COLUMN public."login_events"."deviceFingerprint" IS 'sha256(normalizedUA | ip/24). Stable across patch-version UA churn and within a /24 network so routine re-logins from the same device do not re-alert.';
|
|
57
|
+
COMMENT ON COLUMN public."login_events".city IS 'Reserved for future geolocation enrichment (nullable, unused today).';
|
|
58
|
+
COMMENT ON COLUMN public."login_events".country IS 'Reserved for future geolocation enrichment (nullable, unused today).';
|
|
59
|
+
COMMENT ON COLUMN public."login_events"."isNew" IS 'True when this (userId, deviceFingerprint) pair had not been seen before this row → a new-device email was queued.';
|
|
60
|
+
|
|
61
|
+
-- ============================================
|
|
62
|
+
-- INDEXES
|
|
63
|
+
-- ============================================
|
|
64
|
+
-- Most-recent-first history lookups per user (login-history UI, TTL checks).
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_login_events_user_created
|
|
66
|
+
ON public."login_events" ("userId", "createdAt" DESC);
|
|
67
|
+
|
|
68
|
+
-- "Have we seen this fingerprint for this user?" — the new-device decision.
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_login_events_user_fingerprint
|
|
70
|
+
ON public."login_events" ("userId", "deviceFingerprint");
|
|
71
|
+
|
|
72
|
+
-- ============================================
|
|
73
|
+
-- RLS
|
|
74
|
+
-- ============================================
|
|
75
|
+
-- A user may read ONLY their own login events; superadmin/developer bypass via
|
|
76
|
+
-- can_bypass_rls() (defined in 001). Writes are service-only: the dispatcher
|
|
77
|
+
-- inserts with no user GUC (service/bypass pool), matching how session/account
|
|
78
|
+
-- rows are written in 002. Mirrors the "self read + service write" pattern.
|
|
79
|
+
ALTER TABLE public."login_events" ENABLE ROW LEVEL SECURITY;
|
|
80
|
+
|
|
81
|
+
DROP POLICY IF EXISTS "Login events self read" ON public."login_events";
|
|
82
|
+
DROP POLICY IF EXISTS "Login events service write" ON public."login_events";
|
|
83
|
+
|
|
84
|
+
CREATE POLICY "Login events self read" ON public."login_events"
|
|
85
|
+
FOR SELECT TO authenticated
|
|
86
|
+
USING (public.can_bypass_rls() OR "userId" = public.get_auth_user_id());
|
|
87
|
+
|
|
88
|
+
CREATE POLICY "Login events service write" ON public."login_events"
|
|
89
|
+
FOR ALL TO authenticated
|
|
90
|
+
USING (public.can_bypass_rls())
|
|
91
|
+
WITH CHECK (public.can_bypass_rls());
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
-- Migration: 027_rls_bypass_developer_and_system_admin_team.sql
|
|
2
|
+
-- Description: Resolve the System Admin Team by a metadata flag instead of a
|
|
3
|
+
-- hardcoded team id, auto-enroll/unenroll developer/superadmin
|
|
4
|
+
-- users via a trigger, and fix 4 RLS policies that hardcoded
|
|
5
|
+
-- their own role check instead of calling can_bypass_rls().
|
|
6
|
+
-- Date: 2026-09-03
|
|
7
|
+
-- Issues: #108, #102
|
|
8
|
+
--
|
|
9
|
+
-- Why both issues in one migration: #102's fix (make a policy call
|
|
10
|
+
-- can_bypass_rls()) only makes 'developer' bypass actually reachable in a
|
|
11
|
+
-- real (non-seeded) deployment once #108's enrollment mechanism exists —
|
|
12
|
+
-- fixing one without the other leaves the combination still broken.
|
|
13
|
+
--
|
|
14
|
+
-- #108 — System Admin Team enrollment:
|
|
15
|
+
-- can_bypass_rls() grants 'developer' a conditional bypass: membership in
|
|
16
|
+
-- the System Admin Team. Nothing ever enrolled anyone into it outside of
|
|
17
|
+
-- 090_sample_data.sql's explicit INSERTs, and the team id was a literal
|
|
18
|
+
-- ('team-nextspark-001') duplicated in this function AND in
|
|
19
|
+
-- dual-auth.ts's SYSTEM_ADMIN_TEAM_ID constant. This migration:
|
|
20
|
+
-- 1. Resolves the System Admin Team by `teams.metadata.systemAdmin =
|
|
21
|
+
-- true` instead of a literal id — 090_sample_data.sql's team already
|
|
22
|
+
-- carries this flag, so seeded deployments keep working unchanged;
|
|
23
|
+
-- a real deployment flags its own team, no code change needed.
|
|
24
|
+
-- 2. Adds a trigger on users(role) that auto-enrolls a user into that
|
|
25
|
+
-- team (as 'owner' for superadmin, 'admin' for developer) when their
|
|
26
|
+
-- role becomes bypass-eligible, and removes the membership when it
|
|
27
|
+
-- stops being eligible. Framework-level (a Postgres trigger fires
|
|
28
|
+
-- regardless of which application code performed the UPDATE) rather
|
|
29
|
+
-- than an app-level hook, because no Server Action in core actually
|
|
30
|
+
-- assigns users.role today — the only code that does lives in
|
|
31
|
+
-- apps/dev, reimplementable by any consumer of this framework.
|
|
32
|
+
-- 3. Backfills existing developer/superadmin users into the team, for
|
|
33
|
+
-- deployments that already have such users predating this trigger.
|
|
34
|
+
-- Deliberately NOT included here (separate, larger concern — see #108):
|
|
35
|
+
-- POST /api/v1/users creating a "half" user (no account/team/subscription
|
|
36
|
+
-- row). That depends on patterns from the not-yet-approved #98 MCP work.
|
|
37
|
+
--
|
|
38
|
+
-- #102 (+ 3 more found while auditing the same pattern) — RLS policies that
|
|
39
|
+
-- hardcoded `role IN ('admin','superadmin')` or `role = 'superadmin'`
|
|
40
|
+
-- instead of calling can_bypass_rls() have drifted from it in both
|
|
41
|
+
-- directions. 'admin' is NOT a valid users.role value (see check_users_role
|
|
42
|
+
-- in 002_auth_tables.sql: only 'member' | 'superadmin' | 'developer'), so
|
|
43
|
+
-- that branch never matched any real row — removing it changes nothing for
|
|
44
|
+
-- existing behavior. All four also silently excluded 'developer', who
|
|
45
|
+
-- can_bypass_rls() already grants conditional bypass everywhere else.
|
|
46
|
+
-- Switching to can_bypass_rls() is a pure widening for 'developer' and an
|
|
47
|
+
-- exact match for 'superadmin' — no regression, only fixes a gap:
|
|
48
|
+
-- - users_metas_owner_or_admin_all (003_user_metas.sql)
|
|
49
|
+
-- - "API key owner or admin can do all" (004_api_key.sql)
|
|
50
|
+
-- - "API logs superadmin can select" (005_api_audit_log.sql)
|
|
51
|
+
-- - "Plans superadmin write" (012_billing_plans.sql)
|
|
52
|
+
|
|
53
|
+
-- ============================================
|
|
54
|
+
-- #108.1 — can_bypass_rls(): resolve System Admin Team by metadata flag
|
|
55
|
+
-- ============================================
|
|
56
|
+
|
|
57
|
+
CREATE OR REPLACE FUNCTION public.can_bypass_rls()
|
|
58
|
+
RETURNS BOOLEAN AS $$
|
|
59
|
+
DECLARE
|
|
60
|
+
current_user_id TEXT;
|
|
61
|
+
user_role TEXT;
|
|
62
|
+
admin_team_id TEXT;
|
|
63
|
+
is_system_admin_member BOOLEAN;
|
|
64
|
+
BEGIN
|
|
65
|
+
current_user_id := public.get_auth_user_id();
|
|
66
|
+
|
|
67
|
+
SELECT role INTO user_role
|
|
68
|
+
FROM public."users"
|
|
69
|
+
WHERE id = current_user_id;
|
|
70
|
+
|
|
71
|
+
IF user_role = 'superadmin' THEN
|
|
72
|
+
RETURN TRUE;
|
|
73
|
+
END IF;
|
|
74
|
+
|
|
75
|
+
IF user_role = 'developer' THEN
|
|
76
|
+
-- The System Admin Team is whichever team has metadata.systemAdmin =
|
|
77
|
+
-- true, not a hardcoded id — each deployment can flag its own team.
|
|
78
|
+
SELECT id INTO admin_team_id
|
|
79
|
+
FROM public."teams"
|
|
80
|
+
WHERE (metadata->>'systemAdmin')::boolean IS TRUE
|
|
81
|
+
LIMIT 1;
|
|
82
|
+
|
|
83
|
+
IF admin_team_id IS NULL THEN
|
|
84
|
+
RETURN FALSE;
|
|
85
|
+
END IF;
|
|
86
|
+
|
|
87
|
+
SELECT EXISTS(
|
|
88
|
+
SELECT 1 FROM public."team_members"
|
|
89
|
+
WHERE "userId" = current_user_id
|
|
90
|
+
AND "teamId" = admin_team_id
|
|
91
|
+
) INTO is_system_admin_member;
|
|
92
|
+
|
|
93
|
+
RETURN is_system_admin_member;
|
|
94
|
+
END IF;
|
|
95
|
+
|
|
96
|
+
RETURN FALSE;
|
|
97
|
+
END;
|
|
98
|
+
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = public;
|
|
99
|
+
|
|
100
|
+
-- ============================================
|
|
101
|
+
-- #108.2 — auto-enroll/unenroll on role change
|
|
102
|
+
-- ============================================
|
|
103
|
+
|
|
104
|
+
CREATE OR REPLACE FUNCTION public.sync_system_admin_team_membership()
|
|
105
|
+
RETURNS TRIGGER
|
|
106
|
+
LANGUAGE plpgsql
|
|
107
|
+
SECURITY DEFINER
|
|
108
|
+
SET search_path = public
|
|
109
|
+
AS $$
|
|
110
|
+
DECLARE
|
|
111
|
+
admin_team_id TEXT;
|
|
112
|
+
new_team_role TEXT;
|
|
113
|
+
BEGIN
|
|
114
|
+
SELECT id INTO admin_team_id
|
|
115
|
+
FROM public."teams"
|
|
116
|
+
WHERE (metadata->>'systemAdmin')::boolean IS TRUE
|
|
117
|
+
LIMIT 1;
|
|
118
|
+
|
|
119
|
+
-- No team flagged as the System Admin Team in this deployment yet —
|
|
120
|
+
-- nothing to enroll into. Not an error: same as no bypass being possible.
|
|
121
|
+
IF admin_team_id IS NULL THEN
|
|
122
|
+
RETURN NEW;
|
|
123
|
+
END IF;
|
|
124
|
+
|
|
125
|
+
IF NEW.role IN ('developer', 'superadmin') THEN
|
|
126
|
+
new_team_role := CASE WHEN NEW.role = 'superadmin' THEN 'owner' ELSE 'admin' END;
|
|
127
|
+
|
|
128
|
+
INSERT INTO public."team_members" ("teamId", "userId", role)
|
|
129
|
+
VALUES (admin_team_id, NEW.id, new_team_role)
|
|
130
|
+
ON CONFLICT ("teamId", "userId") DO UPDATE SET role = EXCLUDED.role;
|
|
131
|
+
|
|
132
|
+
ELSIF TG_OP = 'UPDATE' AND OLD.role IN ('developer', 'superadmin') THEN
|
|
133
|
+
-- Role changed away from a bypass-eligible tier: revoke membership.
|
|
134
|
+
DELETE FROM public."team_members"
|
|
135
|
+
WHERE "teamId" = admin_team_id AND "userId" = NEW.id;
|
|
136
|
+
END IF;
|
|
137
|
+
|
|
138
|
+
RETURN NEW;
|
|
139
|
+
END;
|
|
140
|
+
$$;
|
|
141
|
+
|
|
142
|
+
DROP TRIGGER IF EXISTS trg_sync_system_admin_team_membership ON public."users";
|
|
143
|
+
CREATE TRIGGER trg_sync_system_admin_team_membership
|
|
144
|
+
AFTER INSERT OR UPDATE OF role ON public."users"
|
|
145
|
+
FOR EACH ROW
|
|
146
|
+
EXECUTE FUNCTION public.sync_system_admin_team_membership();
|
|
147
|
+
|
|
148
|
+
-- ============================================
|
|
149
|
+
-- #108.3 — backfill existing developer/superadmin users
|
|
150
|
+
-- ============================================
|
|
151
|
+
|
|
152
|
+
DO $$
|
|
153
|
+
DECLARE
|
|
154
|
+
admin_team_id TEXT;
|
|
155
|
+
BEGIN
|
|
156
|
+
SELECT id INTO admin_team_id
|
|
157
|
+
FROM public."teams"
|
|
158
|
+
WHERE (metadata->>'systemAdmin')::boolean IS TRUE
|
|
159
|
+
LIMIT 1;
|
|
160
|
+
|
|
161
|
+
IF admin_team_id IS NOT NULL THEN
|
|
162
|
+
INSERT INTO public."team_members" ("teamId", "userId", role)
|
|
163
|
+
SELECT admin_team_id, u.id, CASE WHEN u.role = 'superadmin' THEN 'owner' ELSE 'admin' END
|
|
164
|
+
FROM public."users" u
|
|
165
|
+
WHERE u.role IN ('developer', 'superadmin')
|
|
166
|
+
ON CONFLICT ("teamId", "userId") DO UPDATE SET role = EXCLUDED.role;
|
|
167
|
+
END IF;
|
|
168
|
+
END $$;
|
|
169
|
+
|
|
170
|
+
-- ============================================
|
|
171
|
+
-- #102 + siblings — switch drifted policies to can_bypass_rls()
|
|
172
|
+
-- ============================================
|
|
173
|
+
|
|
174
|
+
DROP POLICY IF EXISTS "users_metas_owner_or_admin_all" ON "users_metas";
|
|
175
|
+
CREATE POLICY "users_metas_owner_or_admin_all" ON "users_metas"
|
|
176
|
+
FOR ALL
|
|
177
|
+
TO authenticated
|
|
178
|
+
USING ("userId" = public.get_auth_user_id() OR public.can_bypass_rls())
|
|
179
|
+
WITH CHECK ("userId" = public.get_auth_user_id() OR public.can_bypass_rls());
|
|
180
|
+
|
|
181
|
+
DROP POLICY IF EXISTS "API key owner or admin can do all" ON "api_key";
|
|
182
|
+
CREATE POLICY "API key owner or admin can do all" ON "api_key"
|
|
183
|
+
FOR ALL TO authenticated
|
|
184
|
+
USING ("userId" = public.get_auth_user_id() OR public.can_bypass_rls())
|
|
185
|
+
WITH CHECK ("userId" = public.get_auth_user_id() OR public.can_bypass_rls());
|
|
186
|
+
|
|
187
|
+
DROP POLICY IF EXISTS "API logs superadmin can select" ON "api_audit_log";
|
|
188
|
+
CREATE POLICY "API logs superadmin can select" ON "api_audit_log"
|
|
189
|
+
FOR SELECT TO authenticated
|
|
190
|
+
USING (public.can_bypass_rls());
|
|
191
|
+
|
|
192
|
+
DROP POLICY IF EXISTS "Plans superadmin write" ON public."plans";
|
|
193
|
+
CREATE POLICY "Plans superadmin write"
|
|
194
|
+
ON public."plans"
|
|
195
|
+
FOR ALL TO authenticated
|
|
196
|
+
USING (public.can_bypass_rls())
|
|
197
|
+
WITH CHECK (public.can_bypass_rls());
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DashboardProviders — Client providers needed
|
|
2
|
+
* DashboardProviders — Client providers needed by authenticated routes.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Since #115 the generated root layout mounts QueryProvider, TeamProvider,
|
|
5
|
+
* SubscriptionProvider and Toaster itself (matching layout.ppr.tsx) so the
|
|
6
|
+
* activeTeamId cookie is synced from any route, not only from the dashboard.
|
|
7
|
+
* When a TeamProvider is already mounted above, this component renders its
|
|
8
|
+
* children as-is: nesting a second TeamProvider would duplicate the teams
|
|
9
|
+
* fetch, the cookie sync and the TeamSwitchModal, and a second Toaster would
|
|
10
|
+
* show every toast twice.
|
|
11
|
+
*
|
|
12
|
+
* The full provider stack is kept as a fallback for apps whose generated root
|
|
13
|
+
* layout predates that change (it originally lived here to keep ~500KB of
|
|
14
|
+
* client JS off public landing pages).
|
|
6
15
|
*
|
|
7
16
|
* Used by: dashboard/layout.tsx, superadmin/layout.tsx, devtools/layout.tsx
|
|
8
17
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardProviders.d.ts","sourceRoot":"","sources":["../../src/providers/DashboardProviders.tsx"],"names":[],"mappings":"AAQA
|
|
1
|
+
{"version":3,"file":"DashboardProviders.d.ts","sourceRoot":"","sources":["../../src/providers/DashboardProviders.tsx"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAiB7E"}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Suspense } from "react";
|
|
4
4
|
import { QueryProvider } from "./query-provider.js";
|
|
5
|
-
import { TeamProvider } from "../contexts/TeamContext.js";
|
|
5
|
+
import { TeamProvider, useOptionalTeamContext } from "../contexts/TeamContext.js";
|
|
6
6
|
import { SubscriptionProvider } from "../contexts/SubscriptionContext.js";
|
|
7
7
|
import { Toaster } from "../components/ui/sonner.js";
|
|
8
8
|
function DashboardProviders({ children }) {
|
|
9
|
+
const rootTeamContext = useOptionalTeamContext();
|
|
10
|
+
if (rootTeamContext) {
|
|
11
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
12
|
+
}
|
|
9
13
|
return /* @__PURE__ */ jsx(QueryProvider, { children: /* @__PURE__ */ jsx(TeamProvider, { children: /* @__PURE__ */ jsxs(SubscriptionProvider, { children: [
|
|
10
14
|
children,
|
|
11
15
|
/* @__PURE__ */ jsx(Suspense, { children: /* @__PURE__ */ jsx(Toaster, { position: "bottom-left" }) })
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
import { type ThemeProviderProps } from "next-themes";
|
|
2
|
-
|
|
1
|
+
import { type ThemeProviderProps as NextThemesProviderProps } from "next-themes";
|
|
2
|
+
import { type ForcedThemeRoutes } from "../lib/theme/forced-theme";
|
|
3
|
+
export interface ThemeProviderProps extends NextThemesProviderProps {
|
|
4
|
+
/**
|
|
5
|
+
* Routes whose theme is forced regardless of user/system preference
|
|
6
|
+
* (`theme.config.ts` → `forcedThemeRoutes`).
|
|
7
|
+
*
|
|
8
|
+
* Resolved here, on the client, against `usePathname()` instead of once in
|
|
9
|
+
* the root layout from request headers: the root layout does not re-render
|
|
10
|
+
* on client-side navigations, so a server-computed value would go stale as
|
|
11
|
+
* soon as the user navigated into or out of a forced route. On the initial
|
|
12
|
+
* request this component is still server-rendered, so next-themes' blocking
|
|
13
|
+
* script receives the forced value and there is no flash.
|
|
14
|
+
*
|
|
15
|
+
* Takes precedence over the plain `forcedTheme` prop.
|
|
16
|
+
*/
|
|
17
|
+
forcedThemeRoutes?: ForcedThemeRoutes;
|
|
18
|
+
}
|
|
19
|
+
export declare function ThemeProvider({ children, forcedThemeRoutes, forcedTheme, ...props }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
3
20
|
//# sourceMappingURL=theme-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/providers/theme-provider.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/providers/theme-provider.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,kBAAkB,IAAI,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAChF,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAEtF,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IACjE;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACtC;AAED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,EAAE,kBAAkB,2CASvG"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
3
4
|
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
import { resolveForcedTheme } from "../lib/theme/forced-theme.js";
|
|
6
|
+
function ThemeProvider({ children, forcedThemeRoutes, forcedTheme, ...props }) {
|
|
7
|
+
const pathname = usePathname();
|
|
8
|
+
const routeForcedTheme = resolveForcedTheme(pathname, forcedThemeRoutes);
|
|
9
|
+
return /* @__PURE__ */ jsx(NextThemesProvider, { ...props, forcedTheme: routeForcedTheme ?? forcedTheme, children });
|
|
6
10
|
}
|
|
7
11
|
export {
|
|
8
12
|
ThemeProvider
|
|
@@ -4,6 +4,7 @@ import { redirect } from 'next/navigation'
|
|
|
4
4
|
import { SignupForm } from '@nextsparkjs/core/components/auth/forms/SignupForm'
|
|
5
5
|
import { getTemplateOrDefault, getMetadataOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
6
6
|
import { AUTH_CONFIG } from '@nextsparkjs/core/lib/config'
|
|
7
|
+
import { resolveAuthMethods } from '@nextsparkjs/core/lib/auth/auth-methods'
|
|
7
8
|
import { TeamService } from '@nextsparkjs/core/lib/services'
|
|
8
9
|
|
|
9
10
|
// This page reads DB state (TeamService.hasGlobal) and redirect()s during render,
|
|
@@ -23,6 +24,13 @@ export const metadata: Metadata = getMetadataOrDefault(
|
|
|
23
24
|
async function SignupPageContent() {
|
|
24
25
|
const registrationMode = AUTH_CONFIG?.registration?.mode ?? 'open'
|
|
25
26
|
|
|
27
|
+
// Passwordless preset (no 'email-password' in auth.methods): the account is
|
|
28
|
+
// created by the first one-time-code sign-in, so there is no password signup
|
|
29
|
+
// form to show — send people to /login instead.
|
|
30
|
+
if (!resolveAuthMethods(AUTH_CONFIG).includes('email-password')) {
|
|
31
|
+
redirect('/login')
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
// In invitation-only mode, allow the first user to register
|
|
27
35
|
// (when no global team exists yet). Subsequent users need invitations.
|
|
28
36
|
if (registrationMode === 'invitation-only') {
|