@nextsparkjs/core 0.1.0-beta.185 → 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 +29 -0
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +24 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts +22 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
- package/dist/hooks/useEnsureUserMetadata.js +18 -5
- package/dist/hooks/useSessionCookieRefresh.d.ts +35 -0
- package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -0
- package/dist/hooks/useSessionCookieRefresh.js +49 -0
- package/dist/lib/api/api-error.d.ts +3 -1
- package/dist/lib/api/api-error.d.ts.map +1 -1
- package/dist/lib/api/api-error.js +4 -2
- package/dist/lib/api/auth/dual-auth.d.ts +38 -3
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +42 -7
- package/dist/lib/api/auth/permissions.d.ts +7 -2
- package/dist/lib/api/auth/permissions.d.ts.map +1 -1
- package/dist/lib/api/auth/permissions.js +2 -2
- package/dist/lib/api/auth/scope-policy.d.ts +71 -0
- package/dist/lib/api/auth/scope-policy.d.ts.map +1 -0
- package/dist/lib/api/auth/scope-policy.js +53 -0
- package/dist/lib/api/auth.d.ts +15 -0
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +17 -4
- package/dist/lib/api/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 +315 -134
- 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 +1639 -0
- package/dist/lib/auth-client.d.ts.map +1 -0
- package/dist/lib/auth-client.js +4 -0
- 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/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 +8 -4
- package/dist/lib/mcp/audit.d.ts.map +1 -1
- package/dist/lib/plugins/hook-system.d.ts +13 -0
- package/dist/lib/plugins/hook-system.d.ts.map +1 -1
- package/dist/lib/plugins/hook-system.js +32 -2
- package/dist/lib/scheduled-actions/handlers/index.d.ts +1 -0
- package/dist/lib/scheduled-actions/handlers/index.d.ts.map +1 -1
- package/dist/lib/scheduled-actions/handlers/index.js +3 -1
- package/dist/lib/scheduled-actions/handlers/security-notification.d.ts +26 -0
- package/dist/lib/scheduled-actions/handlers/security-notification.d.ts.map +1 -0
- package/dist/lib/scheduled-actions/handlers/security-notification.js +54 -0
- package/dist/lib/selectors/core-selectors.d.ts +12 -0
- package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.d.ts +12 -0
- package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.js +14 -1
- package/dist/lib/selectors/selectors.d.ts +24 -0
- package/dist/lib/selectors/selectors.d.ts.map +1 -1
- package/dist/lib/services/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 +4 -1
- package/dist/templates/app/api/superadmin/subscriptions/route.ts +2 -1
- package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
- package/dist/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
- package/dist/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
- package/dist/templates/app/api/v1/api-keys/route.ts +21 -43
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
- package/dist/templates/app/api/v1/billing/cancel/route.ts +7 -6
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +5 -4
- package/dist/templates/app/api/v1/billing/check-action/route.ts +6 -4
- package/dist/templates/app/api/v1/billing/checkout/route.ts +7 -5
- package/dist/templates/app/api/v1/billing/plans/route.ts +8 -12
- package/dist/templates/app/api/v1/billing/portal/route.ts +7 -5
- package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
- package/dist/templates/app/api/v1/devtools/blocks/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/features/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/flows/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
- package/dist/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/testing/route.ts +5 -4
- package/dist/templates/app/api/v1/media/[id]/route.ts +13 -25
- package/dist/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
- package/dist/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
- package/dist/templates/app/api/v1/media/route.ts +5 -9
- package/dist/templates/app/api/v1/media/upload/route.ts +9 -21
- package/dist/templates/app/api/v1/media-tags/route.ts +9 -13
- package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
- package/dist/templates/app/api/v1/post-categories/route.ts +5 -4
- package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
- package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
- package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
- package/dist/templates/app/api/v1/team-invitations/route.ts +9 -11
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
- package/dist/templates/app/api/v1/teams/route.ts +33 -27
- package/dist/templates/app/api/v1/teams/switch/route.ts +11 -13
- package/dist/templates/app/api/v1/theme/[...path]/route.ts +23 -2
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
- package/dist/templates/app/api/v1/users/[id]/route.ts +35 -68
- package/dist/templates/app/api/v1/users/route.ts +21 -25
- package/dist/templates/app/dashboard/layout.tsx +2 -6
- package/dist/templates/app/layout.ppr.tsx +9 -14
- package/dist/templates/app/layout.tsx +37 -20
- package/dist/templates/contents/themes/starter/config/app.config.ts +19 -0
- package/dist/templates/contents/themes/starter/config/theme.config.ts +6 -0
- package/dist/templates/contents/themes/starter/styles/globals.css +6 -2
- package/dist/templates/proxy.ts +46 -10
- package/dist/templates/tsconfig.json +4 -0
- package/dist/types/theme.d.ts +9 -0
- package/dist/types/theme.d.ts.map +1 -1
- package/migrations/005_api_audit_log.sql +2 -1
- package/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
- package/migrations/026_login_events.sql +91 -0
- package/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
- package/package.json +14 -11
- package/scripts/build/registry/__tests__/scope-registry.test.mjs +18 -0
- package/scripts/build/registry/generators/scope-registry.mjs +9 -2
- package/templates/app/(auth)/signup/page.tsx +8 -0
- package/templates/app/(public)/[...slug]/page.tsx +9 -14
- package/templates/app/api/auth/[...all]/route.ts +23 -1
- package/templates/app/api/health/route.ts +4 -2
- package/templates/app/api/internal/user-metadata/route.ts +5 -3
- package/templates/app/api/mcp/route.ts +4 -1
- package/templates/app/api/superadmin/subscriptions/route.ts +2 -1
- package/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
- package/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
- package/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
- package/templates/app/api/v1/api-keys/route.ts +21 -43
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
- package/templates/app/api/v1/billing/cancel/route.ts +7 -6
- package/templates/app/api/v1/billing/change-plan/route.ts +5 -4
- package/templates/app/api/v1/billing/check-action/route.ts +6 -4
- package/templates/app/api/v1/billing/checkout/route.ts +7 -5
- package/templates/app/api/v1/billing/plans/route.ts +8 -12
- package/templates/app/api/v1/billing/portal/route.ts +7 -5
- package/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
- package/templates/app/api/v1/devtools/blocks/route.ts +5 -4
- package/templates/app/api/v1/devtools/features/route.ts +5 -4
- package/templates/app/api/v1/devtools/flows/route.ts +5 -4
- package/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
- package/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
- package/templates/app/api/v1/devtools/testing/route.ts +5 -4
- package/templates/app/api/v1/media/[id]/route.ts +13 -25
- package/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
- package/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
- package/templates/app/api/v1/media/route.ts +5 -9
- package/templates/app/api/v1/media/upload/route.ts +9 -21
- package/templates/app/api/v1/media-tags/route.ts +9 -13
- package/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
- package/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
- package/templates/app/api/v1/post-categories/route.ts +5 -4
- package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
- package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
- package/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
- package/templates/app/api/v1/team-invitations/route.ts +9 -11
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
- package/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
- package/templates/app/api/v1/teams/route.ts +33 -27
- package/templates/app/api/v1/teams/switch/route.ts +11 -13
- package/templates/app/api/v1/theme/[...path]/route.ts +23 -2
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
- package/templates/app/api/v1/users/[id]/route.ts +35 -68
- package/templates/app/api/v1/users/route.ts +21 -25
- package/templates/app/dashboard/layout.tsx +2 -6
- package/templates/app/layout.ppr.tsx +9 -14
- package/templates/app/layout.tsx +37 -20
- package/templates/contents/themes/starter/config/app.config.ts +19 -0
- package/templates/contents/themes/starter/config/theme.config.ts +6 -0
- package/templates/contents/themes/starter/styles/globals.css +6 -2
- package/templates/proxy.ts +46 -10
- package/templates/tsconfig.json +4 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +23 -0
- package/tests/jest/__mocks__/next-server.js +24 -0
- package/dist/styles/classes.json +0 -1093
- package/dist/styles/ui.css +0 -2
- package/scripts/build/build-ui-css.mjs +0 -351
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamContext.d.ts","sourceRoot":"","sources":["../../src/contexts/TeamContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGvG,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAM7D,UAAU,gBAAgB;IACxB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAA;IACxB,SAAS,EAAE,kBAAkB,EAAE,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,wBAAwB,EAAE,OAAO,CAAA;IACjC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC;AAKD,eAAO,MAAM,eAAe,yBAA0B,CAAA;AAGtD,wBAAsB,cAAc,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAwBpE;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"TeamContext.d.ts","sourceRoot":"","sources":["../../src/contexts/TeamContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGvG,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAM7D,UAAU,gBAAgB;IACxB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAA;IACxB,SAAS,EAAE,kBAAkB,EAAE,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,wBAAwB,EAAE,OAAO,CAAA;IACjC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC;AAKD,eAAO,MAAM,eAAe,yBAA0B,CAAA;AAGtD,wBAAsB,cAAc,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAwBpE;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAqOjE;AAED,wBAAgB,cAAc,qBAM7B;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,GAAG,SAAS,CAErE"}
|
|
@@ -82,7 +82,10 @@ function TeamProvider({ children }) {
|
|
|
82
82
|
if (currentTeam && userTeams.some((t) => t.team.id === currentTeam.id)) return;
|
|
83
83
|
const storedTeamId = typeof window !== "undefined" ? localStorage.getItem("activeTeamId") : null;
|
|
84
84
|
const storedTeam = storedTeamId ? userTeams.find((t) => t.team.id === storedTeamId) : null;
|
|
85
|
-
const
|
|
85
|
+
const defaultTeam = storedTeam ? void 0 : [...userTeams].sort(
|
|
86
|
+
(a, b) => new Date(a.joinedAt).getTime() - new Date(b.joinedAt).getTime()
|
|
87
|
+
)[0];
|
|
88
|
+
const activeTeam = storedTeam || defaultTeam;
|
|
86
89
|
if (activeTeam) {
|
|
87
90
|
setCurrentTeam(activeTeam.team);
|
|
88
91
|
if (typeof window !== "undefined") {
|
|
@@ -181,9 +184,13 @@ function useTeamContext() {
|
|
|
181
184
|
}
|
|
182
185
|
return context;
|
|
183
186
|
}
|
|
187
|
+
function useOptionalTeamContext() {
|
|
188
|
+
return useContext(TeamContext);
|
|
189
|
+
}
|
|
184
190
|
export {
|
|
185
191
|
TEAMS_QUERY_KEY,
|
|
186
192
|
TeamProvider,
|
|
187
193
|
fetchUserTeams,
|
|
194
|
+
useOptionalTeamContext,
|
|
188
195
|
useTeamContext
|
|
189
196
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otp-verification.d.ts","sourceRoot":"","sources":["../../src/emails/otp-verification.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAKjF,wBAA8B,eAAe,CAC3C,IAAI,EAAE,wBAAwB,EAC9B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"otp-verification.d.ts","sourceRoot":"","sources":["../../src/emails/otp-verification.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAKjF,wBAA8B,eAAe,CAC3C,IAAI,EAAE,wBAAwB,EAC9B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CAmEvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reset-password.d.ts","sourceRoot":"","sources":["../../src/emails/reset-password.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAI/E,wBAA8B,aAAa,CACzC,IAAI,EAAE,sBAAsB,EAC5B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"reset-password.d.ts","sourceRoot":"","sources":["../../src/emails/reset-password.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAI/E,wBAA8B,aAAa,CACzC,IAAI,EAAE,sBAAsB,EAC5B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA2FvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-invitation.d.ts","sourceRoot":"","sources":["../../src/emails/team-invitation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"team-invitation.d.ts","sourceRoot":"","sources":["../../src/emails/team-invitation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAKhF,wBAA8B,cAAc,CAC1C,IAAI,EAAE,uBAAuB,EAC7B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CAyFvB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getTranslations } from "next-intl/server";
|
|
2
|
+
import { escapeHtml } from "../lib/auth/security-notifications/templates/shared.js";
|
|
2
3
|
const APP_NAME_FALLBACK = process.env.NEXT_PUBLIC_APP_NAME || "Your App";
|
|
3
4
|
async function teamInvitation(data, locale) {
|
|
4
5
|
const t = await getTranslations({ locale, namespace: "email.teamInvitation" });
|
|
@@ -32,7 +33,7 @@ async function teamInvitation(data, locale) {
|
|
|
32
33
|
<h2 style="color: #333333; font-size: 24px; margin: 0 0 20px 0; font-weight: 600;">${t("title")}</h2>
|
|
33
34
|
|
|
34
35
|
<p style="color: #666666; font-size: 16px; line-height: 1.6; margin: 0 0 20px 0;">
|
|
35
|
-
${t("bodyIntro", { inviterName: data.inviterName, teamName: data.teamName, role: data.role })}
|
|
36
|
+
${t("bodyIntro", { inviterName: escapeHtml(data.inviterName), teamName: escapeHtml(data.teamName), role: data.role })}
|
|
36
37
|
</p>
|
|
37
38
|
|
|
38
39
|
<p style="color: #666666; font-size: 16px; line-height: 1.6; margin: 0 0 30px 0;">
|
|
@@ -74,7 +75,7 @@ async function teamInvitation(data, locale) {
|
|
|
74
75
|
${t("copyright", { year, appName })}
|
|
75
76
|
</p>
|
|
76
77
|
<p style="color: #999999; font-size: 12px; margin: 0;">
|
|
77
|
-
${t("footerSentTo", { inviteeEmail: data.inviteeEmail })}<br>
|
|
78
|
+
${t("footerSentTo", { inviteeEmail: escapeHtml(data.inviteeEmail) })}<br>
|
|
78
79
|
${t("footerUnexpected")}
|
|
79
80
|
</p>
|
|
80
81
|
</td>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../src/emails/verify-email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAI9E,wBAA8B,WAAW,CACvC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../src/emails/verify-email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAI9E,wBAA8B,WAAW,CACvC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CAgFvB"}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from './useFeature';
|
|
|
17
17
|
export * from './useInvoice';
|
|
18
18
|
export * from './useInvoices';
|
|
19
19
|
export * from './useLastAuthMethod';
|
|
20
|
+
export * from './useSessionCookieRefresh';
|
|
20
21
|
export * from './useLocale';
|
|
21
22
|
export * from './useMedia';
|
|
22
23
|
export * from './useMediaUpload';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAGA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,WAAW,CAAA;AACzB,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,0BAA0B,CAAA;AACxC,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAGA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,WAAW,CAAA;AACzB,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,oBAAoB,CAAA;AAClC,cAAc,0BAA0B,CAAA;AACxC,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA"}
|
package/dist/hooks/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export * from "./useFeature.js";
|
|
|
17
17
|
export * from "./useInvoice.js";
|
|
18
18
|
export * from "./useInvoices.js";
|
|
19
19
|
export * from "./useLastAuthMethod.js";
|
|
20
|
+
export * from "./useSessionCookieRefresh.js";
|
|
20
21
|
export * from "./useLocale.js";
|
|
21
22
|
export * from "./useMedia.js";
|
|
22
23
|
export * from "./useMediaUpload.js";
|
package/dist/hooks/useAuth.d.ts
CHANGED
|
@@ -102,6 +102,35 @@ export declare function useAuth(): {
|
|
|
102
102
|
})>;
|
|
103
103
|
signOut: () => Promise<void>;
|
|
104
104
|
googleSignIn: (redirectTo?: string) => Promise<void>;
|
|
105
|
+
sendOtp: (email: string) => Promise<{
|
|
106
|
+
success: boolean;
|
|
107
|
+
}>;
|
|
108
|
+
signInWithOtp: ({ email, otp, redirectTo }: {
|
|
109
|
+
email: string;
|
|
110
|
+
otp: string;
|
|
111
|
+
redirectTo?: string;
|
|
112
|
+
}) => Promise<Omit<{
|
|
113
|
+
token: string;
|
|
114
|
+
user: {
|
|
115
|
+
id: string;
|
|
116
|
+
createdAt: Date;
|
|
117
|
+
updatedAt: Date;
|
|
118
|
+
email: string;
|
|
119
|
+
emailVerified: boolean;
|
|
120
|
+
name: string;
|
|
121
|
+
image?: string | null | undefined;
|
|
122
|
+
};
|
|
123
|
+
}, "user"> & {
|
|
124
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
125
|
+
id: string;
|
|
126
|
+
createdAt: Date;
|
|
127
|
+
updatedAt: Date;
|
|
128
|
+
emailVerified: boolean;
|
|
129
|
+
name: string;
|
|
130
|
+
email?: string;
|
|
131
|
+
image?: string;
|
|
132
|
+
} & {} & {}>;
|
|
133
|
+
}>;
|
|
105
134
|
resetPassword: (email: string) => Promise<{
|
|
106
135
|
success: boolean;
|
|
107
136
|
data: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAS9C,wBAAgB,OAAO;
|
|
1
|
+
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAS9C,wBAAgB,OAAO;UA4OS,WAAW,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;8CAtOa;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;;;;;+DAmBvC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAuE1H,MAAM;qBAhCjB,MAAM;;;gDAgBiB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;iBA8Kyf,CAAC;;;;;;;;;;;;;2BAlJ9jB,MAAM;;;;;;;;;;;;kCA6BC,MAAM,UAAU,MAAM;;;;;;;;;;;sCAwClB,MAAM,eAAe,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCA8B5B,MAAM;;;;;;;;;;;;;;;EA+C3D"}
|
package/dist/hooks/useAuth.js
CHANGED
|
@@ -29,6 +29,7 @@ function useAuth() {
|
|
|
29
29
|
email,
|
|
30
30
|
password,
|
|
31
31
|
name: `${firstName || ""} ${lastName || ""}`.trim() || email.split("@")[0],
|
|
32
|
+
// @ts-expect-error — pre-existing type error, tracked in https://github.com/NextSpark-js/nextspark/issues/131
|
|
32
33
|
firstName,
|
|
33
34
|
lastName
|
|
34
35
|
},
|
|
@@ -48,6 +49,27 @@ function useAuth() {
|
|
|
48
49
|
await authClient.signOut();
|
|
49
50
|
router.push("/login");
|
|
50
51
|
};
|
|
52
|
+
const handleSendOtp = async (email) => {
|
|
53
|
+
const { data, error } = await authClient.emailOtp.sendVerificationOtp({
|
|
54
|
+
email,
|
|
55
|
+
type: "sign-in"
|
|
56
|
+
});
|
|
57
|
+
if (error) {
|
|
58
|
+
throw new Error(error.message || "Failed to send the sign-in code");
|
|
59
|
+
}
|
|
60
|
+
return data;
|
|
61
|
+
};
|
|
62
|
+
const handleOtpSignIn = async ({ email, otp, redirectTo }) => {
|
|
63
|
+
const { data, error } = await authClient.signIn.emailOtp({ email, otp });
|
|
64
|
+
if (error) {
|
|
65
|
+
throw new Error(error.message || "Invalid or expired code");
|
|
66
|
+
}
|
|
67
|
+
if (data) {
|
|
68
|
+
saveAuthMethod("email");
|
|
69
|
+
router.push(redirectTo || "/dashboard");
|
|
70
|
+
}
|
|
71
|
+
return data;
|
|
72
|
+
};
|
|
51
73
|
const handleGoogleSignIn = async (redirectTo) => {
|
|
52
74
|
const callbackURL = redirectTo ? `${redirectTo}${redirectTo.includes("?") ? "&" : "?"}auth_method=google` : "/dashboard?auth_method=google";
|
|
53
75
|
await authClient.signIn.social({
|
|
@@ -174,6 +196,8 @@ function useAuth() {
|
|
|
174
196
|
signUp: handleSignUp,
|
|
175
197
|
signOut: handleSignOut,
|
|
176
198
|
googleSignIn: handleGoogleSignIn,
|
|
199
|
+
sendOtp: handleSendOtp,
|
|
200
|
+
signInWithOtp: handleOtpSignIn,
|
|
177
201
|
resetPassword: handleResetPassword,
|
|
178
202
|
updatePassword: handleUpdatePassword,
|
|
179
203
|
changePassword: handleChangePassword,
|
|
@@ -1,6 +1,28 @@
|
|
|
1
|
+
interface UserMetadata {
|
|
2
|
+
uiPreferences?: {
|
|
3
|
+
theme?: string;
|
|
4
|
+
sidebarCollapsed?: boolean;
|
|
5
|
+
};
|
|
6
|
+
securityPreferences?: {
|
|
7
|
+
loginAlertsEnabled?: boolean;
|
|
8
|
+
};
|
|
9
|
+
notificationsPreferences?: Record<string, boolean>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns the default blocks (uiPreferences, securityPreferences,
|
|
13
|
+
* notificationsPreferences) that are still missing from the user's meta.
|
|
14
|
+
*
|
|
15
|
+
* Each block is checked independently: other flows (onboarding progress, tours,
|
|
16
|
+
* active team pointer, ...) write their own keys into `meta` early, so an
|
|
17
|
+
* "is meta empty?" guard would almost never fire for real users (see #119).
|
|
18
|
+
* Only the missing blocks are returned so existing preferences are never
|
|
19
|
+
* overwritten.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getMissingMetadataDefaults(meta: UserMetadata | null | undefined): Partial<UserMetadata>;
|
|
1
22
|
/**
|
|
2
23
|
* Hook to ensure user has default metadata
|
|
3
24
|
* Uses TanStack Query for proper caching and deduplication
|
|
4
25
|
*/
|
|
5
26
|
export declare function useEnsureUserMetadata(): void;
|
|
27
|
+
export {};
|
|
6
28
|
//# sourceMappingURL=useEnsureUserMetadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEnsureUserMetadata.d.ts","sourceRoot":"","sources":["../../src/hooks/useEnsureUserMetadata.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useEnsureUserMetadata.d.ts","sourceRoot":"","sources":["../../src/hooks/useEnsureUserMetadata.ts"],"names":[],"mappings":"AAOA,UAAU,YAAY;IACpB,aAAa,CAAC,EAAE;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAC3B,CAAA;IACD,mBAAmB,CAAC,EAAE;QACpB,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAC7B,CAAA;IACD,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnD;AA0CD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GACpC,OAAO,CAAC,YAAY,CAAC,CASvB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,SAgEpC"}
|
|
@@ -33,6 +33,17 @@ const DEFAULT_METADATA = {
|
|
|
33
33
|
featureAnnouncementsPush: false
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
+
const METADATA_BLOCKS = Object.keys(DEFAULT_METADATA);
|
|
37
|
+
function getMissingMetadataDefaults(meta) {
|
|
38
|
+
const missing = {};
|
|
39
|
+
for (const block of METADATA_BLOCKS) {
|
|
40
|
+
const current = meta == null ? void 0 : meta[block];
|
|
41
|
+
if (current === null || current === void 0) {
|
|
42
|
+
missing[block] = DEFAULT_METADATA[block];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return missing;
|
|
46
|
+
}
|
|
36
47
|
function useEnsureUserMetadata() {
|
|
37
48
|
var _a, _b;
|
|
38
49
|
const session = authClient.useSession();
|
|
@@ -57,7 +68,7 @@ function useEnsureUserMetadata() {
|
|
|
57
68
|
// 30 minutes cache
|
|
58
69
|
});
|
|
59
70
|
const createMetadataMutation = useMutation({
|
|
60
|
-
mutationFn: async () => {
|
|
71
|
+
mutationFn: async (metadata) => {
|
|
61
72
|
const response = await fetch("/api/internal/user-metadata", {
|
|
62
73
|
method: "POST",
|
|
63
74
|
headers: {
|
|
@@ -65,7 +76,7 @@ function useEnsureUserMetadata() {
|
|
|
65
76
|
},
|
|
66
77
|
body: JSON.stringify({
|
|
67
78
|
userId,
|
|
68
|
-
metadata
|
|
79
|
+
metadata
|
|
69
80
|
})
|
|
70
81
|
});
|
|
71
82
|
if (!response.ok) {
|
|
@@ -82,14 +93,16 @@ function useEnsureUserMetadata() {
|
|
|
82
93
|
});
|
|
83
94
|
useEffect(() => {
|
|
84
95
|
if (!isFetched || !userData || hasCreatedMetadata.current) return;
|
|
85
|
-
const
|
|
96
|
+
const missingDefaults = getMissingMetadataDefaults(userData.meta);
|
|
97
|
+
const needsMetadata = Object.keys(missingDefaults).length > 0;
|
|
86
98
|
if (needsMetadata && !createMetadataMutation.isPending) {
|
|
87
99
|
hasCreatedMetadata.current = true;
|
|
88
|
-
console.log("Creating default metadata for user:", userId);
|
|
89
|
-
createMetadataMutation.mutate();
|
|
100
|
+
console.log("Creating default metadata for user:", userId, Object.keys(missingDefaults));
|
|
101
|
+
createMetadataMutation.mutate(missingDefaults);
|
|
90
102
|
}
|
|
91
103
|
}, [isFetched, userData, userId, createMetadataMutation]);
|
|
92
104
|
}
|
|
93
105
|
export {
|
|
106
|
+
getMissingMetadataDefaults,
|
|
94
107
|
useEnsureUserMetadata
|
|
95
108
|
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface UseSessionCookieRefreshOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Minimum time between two refresh calls, in milliseconds. Every trigger
|
|
4
|
+
* inside this window is ignored. Default: 5 minutes (matches the default
|
|
5
|
+
* session cookie cache, so at most one extra DB read per 5 minutes).
|
|
6
|
+
*/
|
|
7
|
+
minIntervalMs?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Also refresh once right after mount — i.e. every time the app is opened
|
|
10
|
+
* (PWA cold start, full reload). Default: true.
|
|
11
|
+
*/
|
|
12
|
+
refreshOnMount?: boolean;
|
|
13
|
+
/** Set to false to turn the hook off (e.g. on public/auth pages). Default: true. */
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const DEFAULT_SESSION_REFRESH_INTERVAL_MS: number;
|
|
17
|
+
/**
|
|
18
|
+
* Keep the session cookie alive for installed PWAs / long-lived tabs.
|
|
19
|
+
*
|
|
20
|
+
* Server Component renders read the session with `disableRefresh`, so they
|
|
21
|
+
* never renew the cookie (Next.js cannot write cookies during render). This
|
|
22
|
+
* hook triggers the real renewal through Better Auth's `get-session` Route
|
|
23
|
+
* Handler — where `Set-Cookie` works — whenever the app is (re)opened:
|
|
24
|
+
*
|
|
25
|
+
* - on mount (app open / reload), unless `refreshOnMount: false`
|
|
26
|
+
* - when the document becomes visible again (`visibilitychange`, `focus`,
|
|
27
|
+
* `pageshow` for bfcache restores)
|
|
28
|
+
* - when connectivity comes back (`online`)
|
|
29
|
+
*
|
|
30
|
+
* Calls are throttled by `minIntervalMs`; a failed call (offline, server
|
|
31
|
+
* down) does not count towards the throttle, so the next trigger retries.
|
|
32
|
+
* Anonymous visitors just get a cheap `null` session response.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useSessionCookieRefresh(options?: UseSessionCookieRefreshOptions): void;
|
|
35
|
+
//# sourceMappingURL=useSessionCookieRefresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSessionCookieRefresh.d.ts","sourceRoot":"","sources":["../../src/hooks/useSessionCookieRefresh.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,mCAAmC,QAAgB,CAAA;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,8BAAmC,QAmDnF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { refreshSessionCookie } from "../lib/auth-client.js";
|
|
4
|
+
const DEFAULT_SESSION_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
5
|
+
function useSessionCookieRefresh(options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
minIntervalMs = DEFAULT_SESSION_REFRESH_INTERVAL_MS,
|
|
8
|
+
refreshOnMount = true,
|
|
9
|
+
enabled = true
|
|
10
|
+
} = options;
|
|
11
|
+
const lastRefreshAtRef = useRef(0);
|
|
12
|
+
const inFlightRef = useRef(false);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!enabled || typeof window === "undefined") return;
|
|
15
|
+
const refresh = async () => {
|
|
16
|
+
if (inFlightRef.current) return;
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
if (now - lastRefreshAtRef.current < minIntervalMs) return;
|
|
19
|
+
inFlightRef.current = true;
|
|
20
|
+
lastRefreshAtRef.current = now;
|
|
21
|
+
try {
|
|
22
|
+
await refreshSessionCookie();
|
|
23
|
+
} catch {
|
|
24
|
+
lastRefreshAtRef.current = 0;
|
|
25
|
+
} finally {
|
|
26
|
+
inFlightRef.current = false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const onVisibilityChange = () => {
|
|
30
|
+
if (document.visibilityState === "visible") void refresh();
|
|
31
|
+
};
|
|
32
|
+
const onWake = () => void refresh();
|
|
33
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
34
|
+
window.addEventListener("focus", onWake);
|
|
35
|
+
window.addEventListener("pageshow", onWake);
|
|
36
|
+
window.addEventListener("online", onWake);
|
|
37
|
+
if (refreshOnMount) void refresh();
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
40
|
+
window.removeEventListener("focus", onWake);
|
|
41
|
+
window.removeEventListener("pageshow", onWake);
|
|
42
|
+
window.removeEventListener("online", onWake);
|
|
43
|
+
};
|
|
44
|
+
}, [enabled, minIntervalMs, refreshOnMount]);
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
DEFAULT_SESSION_REFRESH_INTERVAL_MS,
|
|
48
|
+
useSessionCookieRefresh
|
|
49
|
+
};
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
export declare const API_ERROR_CODES: {
|
|
8
8
|
/** Role-based permission check failed (checkPermission) */
|
|
9
9
|
readonly PERMISSION_DENIED: "PERMISSION_DENIED";
|
|
10
|
-
/** API key scope
|
|
10
|
+
/** API key lacks the scope the route declared (authenticateRequest / hasRequiredScope) */
|
|
11
11
|
readonly INSUFFICIENT_SCOPE: "INSUFFICIENT_SCOPE";
|
|
12
|
+
/** Route declared no required API key scope, so API keys are rejected (fail closed, #93) */
|
|
13
|
+
readonly SCOPE_NOT_DECLARED: "SCOPE_NOT_DECLARED";
|
|
12
14
|
};
|
|
13
15
|
export type ApiErrorCode = typeof API_ERROR_CODES[keyof typeof API_ERROR_CODES];
|
|
14
16
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../../../src/lib/api/api-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,eAAe;IAC1B,2DAA2D;;IAE3D,
|
|
1
|
+
{"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../../../src/lib/api/api-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,eAAe;IAC1B,2DAA2D;;IAE3D,0FAA0F;;IAE1F,4FAA4F;;CAEpF,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,OAAO,eAAe,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAE/E;;;;;GAKG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;gBAEL,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;IAa5F;;OAEG;IACH,kBAAkB,IAAI,OAAO;IAI7B;;;;;OAKG;WACU,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAqC1F"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
const API_ERROR_CODES = {
|
|
2
2
|
/** Role-based permission check failed (checkPermission) */
|
|
3
3
|
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
4
|
-
/** API key scope
|
|
5
|
-
INSUFFICIENT_SCOPE: "INSUFFICIENT_SCOPE"
|
|
4
|
+
/** API key lacks the scope the route declared (authenticateRequest / hasRequiredScope) */
|
|
5
|
+
INSUFFICIENT_SCOPE: "INSUFFICIENT_SCOPE",
|
|
6
|
+
/** Route declared no required API key scope, so API keys are rejected (fail closed, #93) */
|
|
7
|
+
SCOPE_NOT_DECLARED: "SCOPE_NOT_DECLARED"
|
|
6
8
|
};
|
|
7
9
|
class ApiError extends Error {
|
|
8
10
|
code;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Used by the unified /api/v1/ endpoints.
|
|
6
6
|
*/
|
|
7
7
|
import { NextRequest, NextResponse } from 'next/server';
|
|
8
|
+
import { type AuthenticateOptions, type DualAuthFailure } from './scope-policy';
|
|
9
|
+
export type { AuthenticateOptions, DualAuthFailure, DualAuthFailureCode } from './scope-policy';
|
|
8
10
|
/**
|
|
9
11
|
* System Admin Team - Members can bypass team context validation
|
|
10
12
|
* This team is created in core/migrations/090_sample_data.sql
|
|
@@ -33,23 +35,48 @@ export interface DualAuthUser {
|
|
|
33
35
|
}
|
|
34
36
|
export interface DualAuthResult {
|
|
35
37
|
success: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* How the caller authenticated. On a failed result, `'api-key'` means a
|
|
40
|
+
* valid key WAS presented but rejected by scope enforcement (see `error`);
|
|
41
|
+
* `'none'` means no usable credential was found.
|
|
42
|
+
*/
|
|
36
43
|
type: 'api-key' | 'session' | 'none';
|
|
37
44
|
user: DualAuthUser | null;
|
|
38
45
|
scopes?: string[];
|
|
46
|
+
/** `api_key.id` of the key that authenticated the request (type 'api-key' only). Used for audit logging (#105). */
|
|
47
|
+
keyId?: string;
|
|
39
48
|
rateLimitResponse?: Response;
|
|
40
49
|
/** Set when a dev-only x-act-as-user override replaced the real caller. */
|
|
41
50
|
actingAs?: {
|
|
42
51
|
originalUserId: string;
|
|
43
52
|
originalRole: string;
|
|
44
53
|
};
|
|
54
|
+
/** Why the request was rejected. Only present when `success` is false. */
|
|
55
|
+
error?: DualAuthFailure;
|
|
45
56
|
}
|
|
46
57
|
/**
|
|
47
|
-
* Try to authenticate request using either API Key or Session
|
|
58
|
+
* Try to authenticate request using either API Key or Session.
|
|
59
|
+
*
|
|
60
|
+
* API-key scope enforcement happens HERE and fails closed (#93): a route
|
|
61
|
+
* declares the scope it needs via `options.requiredScope`, or explicitly
|
|
62
|
+
* accepts any valid key via `options.allowAnyScope`. A key hitting a route
|
|
63
|
+
* that declared neither is rejected (403 `SCOPE_NOT_DECLARED`) and the route
|
|
64
|
+
* is named in the log, so a forgotten declaration is visible instead of a
|
|
65
|
+
* silently over-privileged credential. Session auth is unaffected.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* const authResult = await authenticateRequest(request, { requiredScope: 'users:read' })
|
|
69
|
+
* if (!authResult.success || !authResult.user) return createAuthFailureResponse(authResult)
|
|
48
70
|
*/
|
|
49
|
-
export declare function authenticateRequest(request: NextRequest): Promise<DualAuthResult>;
|
|
71
|
+
export declare function authenticateRequest(request: NextRequest, options?: AuthenticateOptions): Promise<DualAuthResult>;
|
|
50
72
|
/**
|
|
51
73
|
* Check if user has required scope (for API Key auth)
|
|
52
74
|
*
|
|
75
|
+
* Since #93 the entry point already enforces the route's declared scope;
|
|
76
|
+
* this remains for routes that branch on a *second*, finer scope after
|
|
77
|
+
* authenticating (e.g. read-vs-write within one handler). An array is
|
|
78
|
+
* satisfied by any one of its scopes; an empty array by none.
|
|
79
|
+
*
|
|
53
80
|
* `admin:all` was previously accepted as an implicit full-access scope here,
|
|
54
81
|
* but it was never in `API_SCOPES`, never mintable via `validateScopesForUser`,
|
|
55
82
|
* and undocumented — a full-access string nothing legitimately produced.
|
|
@@ -57,7 +84,7 @@ export declare function authenticateRequest(request: NextRequest): Promise<DualA
|
|
|
57
84
|
* superadmin, see `validateScopesForUser`) already covers the "this key has
|
|
58
85
|
* full access" case in a documented, auditable way.
|
|
59
86
|
*/
|
|
60
|
-
export declare function hasRequiredScope(authResult: DualAuthResult, requiredScope: string): boolean;
|
|
87
|
+
export declare function hasRequiredScope(authResult: DualAuthResult, requiredScope: string | string[]): boolean;
|
|
61
88
|
/**
|
|
62
89
|
* Resolve and validate team context from request.
|
|
63
90
|
*
|
|
@@ -92,4 +119,12 @@ export declare function createAuthError(message?: string, status?: number): Next
|
|
|
92
119
|
error: string;
|
|
93
120
|
code: string;
|
|
94
121
|
}>;
|
|
122
|
+
/**
|
|
123
|
+
* Turn a failed `authenticateRequest` result into the right HTTP response:
|
|
124
|
+
* 401 `AUTHENTICATION_FAILED` when nothing usable was presented, 403
|
|
125
|
+
* `INSUFFICIENT_SCOPE` / `SCOPE_NOT_DECLARED` when a valid API key was
|
|
126
|
+
* rejected by scope enforcement. Use it in the `!authResult.success` branch
|
|
127
|
+
* instead of a hand-written 401, so scope rejections are not mislabeled.
|
|
128
|
+
*/
|
|
129
|
+
export declare function createAuthFailureResponse(authResult: DualAuthResult): NextResponse;
|
|
95
130
|
//# sourceMappingURL=dual-auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dual-auth.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/dual-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"dual-auth.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/dual-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAMvD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAKrB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAK/F;;;GAGG;AACH,eAAO,MAAM,oBAAoB,uBAAuB,CAAA;AAExD;;GAEG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AACnD,eAAO,MAAM,kBAAkB,8BAA8B,CAAA;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AAOjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,mHAAmH;IACnH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iBAAiB,CAAC,EAAE,QAAQ,CAAA;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3D,0EAA0E;IAC1E,KAAK,CAAC,EAAE,eAAe,CAAA;CACxB;AAqBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,cAAc,CAAC,CAsCzB;AAuKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAUtG;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,cAAc,GACzB,OAAO,CAAC,MAAM,GAAG,YAAY,CAAC,CAsChC;AAMD;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,cAAc,EAC1B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CAkBlB;AA2BD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,MAAkC,EAAE,MAAM,GAAE,MAAY;;;;GAShG;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,cAAc,GAAG,YAAY,CAUlF"}
|
|
@@ -4,6 +4,12 @@ import { validateApiKey } from "../auth.js";
|
|
|
4
4
|
import { queryOne } from "../../db.js";
|
|
5
5
|
import { TeamMemberService } from "../../services/team-member.service.js";
|
|
6
6
|
import { TeamService } from "../../services/team.service.js";
|
|
7
|
+
import {
|
|
8
|
+
authenticationFailed,
|
|
9
|
+
describeRoute,
|
|
10
|
+
resolveApiKeyScopeFailure,
|
|
11
|
+
scopesSatisfy
|
|
12
|
+
} from "./scope-policy.js";
|
|
7
13
|
const SYSTEM_ADMIN_TEAM_ID = "team-nextspark-001";
|
|
8
14
|
const ADMIN_BYPASS_HEADER = "x-admin-bypass";
|
|
9
15
|
const ADMIN_BYPASS_VALUE = "confirm-cross-team-access";
|
|
@@ -24,9 +30,20 @@ async function getUserDefaultTeamId(userId) {
|
|
|
24
30
|
return void 0;
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
|
-
async function authenticateRequest(request) {
|
|
33
|
+
async function authenticateRequest(request, options = {}) {
|
|
28
34
|
const apiKeyResult = await tryApiKeyAuth(request);
|
|
29
35
|
if (apiKeyResult.success) {
|
|
36
|
+
const failure = resolveApiKeyScopeFailure(apiKeyResult.scopes, options, describeRoute(request));
|
|
37
|
+
if (failure) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
type: "api-key",
|
|
41
|
+
user: apiKeyResult.user,
|
|
42
|
+
scopes: apiKeyResult.scopes,
|
|
43
|
+
keyId: apiKeyResult.keyId,
|
|
44
|
+
error: failure
|
|
45
|
+
};
|
|
46
|
+
}
|
|
30
47
|
return applyActAsOverride(request, apiKeyResult);
|
|
31
48
|
}
|
|
32
49
|
const sessionResult = await trySessionAuth(request);
|
|
@@ -36,7 +53,8 @@ async function authenticateRequest(request) {
|
|
|
36
53
|
return {
|
|
37
54
|
success: false,
|
|
38
55
|
type: "none",
|
|
39
|
-
user: null
|
|
56
|
+
user: null,
|
|
57
|
+
error: authenticationFailed()
|
|
40
58
|
};
|
|
41
59
|
}
|
|
42
60
|
function isProductionRuntime() {
|
|
@@ -104,7 +122,8 @@ async function tryApiKeyAuth(request) {
|
|
|
104
122
|
name: userInfo == null ? void 0 : userInfo.name,
|
|
105
123
|
defaultTeamId
|
|
106
124
|
},
|
|
107
|
-
scopes: apiAuth.scopes || []
|
|
125
|
+
scopes: apiAuth.scopes || [],
|
|
126
|
+
keyId: apiAuth.keyId
|
|
108
127
|
};
|
|
109
128
|
} catch (error) {
|
|
110
129
|
console.error("API Key auth failed:", error);
|
|
@@ -128,6 +147,7 @@ async function trySessionAuth(request) {
|
|
|
128
147
|
user: {
|
|
129
148
|
id: session.user.id,
|
|
130
149
|
email: session.user.email,
|
|
150
|
+
// @ts-expect-error — pre-existing type error, tracked in https://github.com/NextSpark-js/nextspark/issues/131
|
|
131
151
|
role: session.user.role || "user",
|
|
132
152
|
name: session.user.name,
|
|
133
153
|
defaultTeamId
|
|
@@ -144,8 +164,8 @@ function hasRequiredScope(authResult, requiredScope) {
|
|
|
144
164
|
if (authResult.type === "session") {
|
|
145
165
|
return true;
|
|
146
166
|
}
|
|
147
|
-
if (authResult.type === "api-key"
|
|
148
|
-
return authResult.scopes
|
|
167
|
+
if (authResult.type === "api-key") {
|
|
168
|
+
return scopesSatisfy(authResult.scopes, requiredScope);
|
|
149
169
|
}
|
|
150
170
|
return false;
|
|
151
171
|
}
|
|
@@ -191,8 +211,11 @@ async function canBypassTeamContext(authResult, request) {
|
|
|
191
211
|
async function checkSystemAdminMembership(userId) {
|
|
192
212
|
try {
|
|
193
213
|
const member = await queryOne(
|
|
194
|
-
|
|
195
|
-
|
|
214
|
+
`SELECT tm.id FROM "team_members" tm
|
|
215
|
+
JOIN "teams" t ON t.id = tm."teamId"
|
|
216
|
+
WHERE tm."userId" = $1 AND (t.metadata->>'systemAdmin')::boolean IS TRUE
|
|
217
|
+
LIMIT 1`,
|
|
218
|
+
[userId]
|
|
196
219
|
);
|
|
197
220
|
return !!member;
|
|
198
221
|
} catch (error) {
|
|
@@ -210,6 +233,17 @@ function createAuthError(message = "Authentication required", status = 401) {
|
|
|
210
233
|
{ status }
|
|
211
234
|
);
|
|
212
235
|
}
|
|
236
|
+
function createAuthFailureResponse(authResult) {
|
|
237
|
+
const failure = authResult.error ?? authenticationFailed();
|
|
238
|
+
return NextResponse.json(
|
|
239
|
+
{
|
|
240
|
+
success: false,
|
|
241
|
+
error: failure.message,
|
|
242
|
+
code: failure.code
|
|
243
|
+
},
|
|
244
|
+
{ status: failure.status }
|
|
245
|
+
);
|
|
246
|
+
}
|
|
213
247
|
export {
|
|
214
248
|
ACT_AS_USER_HEADER,
|
|
215
249
|
ADMIN_BYPASS_HEADER,
|
|
@@ -218,6 +252,7 @@ export {
|
|
|
218
252
|
authenticateRequest,
|
|
219
253
|
canBypassTeamContext,
|
|
220
254
|
createAuthError,
|
|
255
|
+
createAuthFailureResponse,
|
|
221
256
|
hasRequiredScope,
|
|
222
257
|
resolveTeamContext
|
|
223
258
|
};
|
|
@@ -17,9 +17,14 @@ export declare function isSuperAdmin(authResult: DualAuthResult): boolean;
|
|
|
17
17
|
* Verifica si el usuario tiene permisos para operaciones administrativas
|
|
18
18
|
* Combina validacion de superadmin + scopes de API key
|
|
19
19
|
*
|
|
20
|
+
* Fail closed for API keys (#93): a key is only granted admin access when the
|
|
21
|
+
* caller names the scope the operation needs and the key holds it. Omitting
|
|
22
|
+
* `requiredScope` used to hand a narrowly-scoped key its superadmin owner's
|
|
23
|
+
* full permissions; now it denies the key instead. Sessions are unaffected.
|
|
24
|
+
*
|
|
20
25
|
* @param authResult - Resultado de autenticacion
|
|
21
|
-
* @param requiredScope - Scope requerido para API keys (
|
|
26
|
+
* @param requiredScope - Scope requerido para API keys (obligatorio para que una API key pase)
|
|
22
27
|
* @returns true si tiene permisos de admin
|
|
23
28
|
*/
|
|
24
|
-
export declare function hasAdminPermission(authResult: DualAuthResult, requiredScope?: string): boolean;
|
|
29
|
+
export declare function hasAdminPermission(authResult: DualAuthResult, requiredScope?: string | string[]): boolean;
|
|
25
30
|
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/permissions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAOhE;AAED
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/permissions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAOhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,cAAc,EAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAChC,OAAO,CAYT"}
|