@nextsparkjs/core 0.1.0-beta.194 → 0.1.0-beta.196
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 +8 -1
- package/dist/components/app/guards/DeveloperGuard.d.ts.map +1 -1
- package/dist/components/app/guards/DeveloperGuard.js +2 -1
- package/dist/components/app/guards/SuperAdminGuard.d.ts +8 -1
- package/dist/components/app/guards/SuperAdminGuard.d.ts.map +1 -1
- package/dist/components/app/guards/SuperAdminGuard.js +2 -1
- package/dist/components/billing/InvoicesPagination.js +2 -2
- package/dist/components/dashboard/block-editor/block-picker.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/block-picker.js +2 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.js +39 -23
- package/dist/components/dashboard/block-editor/pattern-reference-preview.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/pattern-reference-preview.js +2 -1
- package/dist/components/dashboard/layouts/AuthGuard.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/AuthGuard.js +3 -4
- package/dist/components/dashboard/layouts/AuthenticatedDashboardLayout.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/AuthenticatedDashboardLayout.js +9 -10
- package/dist/components/dashboard/layouts/DashboardAuthLayout.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/DashboardAuthLayout.js +3 -4
- package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts +2 -2
- package/dist/components/dashboard/navigation/DynamicNavigation.js +2 -2
- package/dist/components/entities/wrappers/EntityDetailWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityDetailWrapper.js +5 -2
- package/dist/components/entities/wrappers/EntityFormWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityFormWrapper.js +5 -2
- package/dist/components/entities/wrappers/EntityListWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityListWrapper.js +5 -2
- package/dist/components/settings/layouts/SettingsSidebar.d.ts.map +1 -1
- package/dist/components/settings/layouts/SettingsSidebar.js +0 -1
- package/dist/components/teams/CreateTeamButton.js +2 -2
- package/dist/components/teams/CreateTeamDialog.js +1 -1
- package/dist/components/teams/InviteMemberButton.js +3 -3
- package/dist/components/teams/InviteMemberDialog.js +1 -1
- package/dist/components/teams/TeamPendingInvitations.js +2 -2
- package/dist/components/ui/relation-display.d.ts.map +1 -1
- package/dist/components/ui/relation-display.js +2 -1
- package/dist/components/ui/user-display.d.ts.map +1 -1
- package/dist/components/ui/user-display.js +2 -1
- package/dist/emails/otp-verification.d.ts.map +1 -1
- package/dist/emails/otp-verification.js +5 -3
- package/dist/emails/reset-password.d.ts.map +1 -1
- package/dist/emails/reset-password.js +5 -3
- 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/emails/verify-email.js +5 -3
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +21 -4
- package/dist/hooks/usePatternUsages.d.ts.map +1 -1
- package/dist/hooks/usePatternUsages.js +3 -2
- package/dist/lib/api/api-error.d.ts +2 -0
- package/dist/lib/api/api-error.d.ts.map +1 -1
- package/dist/lib/api/api-error.js +3 -1
- package/dist/lib/api/auth/dual-auth.d.ts +2 -1
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +22 -4
- package/dist/lib/api/auth/scope-policy.d.ts +6 -3
- package/dist/lib/api/auth/scope-policy.d.ts.map +1 -1
- package/dist/lib/api/auth.d.ts +2 -0
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +10 -10
- package/dist/lib/api/client-headers.d.ts +11 -0
- package/dist/lib/api/client-headers.d.ts.map +1 -0
- package/dist/lib/api/client-headers.js +12 -0
- package/dist/lib/api/client-ip.d.ts +59 -0
- package/dist/lib/api/client-ip.d.ts.map +1 -0
- package/dist/lib/api/client-ip.js +91 -0
- package/dist/lib/api/cors-response.d.ts +55 -0
- package/dist/lib/api/cors-response.d.ts.map +1 -0
- package/dist/lib/api/cors-response.js +90 -0
- package/dist/lib/api/entities.d.ts.map +1 -1
- package/dist/lib/api/entities.js +4 -3
- package/dist/lib/api/entity/audit-log.d.ts.map +1 -1
- package/dist/lib/api/entity/audit-log.js +2 -1
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +23 -8
- package/dist/lib/api/helpers.d.ts +8 -2
- package/dist/lib/api/helpers.d.ts.map +1 -1
- package/dist/lib/api/helpers.js +32 -103
- package/dist/lib/api/keys.d.ts +6 -0
- package/dist/lib/api/keys.d.ts.map +1 -1
- package/dist/lib/api/keys.js +7 -1
- package/dist/lib/api/permission-middleware.d.ts.map +1 -1
- package/dist/lib/api/permission-middleware.js +0 -2
- package/dist/lib/api/rate-limit.d.ts +12 -0
- package/dist/lib/api/rate-limit.d.ts.map +1 -1
- package/dist/lib/api/rate-limit.js +50 -43
- package/dist/lib/api/request-origin.d.ts +16 -7
- package/dist/lib/api/request-origin.d.ts.map +1 -1
- package/dist/lib/api/request-origin.js +43 -20
- package/dist/lib/auth/load-login-page.d.ts +10 -0
- package/dist/lib/auth/load-login-page.d.ts.map +1 -0
- package/dist/lib/auth/load-login-page.js +7 -0
- package/dist/lib/auth/runtime-readiness.d.ts.map +1 -1
- package/dist/lib/auth/runtime-readiness.js +4 -0
- package/dist/lib/auth/sample-accounts.d.ts +17 -0
- package/dist/lib/auth/sample-accounts.d.ts.map +1 -0
- package/dist/lib/auth/sample-accounts.js +22 -0
- package/dist/lib/auth/security-notifications/device-fingerprint.d.ts +1 -8
- package/dist/lib/auth/security-notifications/device-fingerprint.d.ts.map +1 -1
- package/dist/lib/auth/security-notifications/device-fingerprint.js +2 -12
- package/dist/lib/auth-email-callbacks.js +3 -3
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +13 -5
- package/dist/lib/cache/public-entity-cache.d.ts +21 -0
- package/dist/lib/cache/public-entity-cache.d.ts.map +1 -0
- package/dist/lib/cache/public-entity-cache.js +25 -0
- package/dist/lib/config/config-client.d.ts.map +1 -1
- package/dist/lib/config/config-client.js +1 -1
- package/dist/lib/config/config-sync.d.ts.map +1 -1
- package/dist/lib/config/config-sync.js +1 -1
- package/dist/lib/config/public-config-client.d.ts +1 -0
- package/dist/lib/config/public-config-client.d.ts.map +1 -1
- package/dist/lib/config/public-config-client.js +3 -1
- package/dist/lib/config/types.d.ts +2 -0
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/constants/reserved-slugs.d.ts +17 -8
- package/dist/lib/constants/reserved-slugs.d.ts.map +1 -1
- package/dist/lib/constants/reserved-slugs.js +16 -52
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +9 -3
- package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
- package/dist/lib/entities/entity-hooks.js +4 -0
- package/dist/lib/entities/external-api-generator.d.ts.map +1 -1
- package/dist/lib/entities/external-api-generator.js +2 -4
- package/dist/lib/entities/public-slug.d.ts +46 -0
- package/dist/lib/entities/public-slug.d.ts.map +1 -0
- package/dist/lib/entities/public-slug.js +39 -0
- package/dist/lib/mcp/errors.d.ts +2 -0
- package/dist/lib/mcp/errors.d.ts.map +1 -1
- package/dist/lib/mcp/errors.js +3 -1
- package/dist/lib/mcp/index.d.ts +1 -1
- package/dist/lib/mcp/index.d.ts.map +1 -1
- package/dist/lib/mcp/index.js +2 -1
- package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.js +2 -0
- package/dist/lib/teams/actions.d.ts.map +1 -1
- package/dist/lib/teams/actions.js +0 -1
- package/dist/messages/de/billing.json +12 -1
- package/dist/messages/de/common.json +2 -0
- package/dist/messages/de/docs.json +1 -0
- package/dist/messages/de/features.json +16 -4
- package/dist/messages/de/home.json +5 -0
- package/dist/messages/de/index.d.ts +68 -25
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/index.js +2 -0
- package/dist/messages/de/index.ts +2 -0
- package/dist/messages/de/settings.json +1 -0
- package/dist/messages/de/teams.json +3 -18
- package/dist/messages/en/billing.json +1 -1
- package/dist/messages/en/common.json +2 -0
- package/dist/messages/en/docs.json +1 -0
- package/dist/messages/en/features.json +16 -4
- package/dist/messages/en/home.json +5 -0
- package/dist/messages/en/index.d.ts +58 -25
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/index.js +2 -0
- package/dist/messages/en/index.ts +2 -0
- package/dist/messages/en/settings.json +1 -0
- package/dist/messages/en/teams.json +3 -18
- package/dist/messages/es/billing.json +1 -1
- package/dist/messages/es/common.json +2 -0
- package/dist/messages/es/docs.json +1 -0
- package/dist/messages/es/features.json +16 -4
- package/dist/messages/es/home.json +5 -0
- package/dist/messages/es/index.d.ts +41 -8
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/index.js +2 -0
- package/dist/messages/es/index.ts +2 -0
- package/dist/messages/es/settings.json +1 -0
- package/dist/messages/es/teams.json +3 -0
- package/dist/messages/fr/billing.json +12 -1
- package/dist/messages/fr/common.json +2 -0
- package/dist/messages/fr/docs.json +1 -0
- package/dist/messages/fr/features.json +16 -4
- package/dist/messages/fr/home.json +5 -0
- package/dist/messages/fr/index.d.ts +68 -25
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/index.js +2 -0
- package/dist/messages/fr/index.ts +2 -0
- package/dist/messages/fr/settings.json +1 -0
- package/dist/messages/fr/teams.json +3 -18
- package/dist/messages/it/billing.json +12 -1
- package/dist/messages/it/common.json +2 -0
- package/dist/messages/it/docs.json +1 -0
- package/dist/messages/it/features.json +16 -4
- package/dist/messages/it/home.json +5 -0
- package/dist/messages/it/index.d.ts +68 -25
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/index.js +2 -0
- package/dist/messages/it/index.ts +2 -0
- package/dist/messages/it/settings.json +1 -0
- package/dist/messages/it/teams.json +3 -18
- package/dist/messages/pt/billing.json +12 -1
- package/dist/messages/pt/common.json +2 -0
- package/dist/messages/pt/docs.json +1 -0
- package/dist/messages/pt/features.json +16 -4
- package/dist/messages/pt/home.json +5 -0
- package/dist/messages/pt/index.d.ts +68 -25
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/index.js +2 -0
- package/dist/messages/pt/index.ts +2 -0
- package/dist/messages/pt/settings.json +1 -0
- package/dist/messages/pt/teams.json +3 -18
- package/dist/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/dist/migrations/090_sample_data.sql +10 -0
- package/dist/routes/(auth)/signup/page.cc.d.ts +3 -8
- package/dist/routes/(auth)/signup/page.cc.d.ts.map +1 -1
- package/dist/routes/(auth)/signup/page.cc.js +7 -1
- package/dist/routes/(auth)/verify-email/page.d.ts.map +1 -1
- package/dist/routes/(auth)/verify-email/page.js +2 -1
- package/dist/routes/(public)/page.cc.d.ts +3 -0
- package/dist/routes/(public)/page.cc.d.ts.map +1 -0
- package/dist/routes/(public)/page.cc.js +3 -0
- package/dist/routes/_internal/area-access.cc.d.ts +21 -0
- package/dist/routes/_internal/area-access.cc.d.ts.map +1 -0
- package/dist/routes/_internal/area-access.cc.js +19 -0
- package/dist/routes/_internal/auth-layout.d.ts.map +1 -1
- package/dist/routes/_internal/auth-layout.js +3 -2
- package/dist/routes/_internal/dashboard-main-shared.d.ts +7 -0
- package/dist/routes/_internal/dashboard-main-shared.d.ts.map +1 -0
- package/dist/routes/_internal/dashboard-main-shared.js +49 -0
- package/dist/routes/_internal/default-auth-layout.d.ts.map +1 -1
- package/dist/routes/_internal/default-auth-layout.js +3 -2
- package/dist/routes/_internal/devtools-layout.cc.d.ts +3 -2
- package/dist/routes/_internal/devtools-layout.cc.d.ts.map +1 -1
- package/dist/routes/_internal/devtools-layout.cc.js +1 -1
- package/dist/routes/_internal/devtools-layout.d.ts +7 -2
- package/dist/routes/_internal/devtools-layout.d.ts.map +1 -1
- package/dist/routes/_internal/devtools-layout.js +4 -4
- package/dist/routes/_internal/entity-detail-route.cc.d.ts +8 -0
- package/dist/routes/_internal/entity-detail-route.cc.d.ts.map +1 -0
- package/dist/routes/_internal/entity-detail-route.cc.js +5 -0
- package/dist/routes/_internal/entity-edit-route.cc.d.ts +8 -0
- package/dist/routes/_internal/entity-edit-route.cc.d.ts.map +1 -0
- package/dist/routes/_internal/entity-edit-route.cc.js +5 -0
- package/dist/routes/_internal/entity-permission-layout.d.ts.map +1 -1
- package/dist/routes/_internal/entity-permission-layout.js +0 -7
- package/dist/routes/_internal/public-archive-route.d.ts +1 -1
- package/dist/routes/_internal/public-archive-route.d.ts.map +1 -1
- package/dist/routes/_internal/public-archive-route.js +8 -3
- package/dist/routes/_internal/public-item-route.cc.d.ts +5 -1
- package/dist/routes/_internal/public-item-route.cc.d.ts.map +1 -1
- package/dist/routes/_internal/public-item-route.cc.js +2 -1
- package/dist/routes/_internal/public-item-route.d.ts +7 -0
- package/dist/routes/_internal/public-item-route.d.ts.map +1 -1
- package/dist/routes/_internal/public-item-route.js +6 -1
- package/dist/routes/_internal/superadmin-layout.cc.d.ts +2 -1
- package/dist/routes/_internal/superadmin-layout.cc.d.ts.map +1 -1
- package/dist/routes/_internal/superadmin-layout.cc.js +1 -1
- package/dist/routes/_internal/superadmin-layout.d.ts +7 -2
- package/dist/routes/_internal/superadmin-layout.d.ts.map +1 -1
- package/dist/routes/_internal/superadmin-layout.js +6 -6
- package/dist/routes/_internal/suspended-route.d.ts +16 -0
- package/dist/routes/_internal/suspended-route.d.ts.map +1 -0
- package/dist/routes/_internal/suspended-route.js +7 -0
- package/dist/routes/api/auth/[...all]/route.d.ts.map +1 -1
- package/dist/routes/api/auth/[...all]/route.js +4 -12
- package/dist/routes/api/csp-report/route.d.ts.map +1 -1
- package/dist/routes/api/csp-report/route.js +2 -13
- package/dist/routes/api/devtools/config/entities/route.d.ts +2 -0
- package/dist/routes/api/devtools/config/entities/route.d.ts.map +1 -1
- package/dist/routes/api/devtools/config/entities/route.js +2 -0
- package/dist/routes/api/devtools/config/theme/route.d.ts +2 -0
- package/dist/routes/api/devtools/config/theme/route.d.ts.map +1 -1
- package/dist/routes/api/devtools/config/theme/route.js +2 -0
- package/dist/routes/api/devtools/tests/[...path]/route.d.ts +2 -0
- package/dist/routes/api/devtools/tests/[...path]/route.d.ts.map +1 -1
- package/dist/routes/api/devtools/tests/[...path]/route.js +2 -0
- package/dist/routes/api/devtools/tests/route.d.ts +2 -0
- package/dist/routes/api/devtools/tests/route.d.ts.map +1 -1
- package/dist/routes/api/devtools/tests/route.js +2 -0
- package/dist/routes/api/health/route.d.ts +2 -0
- package/dist/routes/api/health/route.d.ts.map +1 -1
- package/dist/routes/api/health/route.js +2 -0
- package/dist/routes/api/internal/user-metadata/route.d.ts +2 -0
- package/dist/routes/api/internal/user-metadata/route.d.ts.map +1 -1
- package/dist/routes/api/internal/user-metadata/route.js +2 -0
- package/dist/routes/api/mcp/route.d.ts +2 -0
- package/dist/routes/api/mcp/route.d.ts.map +1 -1
- package/dist/routes/api/mcp/route.js +9 -1
- package/dist/routes/api/superadmin/subscriptions/route.d.ts +2 -0
- package/dist/routes/api/superadmin/subscriptions/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/subscriptions/route.js +2 -0
- package/dist/routes/api/superadmin/teams/[teamId]/route.d.ts +2 -0
- package/dist/routes/api/superadmin/teams/[teamId]/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/teams/[teamId]/route.js +2 -0
- package/dist/routes/api/superadmin/teams/route.d.ts +2 -0
- package/dist/routes/api/superadmin/teams/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/teams/route.js +2 -0
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts +2 -0
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/users/[userId]/route.js +2 -0
- package/dist/routes/api/superadmin/users/route.d.ts +2 -0
- package/dist/routes/api/superadmin/users/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/users/route.js +2 -0
- package/dist/routes/api/user/delete-account/route.d.ts +2 -0
- package/dist/routes/api/user/delete-account/route.d.ts.map +1 -1
- package/dist/routes/api/user/delete-account/route.js +2 -0
- package/dist/routes/api/user/plan-flags/route.d.ts +2 -0
- package/dist/routes/api/user/plan-flags/route.d.ts.map +1 -1
- package/dist/routes/api/user/plan-flags/route.js +2 -0
- package/dist/routes/api/user/profile/route.d.ts +2 -0
- package/dist/routes/api/user/profile/route.d.ts.map +1 -1
- package/dist/routes/api/user/profile/route.js +4 -3
- package/dist/routes/api/v1/[entity]/[id]/child/[childType]/[childId]/route.d.ts +2 -0
- package/dist/routes/api/v1/[entity]/[id]/child/[childType]/[childId]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/[entity]/[id]/child/[childType]/[childId]/route.js +2 -0
- package/dist/routes/api/v1/[entity]/[id]/child/[childType]/route.d.ts +2 -0
- package/dist/routes/api/v1/[entity]/[id]/child/[childType]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/[entity]/[id]/child/[childType]/route.js +2 -0
- package/dist/routes/api/v1/billing/cancel/route.d.ts +2 -0
- package/dist/routes/api/v1/billing/cancel/route.d.ts.map +1 -1
- package/dist/routes/api/v1/billing/cancel/route.js +2 -0
- package/dist/routes/api/v1/billing/change-plan/route.d.ts +2 -0
- package/dist/routes/api/v1/billing/change-plan/route.d.ts.map +1 -1
- package/dist/routes/api/v1/billing/change-plan/route.js +2 -0
- package/dist/routes/api/v1/billing/check-action/route.d.ts +2 -0
- package/dist/routes/api/v1/billing/check-action/route.d.ts.map +1 -1
- package/dist/routes/api/v1/billing/check-action/route.js +2 -0
- package/dist/routes/api/v1/billing/checkout/route.d.ts +2 -0
- package/dist/routes/api/v1/billing/checkout/route.d.ts.map +1 -1
- package/dist/routes/api/v1/billing/checkout/route.js +2 -0
- package/dist/routes/api/v1/billing/plans/route.d.ts +2 -0
- package/dist/routes/api/v1/billing/plans/route.d.ts.map +1 -1
- package/dist/routes/api/v1/billing/plans/route.js +2 -0
- package/dist/routes/api/v1/billing/portal/route.d.ts +2 -0
- package/dist/routes/api/v1/billing/portal/route.d.ts.map +1 -1
- package/dist/routes/api/v1/billing/portal/route.js +2 -0
- package/dist/routes/api/v1/blocks/[slug]/route.d.ts +2 -0
- package/dist/routes/api/v1/blocks/[slug]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/blocks/[slug]/route.js +2 -0
- package/dist/routes/api/v1/blocks/route.d.ts +2 -0
- package/dist/routes/api/v1/blocks/route.d.ts.map +1 -1
- package/dist/routes/api/v1/blocks/route.js +2 -0
- package/dist/routes/api/v1/blocks/validate/route.d.ts +2 -0
- package/dist/routes/api/v1/blocks/validate/route.d.ts.map +1 -1
- package/dist/routes/api/v1/blocks/validate/route.js +2 -0
- package/dist/routes/api/v1/devtools/blocks/route.d.ts +2 -1
- package/dist/routes/api/v1/devtools/blocks/route.d.ts.map +1 -1
- package/dist/routes/api/v1/devtools/blocks/route.js +2 -9
- package/dist/routes/api/v1/devtools/docs/route.d.ts +2 -0
- package/dist/routes/api/v1/devtools/docs/route.d.ts.map +1 -1
- package/dist/routes/api/v1/devtools/docs/route.js +2 -0
- package/dist/routes/api/v1/devtools/features/route.d.ts +2 -1
- package/dist/routes/api/v1/devtools/features/route.d.ts.map +1 -1
- package/dist/routes/api/v1/devtools/features/route.js +2 -9
- package/dist/routes/api/v1/devtools/flows/route.d.ts +2 -1
- package/dist/routes/api/v1/devtools/flows/route.d.ts.map +1 -1
- package/dist/routes/api/v1/devtools/flows/route.js +2 -9
- package/dist/routes/api/v1/devtools/scheduled-actions/route.d.ts +2 -1
- package/dist/routes/api/v1/devtools/scheduled-actions/route.d.ts.map +1 -1
- package/dist/routes/api/v1/devtools/scheduled-actions/route.js +2 -9
- package/dist/routes/api/v1/devtools/scheduled-actions/run/route.d.ts +2 -1
- package/dist/routes/api/v1/devtools/scheduled-actions/run/route.d.ts.map +1 -1
- package/dist/routes/api/v1/devtools/scheduled-actions/run/route.js +2 -9
- package/dist/routes/api/v1/devtools/testing/route.d.ts +2 -1
- package/dist/routes/api/v1/devtools/testing/route.d.ts.map +1 -1
- package/dist/routes/api/v1/devtools/testing/route.js +2 -9
- package/dist/routes/api/v1/media/[id]/route.d.ts +2 -0
- package/dist/routes/api/v1/media/[id]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/[id]/route.js +2 -0
- package/dist/routes/api/v1/media/[id]/tags/route.d.ts +2 -0
- package/dist/routes/api/v1/media/[id]/tags/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/[id]/tags/route.js +2 -0
- package/dist/routes/api/v1/media/check-duplicates/route.d.ts +2 -0
- package/dist/routes/api/v1/media/check-duplicates/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/check-duplicates/route.js +2 -0
- package/dist/routes/api/v1/media/route.d.ts +2 -0
- package/dist/routes/api/v1/media/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/route.js +2 -0
- package/dist/routes/api/v1/media/upload/route.d.ts +4 -1
- package/dist/routes/api/v1/media/upload/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/upload/route.js +12 -2
- package/dist/routes/api/v1/media-tags/route.d.ts +2 -0
- package/dist/routes/api/v1/media-tags/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media-tags/route.js +2 -0
- package/dist/routes/api/v1/post-categories/[id]/route.d.ts +2 -0
- package/dist/routes/api/v1/post-categories/[id]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/[id]/route.js +2 -0
- package/dist/routes/api/v1/post-categories/route.d.ts +2 -0
- package/dist/routes/api/v1/post-categories/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/route.js +2 -0
- package/dist/routes/api/v1/teams/[teamId]/members/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/[teamId]/members/route.js +14 -12
- package/dist/routes/api/v1/teams/[teamId]/subscription/route.d.ts +2 -0
- package/dist/routes/api/v1/teams/[teamId]/subscription/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/[teamId]/subscription/route.js +2 -0
- package/dist/routes/api/v1/teams/[teamId]/usage/[limitSlug]/route.d.ts +2 -0
- package/dist/routes/api/v1/teams/[teamId]/usage/[limitSlug]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/[teamId]/usage/[limitSlug]/route.js +2 -0
- package/dist/routes/api/v1/teams/switch/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/switch/route.js +4 -1
- package/dist/routes/api/v1/users/[id]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/users/[id]/route.js +3 -2
- package/dist/routes/api/v1/users/docs.md +34 -19
- package/dist/routes/api/v1/users/me/route.d.ts +6 -0
- package/dist/routes/api/v1/users/me/route.d.ts.map +1 -0
- package/dist/routes/api/v1/users/me/route.js +5 -0
- package/dist/routes/api/v1/users/presets.ts +2 -2
- package/dist/routes/dashboard/(main)/layout.cc.d.ts +4 -0
- package/dist/routes/dashboard/(main)/layout.cc.d.ts.map +1 -0
- package/dist/routes/dashboard/(main)/layout.cc.js +10 -0
- package/dist/routes/dashboard/(main)/layout.d.ts.map +1 -1
- package/dist/routes/dashboard/(main)/layout.js +3 -44
- package/dist/routes/dashboard/permission-denied/page.d.ts +1 -1
- package/dist/routes/dashboard/permission-denied/page.d.ts.map +1 -1
- package/dist/routes/dashboard/permission-denied/page.js +5 -1
- package/dist/routes/dashboard/settings/password/page.d.ts.map +1 -1
- package/dist/routes/dashboard/settings/password/page.js +10 -12
- package/dist/routes/devtools/config/page.js +1 -1
- package/dist/routes/devtools/layout.cc.d.ts.map +1 -1
- package/dist/routes/devtools/layout.cc.js +5 -1
- package/dist/routes/devtools/page.js +1 -1
- package/dist/routes/devtools/scheduled-actions/page.js +1 -1
- package/dist/routes/devtools/tests/[[...path]]/page.js +1 -1
- package/dist/routes/manifest.json +10 -0
- package/dist/routes/robots.d.ts +10 -0
- package/dist/routes/robots.d.ts.map +1 -0
- package/dist/routes/robots.js +10 -0
- package/dist/routes/superadmin/layout.cc.d.ts.map +1 -1
- package/dist/routes/superadmin/layout.cc.js +5 -1
- package/dist/routes/variants.json +12 -2
- package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/dist/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/dist/templates/next.config.mjs +2 -29
- package/dist/templates/projects/blog/api/authors/[username]/route.ts +3 -0
- package/dist/templates/projects/blog/api/authors/route.ts +3 -0
- package/dist/templates/projects/blog/api/posts/public/route.ts +3 -0
- package/dist/templates/projects/starter/README.md +2 -0
- package/dist/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/dist/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/dist/templates/proxy.ts +27 -9
- package/dist/templates/public/favicon.ico +0 -0
- package/dist/templates/public/uploads/README.md +5 -94
- package/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/migrations/090_sample_data.sql +10 -0
- package/package.json +2 -2
- package/scripts/build/registry/__tests__/api-docs-colocated.test.mjs +3 -0
- package/scripts/build/registry/__tests__/billing-registry.test.mjs +50 -1
- package/scripts/build/registry/__tests__/block-registry.test.mjs +3 -0
- package/scripts/build/registry/__tests__/icons-discovery.test.mjs +3 -0
- package/scripts/build/registry/__tests__/quiet-console.mjs +17 -0
- package/scripts/build/registry/__tests__/safe-fs.test.mjs +3 -0
- package/scripts/build/registry/__tests__/team-roles.test.mjs +3 -0
- package/scripts/build/registry/discovery/permissions.test.mjs +3 -0
- package/scripts/build/registry/discovery/plugin-capabilities.mjs +2 -2
- package/scripts/build/registry/generators/billing-registry.mjs +7 -3
- package/scripts/build/registry/host/README.md +25 -1
- package/scripts/build/registry/host/__tests__/host-area-access.test.mjs +32 -0
- package/scripts/build/registry/host/__tests__/host-entities.test.mjs +32 -1
- package/scripts/build/registry/host/__tests__/host-plan.test.mjs +12 -2
- package/scripts/build/registry/host/__tests__/host-proxy-areas.test.mjs +72 -1
- package/scripts/build/registry/host/entity-routes.mjs +9 -3
- package/scripts/build/registry/host/proxy-areas.mjs +65 -2
- package/scripts/build/registry/host/static-imports.mjs +4 -0
- package/scripts/build/routes-manifest.mjs +13 -1
- package/scripts/db/run-migrations.mjs +30 -10
- package/scripts/db/sample-data.mjs +44 -0
- package/scripts/db/ssl-config.mjs +12 -1
- package/scripts/db/verify-theme-migrations.mjs +6 -0
- package/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/templates/next.config.mjs +2 -29
- package/templates/projects/blog/api/authors/[username]/route.ts +3 -0
- package/templates/projects/blog/api/authors/route.ts +3 -0
- package/templates/projects/blog/api/posts/public/route.ts +3 -0
- package/templates/projects/starter/README.md +2 -0
- package/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/templates/proxy.ts +27 -9
- package/templates/public/favicon.ico +0 -0
- package/templates/public/uploads/README.md +5 -94
- package/tests/jest/__mocks__/@nextsparkjs/registries/api-presets-registry.ts +2 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/mcp-registry.ts +2 -0
- package/dist/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/dist/templates/public/uploads/.gitignore +0 -6
- package/dist/templates/public/uploads/temp/.gitkeep +0 -1
- package/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/templates/public/uploads/.gitignore +0 -6
- package/templates/public/uploads/temp/.gitkeep +0 -1
package/dist/lib/api/helpers.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
-
import { validateApiKey } from "./auth.js";
|
|
2
|
+
import { getValidatedApiKey, validateApiKey } from "./auth.js";
|
|
3
3
|
import { mutateWithRLS, queryWithRLS } from "../db.js";
|
|
4
|
-
import {
|
|
4
|
+
import { apiKeyRateLimitResponse } from "./rate-limit.js";
|
|
5
|
+
import { getRecordedClientIp } from "./client-ip.js";
|
|
5
6
|
import { getApplicationConfig } from "../config/index.js";
|
|
6
7
|
import { auth } from "../auth.js";
|
|
7
8
|
import { MetaService } from "../services/meta.service.js";
|
|
8
9
|
import { ScopeService } from "../services/scope.service.js";
|
|
9
10
|
import { getChildEntities } from "../entities/queries.js";
|
|
10
|
-
import { getCorsOrigins
|
|
11
|
+
import { getCorsOrigins } from "../utils/cors.js";
|
|
12
|
+
import { corsGrant, requestPathname, setCorsHeaders, varyOnOrigin } from "./cors-response.js";
|
|
11
13
|
import { isValidUUID } from "../utils/uuid.js";
|
|
12
14
|
import {
|
|
13
15
|
describeRoute,
|
|
@@ -34,7 +36,6 @@ async function validateAndAuthenticateRequest(request, options = {}) {
|
|
|
34
36
|
flags: userFlags,
|
|
35
37
|
isSession: true
|
|
36
38
|
};
|
|
37
|
-
console.log(`[Auth] Session authentication successful for user ${session.user.id} with role ${userRole}`);
|
|
38
39
|
return { auth: sessionAuth };
|
|
39
40
|
}
|
|
40
41
|
} catch (sessionError) {
|
|
@@ -56,7 +57,8 @@ async function validateAndAuthenticateRequest(request, options = {}) {
|
|
|
56
57
|
)
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
const rateLimitResponse = apiKeyRateLimitResponse(request, apiKeyAuth);
|
|
61
|
+
return rateLimitResponse ? { auth: apiKeyAuth, rateLimitResponse } : { auth: apiKeyAuth };
|
|
60
62
|
}
|
|
61
63
|
function generateScopesForRole(role, flags = []) {
|
|
62
64
|
const baseScopes = getBaseScopesFromRegistry();
|
|
@@ -101,47 +103,15 @@ async function validateAndAuthenticateApiRequest(request) {
|
|
|
101
103
|
if (!auth2) {
|
|
102
104
|
throw new Error("Invalid API key");
|
|
103
105
|
}
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
function applyApiKeyRateLimit(auth2) {
|
|
107
|
-
const rateLimitResult = checkRateLimit(auth2.keyId);
|
|
108
|
-
if (!rateLimitResult.allowed) {
|
|
109
|
-
const response = NextResponse.json(
|
|
110
|
-
{
|
|
111
|
-
success: false,
|
|
112
|
-
error: "Rate limit exceeded",
|
|
113
|
-
message: `Too many requests. Limit: ${rateLimitResult.limit} requests per minute`,
|
|
114
|
-
code: "RATE_LIMIT_EXCEEDED",
|
|
115
|
-
retryAfter: Math.ceil((rateLimitResult.resetTime - Date.now()) / 1e3)
|
|
116
|
-
},
|
|
117
|
-
{ status: 429 }
|
|
118
|
-
);
|
|
119
|
-
addRateLimitHeaders(response, auth2.keyId, auth2.scopes);
|
|
120
|
-
return { auth: auth2, rateLimitResponse: response };
|
|
121
|
-
}
|
|
122
|
-
return { auth: auth2 };
|
|
106
|
+
const rateLimitResponse = apiKeyRateLimitResponse(request, auth2);
|
|
107
|
+
return rateLimitResponse ? { auth: auth2, rateLimitResponse } : { auth: auth2 };
|
|
123
108
|
}
|
|
124
109
|
function getApiAuth(request) {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (!userId || !keyId || !scopesHeader) {
|
|
129
|
-
throw new Error("Missing API authentication headers");
|
|
130
|
-
}
|
|
131
|
-
let scopes = [];
|
|
132
|
-
try {
|
|
133
|
-
const parsed = JSON.parse(scopesHeader);
|
|
134
|
-
if (Array.isArray(parsed)) {
|
|
135
|
-
scopes = parsed.filter((s) => typeof s === "string");
|
|
136
|
-
}
|
|
137
|
-
} catch {
|
|
138
|
-
console.warn("[API] Invalid scopes header format");
|
|
110
|
+
const auth2 = getValidatedApiKey(request);
|
|
111
|
+
if (!auth2) {
|
|
112
|
+
throw new Error("No validated API key for this request");
|
|
139
113
|
}
|
|
140
|
-
return
|
|
141
|
-
userId,
|
|
142
|
-
keyId,
|
|
143
|
-
scopes
|
|
144
|
-
};
|
|
114
|
+
return auth2;
|
|
145
115
|
}
|
|
146
116
|
function checkScope(auth2, requiredScope) {
|
|
147
117
|
if (!scopesSatisfy(auth2.scopes, requiredScope)) {
|
|
@@ -203,7 +173,7 @@ async function logApiUsage(auth2, request, statusCode, responseTime, requestBody
|
|
|
203
173
|
try {
|
|
204
174
|
const endpoint = request.nextUrl.pathname;
|
|
205
175
|
const method = request.method;
|
|
206
|
-
const ipAddress =
|
|
176
|
+
const ipAddress = getRecordedClientIp(request.headers);
|
|
207
177
|
const userAgent = request.headers.get("user-agent");
|
|
208
178
|
await mutateWithRLS(
|
|
209
179
|
`INSERT INTO "api_audit_log"
|
|
@@ -229,31 +199,18 @@ async function logApiUsage(auth2, request, statusCode, responseTime, requestBody
|
|
|
229
199
|
function withApiLogging(handler) {
|
|
230
200
|
return async (request, ...args) => {
|
|
231
201
|
const startTime = Date.now();
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
try {
|
|
236
|
-
auth2 = getApiAuth(request);
|
|
237
|
-
} catch {
|
|
238
|
-
}
|
|
239
|
-
if (["POST", "PUT", "PATCH"].includes(request.method)) {
|
|
240
|
-
try {
|
|
241
|
-
const clonedRequest = request.clone();
|
|
242
|
-
requestBody = await clonedRequest.json();
|
|
243
|
-
} catch {
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
const response = await handler(request, ...args);
|
|
247
|
-
const responseTime = Date.now() - startTime;
|
|
202
|
+
const log = (status) => {
|
|
203
|
+
const auth2 = getValidatedApiKey(request);
|
|
248
204
|
if (auth2) {
|
|
249
|
-
logApiUsage(auth2, request,
|
|
205
|
+
logApiUsage(auth2, request, status, Date.now() - startTime).catch(console.error);
|
|
250
206
|
}
|
|
207
|
+
};
|
|
208
|
+
try {
|
|
209
|
+
const response = await handler(request, ...args);
|
|
210
|
+
log(response.status);
|
|
251
211
|
return response;
|
|
252
212
|
} catch (error) {
|
|
253
|
-
|
|
254
|
-
if (auth2) {
|
|
255
|
-
logApiUsage(auth2, request, 500, responseTime, requestBody).catch(console.error);
|
|
256
|
-
}
|
|
213
|
+
log(500);
|
|
257
214
|
throw error;
|
|
258
215
|
}
|
|
259
216
|
};
|
|
@@ -282,32 +239,15 @@ function getAllowedFiltersFromRegistry() {
|
|
|
282
239
|
async function addCorsHeaders(response, request) {
|
|
283
240
|
const env = process.env.NODE_ENV || "development";
|
|
284
241
|
const config = await getApplicationConfig();
|
|
285
|
-
|
|
286
|
-
let allowedOrigin = "null";
|
|
242
|
+
let grant = null;
|
|
287
243
|
if (request) {
|
|
288
244
|
const origin = request.headers.get("origin");
|
|
289
|
-
if (origin)
|
|
290
|
-
|
|
291
|
-
if (env === "development" && corsConfig.allowAllOrigins.development) {
|
|
292
|
-
allowedOrigin = normalizedOrigin;
|
|
293
|
-
} else {
|
|
294
|
-
const allowedOrigins = getCorsOrigins(config, env);
|
|
295
|
-
const matched = isOriginAllowed(normalizedOrigin, allowedOrigins);
|
|
296
|
-
if (matched) {
|
|
297
|
-
allowedOrigin = matched;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
} else if (env === "development" && corsConfig.allowAllOrigins.development) {
|
|
245
|
+
if (origin) grant = corsGrant(origin, config, env, requestPathname(request));
|
|
246
|
+
} else if (env === "development" && config.api.cors.allowAllOrigins.development) {
|
|
302
247
|
const allowedOrigins = getCorsOrigins(config, env);
|
|
303
|
-
|
|
248
|
+
grant = { origin: allowedOrigins[0] || "http://localhost:3000", credentials: true };
|
|
304
249
|
}
|
|
305
|
-
response.headers
|
|
306
|
-
response.headers.append("Vary", "Origin");
|
|
307
|
-
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
|
308
|
-
response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key, x-team-id, x-builder-source");
|
|
309
|
-
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
310
|
-
response.headers.set("Access-Control-Max-Age", "86400");
|
|
250
|
+
setCorsHeaders(response.headers, grant);
|
|
311
251
|
return response;
|
|
312
252
|
}
|
|
313
253
|
async function handleCorsPreflightRequest(request) {
|
|
@@ -321,25 +261,14 @@ async function wrapAuthHandlerWithCors(handler, request) {
|
|
|
321
261
|
statusText: response.statusText,
|
|
322
262
|
headers: new Headers(response.headers)
|
|
323
263
|
});
|
|
324
|
-
const env = process.env.NODE_ENV || "development";
|
|
325
|
-
const config = await getApplicationConfig();
|
|
326
|
-
const corsConfig = config.api.cors;
|
|
327
264
|
const origin = request.headers.get("origin");
|
|
328
265
|
if (origin) {
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
} else {
|
|
334
|
-
const allowedOrigins = getCorsOrigins(config, env);
|
|
335
|
-
const matched = isOriginAllowed(normalizedOrigin, allowedOrigins);
|
|
336
|
-
if (matched) {
|
|
337
|
-
allowedOrigin = matched;
|
|
338
|
-
}
|
|
266
|
+
const grant = corsGrant(origin, await getApplicationConfig(), process.env.NODE_ENV || "development", requestPathname(request));
|
|
267
|
+
if (grant) {
|
|
268
|
+
newResponse.headers.set("Access-Control-Allow-Origin", grant.origin);
|
|
269
|
+
if (grant.credentials) newResponse.headers.set("Access-Control-Allow-Credentials", "true");
|
|
339
270
|
}
|
|
340
|
-
newResponse.headers
|
|
341
|
-
newResponse.headers.set("Access-Control-Allow-Credentials", "true");
|
|
342
|
-
newResponse.headers.append("Vary", "Origin");
|
|
271
|
+
varyOnOrigin(newResponse.headers);
|
|
343
272
|
}
|
|
344
273
|
return newResponse;
|
|
345
274
|
}
|
package/dist/lib/api/keys.d.ts
CHANGED
|
@@ -35,6 +35,12 @@ export declare class ApiKeyManager {
|
|
|
35
35
|
*/
|
|
36
36
|
static suggestKeyName(scopes: string[]): string;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The API key a request presents: the token of `Authorization: Bearer <token>`,
|
|
40
|
+
* else the `x-api-key` header. validateApiKey checks this value, and the
|
|
41
|
+
* cookie-write origin check (lib/api/request-origin) reads it too.
|
|
42
|
+
*/
|
|
43
|
+
export declare function presentedApiKey(headers: Headers): string | null;
|
|
38
44
|
/**
|
|
39
45
|
* Definición de scopes disponibles para API keys
|
|
40
46
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/lib/api/keys.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,aAAa;IACxB;;;OAGG;WACU,cAAc,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBrF;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAM9C;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAKzC;;OAEG;WACU,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQlD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAA;KAAE;IAUpF;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;CAchD;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;CAgCb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,UAAU,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGrD;AAED,oFAAoF;AACpF,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxD;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;yBAIgC,QAAQ,EAAE;;;;;yBAKV,QAAQ,EAAE;;;;;yBAKV,QAAQ,EAAE;;;;;yBAKtB,QAAQ,EAAE;;;;;yBAKQ,QAAQ,EAAE;;;;;yBAKxD,QAAQ,EAAE;;CAErB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASd,CAAC;AAEX;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAmB9F"}
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/lib/api/keys.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,aAAa;IACxB;;;OAGG;WACU,cAAc,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBrF;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAM9C;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAKzC;;OAEG;WACU,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQlD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAA;KAAE;IAUpF;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;CAchD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI/D;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;CAgCb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,UAAU,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGrD;AAED,oFAAoF;AACpF,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxD;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;yBAIgC,QAAQ,EAAE;;;;;yBAKV,QAAQ,EAAE;;;;;yBAKV,QAAQ,EAAE;;;;;yBAKtB,QAAQ,EAAE;;;;;yBAKQ,QAAQ,EAAE;;;;;yBAKxD,QAAQ,EAAE;;CAErB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASd,CAAC;AAEX;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAmB9F"}
|
package/dist/lib/api/keys.js
CHANGED
|
@@ -68,6 +68,11 @@ class ApiKeyManager {
|
|
|
68
68
|
return `${categoryNames} Access Key`;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
function presentedApiKey(headers) {
|
|
72
|
+
const authorization = headers.get("authorization");
|
|
73
|
+
if (authorization == null ? void 0 : authorization.startsWith("Bearer ")) return authorization.substring(7);
|
|
74
|
+
return headers.get("x-api-key") || null;
|
|
75
|
+
}
|
|
71
76
|
const API_SCOPES = {
|
|
72
77
|
// Usuarios
|
|
73
78
|
"users:read": "Read user information",
|
|
@@ -183,5 +188,6 @@ export {
|
|
|
183
188
|
SCOPE_CATEGORIES,
|
|
184
189
|
getApiScopes,
|
|
185
190
|
getAppApiScopes,
|
|
186
|
-
getRateLimitForScopes
|
|
191
|
+
getRateLimitForScopes,
|
|
192
|
+
presentedApiKey
|
|
187
193
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission-middleware.d.ts","sourceRoot":"","sources":["../../../src/lib/api/permission-middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAc1C;;GAEG;AACH,eAAO,MAAM,WAAW,SAAS,CAAA;AAEjC;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAElF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAOjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"permission-middleware.d.ts","sourceRoot":"","sources":["../../../src/lib/api/permission-middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAc1C;;GAEG;AACH,eAAO,MAAM,WAAW,SAAS,CAAA;AAEjC;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAElF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAOjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC,CA8BtE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GACpB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAK9B"}
|
|
@@ -21,9 +21,7 @@ function getActionFromMethod(method, isCollection) {
|
|
|
21
21
|
}
|
|
22
22
|
async function checkEntityPermission(userId, teamId, entitySlug, action) {
|
|
23
23
|
const permission = getEntityPermission(entitySlug, action);
|
|
24
|
-
console.log(`[PermMiddleware] Checking permission: ${permission} for user ${userId} in team ${teamId}`);
|
|
25
24
|
const hasPermission = await checkPermission(userId, teamId, permission);
|
|
26
|
-
console.log(`[PermMiddleware] Result: ${hasPermission}`);
|
|
27
25
|
if (!hasPermission) {
|
|
28
26
|
return {
|
|
29
27
|
allowed: false,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { type ApiKeyAuth } from './auth';
|
|
2
3
|
import { type RateLimitTier } from '../rate-limit-redis';
|
|
3
4
|
export interface RateLimitResult {
|
|
4
5
|
allowed: boolean;
|
|
@@ -10,6 +11,13 @@ export interface RateLimitResult {
|
|
|
10
11
|
* Verifica el rate limit para un identificador específico
|
|
11
12
|
*/
|
|
12
13
|
export declare function checkRateLimit(identifier: string, limit?: number, windowMs?: number): RateLimitResult;
|
|
14
|
+
/**
|
|
15
|
+
* The per-key limit (checkRateLimit, by the key's id) for a request authenticated with `auth`: a 429 ready to
|
|
16
|
+
* return when the key is over it, null otherwise. Counted once per request, however many times the request is
|
|
17
|
+
* authenticated (authenticateRequest, validateAndAuthenticate*Request). The per-address limit of the route
|
|
18
|
+
* wrapper (withRateLimitTier) still applies on top. It is a safety cap, so DISABLE_RATE_LIMITING does not turn it off.
|
|
19
|
+
*/
|
|
20
|
+
export declare function apiKeyRateLimitResponse(request: Request, auth: ApiKeyAuth): NextResponse | null;
|
|
13
21
|
/**
|
|
14
22
|
* Rate limit types for distributed rate limiting
|
|
15
23
|
* Re-export from rate-limit-redis for convenience
|
|
@@ -47,6 +55,10 @@ export declare function applyRateLimit(request: NextRequest, keyId: string, scop
|
|
|
47
55
|
export declare function addRateLimitHeaders(response: NextResponse, keyId: string, scopes: string[]): NextResponse;
|
|
48
56
|
/**
|
|
49
57
|
* Middleware helper para aplicar rate limiting automáticamente
|
|
58
|
+
*
|
|
59
|
+
* Limits per API key, with the limits of the key's scopes, when the request presents a key that validates.
|
|
60
|
+
* The key id and scopes come from that validation, never from request headers; any other request runs the
|
|
61
|
+
* handler without this limit.
|
|
50
62
|
*/
|
|
51
63
|
export declare function withRateLimit<T extends unknown[]>(handler: (request: NextRequest, ...args: T) => Promise<NextResponse>): (request: NextRequest, ...args: T) => Promise<NextResponse>;
|
|
52
64
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/lib/api/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/lib/api/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGxD,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGzD,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAI7B,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,MAAa,EACpB,QAAQ,GAAE,MAAc,GACvB,eAAe,CAyCjB;AAKD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CA0B/F;AAED;;;GAGG;AACH,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,aAAqB,GAC1B,OAAO,CAAC,eAAe,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA6CpD;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,eAAe,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAwB5G;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,IAAI,OAAO,CAEzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,GACf,YAAY,GAAG,IAAI,CAkCrB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,GACf,YAAY,CASd;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,OAAO,EAAE,EAC/C,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,IAEtD,SAAS,WAAW,EAAE,GAAG,MAAM,CAAC,KAAG,OAAO,CAAC,YAAY,CAAC,CA2BvE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG,IAAI,CAaP;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAGlD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,KAAK,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CASD;AAED;;GAEG;AACH,wBAAgB,sBAAsB;;;;;EAErC;AA4DD,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,OAAO,EAAE,EACnD,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,EACpE,IAAI,GAAE,aAAqB,IAqCb,SAAS,WAAW,EAAE,GAAG,MAAM,CAAC,KAAG,OAAO,CAAC,YAAY,CAAC,CAIvE"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { unstable_rethrow } from "next/navigation";
|
|
3
|
-
import { getRateLimitForScopes } from "./keys.js";
|
|
3
|
+
import { getRateLimitForScopes, presentedApiKey } from "./keys.js";
|
|
4
|
+
import { validateApiKey } from "./auth.js";
|
|
4
5
|
import { rateLimitCache, getCacheKey } from "./cache.js";
|
|
6
|
+
import { getClientIp } from "./client-ip.js";
|
|
5
7
|
import {
|
|
6
8
|
checkRateLimit as checkRedisRateLimit,
|
|
7
9
|
maybeRedisConfigured
|
|
8
10
|
} from "../rate-limit-redis.js";
|
|
9
11
|
import { checkRequestOrigin } from "./request-origin.js";
|
|
12
|
+
import { withCors } from "./cors-response.js";
|
|
10
13
|
function checkRateLimit(identifier, limit = 1e3, windowMs = 6e4) {
|
|
11
14
|
const now = Date.now();
|
|
12
15
|
const cacheKey = getCacheKey("rate_limit", identifier);
|
|
@@ -40,6 +43,34 @@ function checkRateLimit(identifier, limit = 1e3, windowMs = 6e4) {
|
|
|
40
43
|
limit
|
|
41
44
|
};
|
|
42
45
|
}
|
|
46
|
+
const apiKeyChecks = /* @__PURE__ */ new WeakMap();
|
|
47
|
+
function apiKeyRateLimitResponse(request, auth) {
|
|
48
|
+
let result = apiKeyChecks.get(request);
|
|
49
|
+
if (!result) {
|
|
50
|
+
result = checkRateLimit(auth.keyId);
|
|
51
|
+
apiKeyChecks.set(request, result);
|
|
52
|
+
}
|
|
53
|
+
if (result.allowed) return null;
|
|
54
|
+
const retryAfter = Math.ceil((result.resetTime - Date.now()) / 1e3);
|
|
55
|
+
return NextResponse.json(
|
|
56
|
+
{
|
|
57
|
+
success: false,
|
|
58
|
+
error: "Rate limit exceeded",
|
|
59
|
+
message: `Too many requests. Limit: ${result.limit} requests per minute`,
|
|
60
|
+
code: "RATE_LIMIT_EXCEEDED",
|
|
61
|
+
retryAfter
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
status: 429,
|
|
65
|
+
headers: {
|
|
66
|
+
"X-RateLimit-Limit": result.limit.toString(),
|
|
67
|
+
"X-RateLimit-Remaining": "0",
|
|
68
|
+
"X-RateLimit-Reset": result.resetTime.toString(),
|
|
69
|
+
"Retry-After": retryAfter.toString()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
43
74
|
async function checkDistributedRateLimit(identifier, type = "api") {
|
|
44
75
|
if (isRateLimitingDisabled()) {
|
|
45
76
|
return { allowed: true, remaining: 999, resetTime: Date.now() + 6e4, limit: 999 };
|
|
@@ -152,20 +183,11 @@ function addRateLimitHeaders(response, keyId, scopes) {
|
|
|
152
183
|
function withRateLimit(handler) {
|
|
153
184
|
return async (request, ...args) => {
|
|
154
185
|
try {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
if (!keyId || !scopesHeader) {
|
|
186
|
+
const auth = presentedApiKey(request.headers) ? await validateApiKey(request) : null;
|
|
187
|
+
if (!auth) {
|
|
158
188
|
return handler(request, ...args);
|
|
159
189
|
}
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const parsed = JSON.parse(scopesHeader);
|
|
163
|
-
if (Array.isArray(parsed)) {
|
|
164
|
-
scopes = parsed.filter((s) => typeof s === "string");
|
|
165
|
-
}
|
|
166
|
-
} catch {
|
|
167
|
-
console.warn("[RateLimit] Invalid scopes header format");
|
|
168
|
-
}
|
|
190
|
+
const { keyId, scopes } = auth;
|
|
169
191
|
const rateLimitResponse = applyRateLimit(request, keyId, scopes);
|
|
170
192
|
if (rateLimitResponse) {
|
|
171
193
|
return rateLimitResponse;
|
|
@@ -208,27 +230,6 @@ function getAllRateLimitStats() {
|
|
|
208
230
|
function getRateLimitCacheStats() {
|
|
209
231
|
return rateLimitCache.getStats();
|
|
210
232
|
}
|
|
211
|
-
function getClientIp(request) {
|
|
212
|
-
const cfIp = request.headers.get("cf-connecting-ip");
|
|
213
|
-
if (cfIp) return cfIp;
|
|
214
|
-
const forwardedFor = request.headers.get("x-forwarded-for");
|
|
215
|
-
if (forwardedFor) {
|
|
216
|
-
const ips = forwardedFor.split(",").map((ip) => ip.trim()).filter(Boolean);
|
|
217
|
-
if (ips.length > 0) return ips[ips.length - 1];
|
|
218
|
-
}
|
|
219
|
-
const realIp = request.headers.get("x-real-ip");
|
|
220
|
-
if (realIp) return realIp;
|
|
221
|
-
const trueClientIp = request.headers.get("true-client-ip");
|
|
222
|
-
if (trueClientIp) return trueClientIp;
|
|
223
|
-
return "unknown";
|
|
224
|
-
}
|
|
225
|
-
function getUserIdentifier(request) {
|
|
226
|
-
const apiKey = request.headers.get("x-api-key");
|
|
227
|
-
if (apiKey) {
|
|
228
|
-
return `apikey:${apiKey.substring(0, 16)}`;
|
|
229
|
-
}
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
233
|
let rateLimitDisabledWarningLogged = false;
|
|
233
234
|
function isRateLimitingDisabled() {
|
|
234
235
|
const disabled = process.env.DISABLE_RATE_LIMITING === "true";
|
|
@@ -239,28 +240,34 @@ function isRateLimitingDisabled() {
|
|
|
239
240
|
return disabled;
|
|
240
241
|
}
|
|
241
242
|
function withRateLimitTier(handler, tier = "api") {
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
243
|
+
const limited = async (request, ...args) => {
|
|
244
|
+
const checked = checkRequestOrigin(request);
|
|
245
|
+
if (checked instanceof NextResponse) return checked;
|
|
246
|
+
request = checked;
|
|
245
247
|
if (isRateLimitingDisabled()) {
|
|
246
248
|
return handler(request, ...args);
|
|
247
249
|
}
|
|
248
|
-
const
|
|
249
|
-
const clientIp = getClientIp(request);
|
|
250
|
-
const identifier = userIdentifier ? `${tier}:${userIdentifier}` : `${tier}:ip:${clientIp}`;
|
|
250
|
+
const identifier = `${tier}:ip:${getClientIp(request.headers)}`;
|
|
251
251
|
const rateLimitResult = await checkDistributedRateLimit(identifier, tier);
|
|
252
252
|
if (!rateLimitResult.allowed) {
|
|
253
253
|
return createRateLimitErrorResponse(rateLimitResult);
|
|
254
254
|
}
|
|
255
255
|
const response = await handler(request, ...args);
|
|
256
|
-
response.
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
if (response.status !== 429) {
|
|
257
|
+
response.headers.set("X-RateLimit-Limit", rateLimitResult.limit.toString());
|
|
258
|
+
response.headers.set("X-RateLimit-Remaining", Math.max(0, rateLimitResult.remaining).toString());
|
|
259
|
+
response.headers.set("X-RateLimit-Reset", rateLimitResult.resetTime.toString());
|
|
260
|
+
}
|
|
259
261
|
return response;
|
|
260
262
|
};
|
|
263
|
+
return async (request, ...args) => {
|
|
264
|
+
const response = await limited(request, ...args);
|
|
265
|
+
return tier === "webhook" ? response : withCors(response, request);
|
|
266
|
+
};
|
|
261
267
|
}
|
|
262
268
|
export {
|
|
263
269
|
addRateLimitHeaders,
|
|
270
|
+
apiKeyRateLimitResponse,
|
|
264
271
|
applyRateLimit,
|
|
265
272
|
checkDistributedRateLimit,
|
|
266
273
|
checkRateLimit,
|
|
@@ -5,22 +5,31 @@
|
|
|
5
5
|
* from an origin the app trusts: its own (NEXT_PUBLIC_APP_URL / BETTER_AUTH_URL)
|
|
6
6
|
* or one in the list Better Auth already trusts for sign-in (see
|
|
7
7
|
* getTrustedOrigins). withRateLimitTier runs it before every handler it wraps.
|
|
8
|
+
* Under the admin and developer areas (isAppOnlyCorsPath) only the app's own
|
|
9
|
+
* origin is trusted, plus, outside production, the request's own origin when it
|
|
10
|
+
* is a private-network address (#170); no other listed origin.
|
|
8
11
|
*
|
|
9
12
|
* What passes without a trusted origin, and why:
|
|
10
13
|
* - Reads (GET/HEAD/OPTIONS).
|
|
11
14
|
* - Requests without the session cookie: there is no session for them to use.
|
|
12
|
-
* - Requests
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* - Requests that present an API key (`Authorization: Bearer sk_...` or
|
|
16
|
+
* x-api-key, in the API-key format). They are let through without their
|
|
17
|
+
* session cookie, so the handler can authenticate them only by the key: a key
|
|
18
|
+
* that does not validate gets 401, never the cookie's session. Any other
|
|
19
|
+
* Authorization value is not a credential here, and the check applies. Handlers
|
|
20
|
+
* should read the session from the request they are given, not from
|
|
21
|
+
* next/headers.
|
|
15
22
|
* - Requests with neither Origin nor Referer, unless their body is one an HTML
|
|
16
23
|
* form can send. Browsers send Origin on every write, so these come from
|
|
17
24
|
* native or server-side clients; requiring a non-form content type keeps
|
|
18
25
|
* a body without origin information from ever being read as a form post.
|
|
19
26
|
*/
|
|
20
|
-
import { NextResponse } from 'next/server';
|
|
27
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
21
28
|
/**
|
|
22
|
-
* Returns a 403 response when a cookie-authenticated
|
|
23
|
-
* trusted origin
|
|
29
|
+
* Checks a write's origin. Returns a 403 response when a cookie-authenticated
|
|
30
|
+
* write does not come from a trusted origin. Otherwise returns the request the
|
|
31
|
+
* handler should get: the same request, or, for a write that would be refused
|
|
32
|
+
* but presents an API key, a copy without its session cookie.
|
|
24
33
|
*/
|
|
25
|
-
export declare function checkRequestOrigin(request:
|
|
34
|
+
export declare function checkRequestOrigin(request: NextRequest): NextResponse | NextRequest;
|
|
26
35
|
//# sourceMappingURL=request-origin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-origin.d.ts","sourceRoot":"","sources":["../../../src/lib/api/request-origin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"request-origin.d.ts","sourceRoot":"","sources":["../../../src/lib/api/request-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAgEvD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,GAAG,WAAW,CAoBnF"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { NextResponse } from "next/server";
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
2
|
import { APP_CONFIG_MERGED } from "../config/index.js";
|
|
3
3
|
import { hasSessionCookie } from "../auth/session-hint.js";
|
|
4
|
-
import { getTrustedOrigins, isOriginAllowed, normalizeOrigin } from "../utils/cors.js";
|
|
4
|
+
import { getTrustedOrigins, isOriginAllowed, isPrivateLanOrigin, normalizeCorsEnvironment, normalizeOrigin } from "../utils/cors.js";
|
|
5
|
+
import { ApiKeyManager, presentedApiKey } from "./keys.js";
|
|
6
|
+
import { appOrigins, isAppOnlyCorsPath, requestPathname } from "./cors-response.js";
|
|
5
7
|
const WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
6
8
|
const FORM_CONTENT_TYPES = /* @__PURE__ */ new Set(["text/plain", "application/x-www-form-urlencoded", "multipart/form-data"]);
|
|
7
9
|
function requestOrigin(headers) {
|
|
@@ -15,31 +17,52 @@ function requestOrigin(headers) {
|
|
|
15
17
|
return "null";
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const SESSION_COOKIE_NAME = /^(?:__Secure-)?better-auth[.-]/;
|
|
21
|
+
function withoutSessionCookies(request) {
|
|
22
|
+
const headers = {};
|
|
23
|
+
request.headers.forEach((value, key) => {
|
|
24
|
+
headers[key] = value;
|
|
25
|
+
});
|
|
26
|
+
const kept = (headers.cookie ?? "").split(";").map((part) => part.trim()).filter((part) => part && !SESSION_COOKIE_NAME.test(part));
|
|
27
|
+
if (kept.length) headers.cookie = kept.join("; ");
|
|
28
|
+
else delete headers.cookie;
|
|
29
|
+
return new NextRequest(request.url, {
|
|
30
|
+
method: request.method,
|
|
31
|
+
headers,
|
|
32
|
+
body: request.body,
|
|
33
|
+
signal: request.signal,
|
|
34
|
+
nextConfig: { basePath: request.nextUrl.basePath }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function isTrustedWriteOrigin(request, origin) {
|
|
38
|
+
const path = requestPathname(request);
|
|
39
|
+
if (path === void 0 || isAppOnlyCorsPath(path)) {
|
|
40
|
+
const lan = normalizeCorsEnvironment(process.env.NODE_ENV || "development") !== "production" && isPrivateLanOrigin(origin);
|
|
41
|
+
return lan || appOrigins().includes(origin);
|
|
42
|
+
}
|
|
43
|
+
return isOriginAllowed(origin, getTrustedOrigins(APP_CONFIG_MERGED, origin)) !== null;
|
|
44
|
+
}
|
|
45
|
+
function refusal(error, code) {
|
|
46
|
+
return NextResponse.json({ success: false, error, code }, { status: 403 });
|
|
22
47
|
}
|
|
23
48
|
function checkRequestOrigin(request) {
|
|
24
|
-
if (!WRITE_METHODS.has(request.method.toUpperCase())) return
|
|
49
|
+
if (!WRITE_METHODS.has(request.method.toUpperCase())) return request;
|
|
25
50
|
const headers = request.headers;
|
|
26
|
-
if (!hasSessionCookie(headers.get("cookie"))) return
|
|
27
|
-
|
|
51
|
+
if (!hasSessionCookie(headers.get("cookie"))) return request;
|
|
52
|
+
let refused;
|
|
28
53
|
const origin = requestOrigin(headers);
|
|
29
54
|
if (origin === null) {
|
|
30
55
|
const contentType = (headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
|
|
31
|
-
if (!FORM_CONTENT_TYPES.has(contentType)) return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
56
|
+
if (!FORM_CONTENT_TYPES.has(contentType)) return request;
|
|
57
|
+
refused = refusal("This request needs an Origin header", "ORIGIN_REQUIRED");
|
|
58
|
+
} else {
|
|
59
|
+
const normalized = normalizeOrigin(origin);
|
|
60
|
+
if (isTrustedWriteOrigin(request, normalized)) return request;
|
|
61
|
+
refused = refusal("Request origin not allowed", "ORIGIN_NOT_ALLOWED");
|
|
36
62
|
}
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
return
|
|
40
|
-
{ success: false, error: "Request origin not allowed", code: "ORIGIN_NOT_ALLOWED" },
|
|
41
|
-
{ status: 403 }
|
|
42
|
-
);
|
|
63
|
+
const apiKey = presentedApiKey(headers);
|
|
64
|
+
if (apiKey && ApiKeyManager.validateKeyFormat(apiKey)) return withoutSessionCookies(request);
|
|
65
|
+
return refused;
|
|
43
66
|
}
|
|
44
67
|
export {
|
|
45
68
|
checkRequestOrigin
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leaves for the login page with a full page load, base path included. Sign-out and the dashboard guards use it.
|
|
3
|
+
*
|
|
4
|
+
* With Cache Components a client navigation out of the dashboard leaves it mounted, hidden, with its last render
|
|
5
|
+
* ("signed out"); shown again after the next sign-in, its effects act on that and send the user back to /login. The
|
|
6
|
+
* login page comes back as the form the last sign-in left. Loading the page drops the React tree, the session store
|
|
7
|
+
* and the query cache together, whichever way the session ended (this tab's sign-out, another tab's, an expiry).
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadLoginPage(): void;
|
|
10
|
+
//# sourceMappingURL=load-login-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-login-page.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/load-login-page.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAEpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-readiness.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/runtime-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAGpB,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"runtime-readiness.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/runtime-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAGpB,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAIlE,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,aAAa,CAAA;AAEpB,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAE5D,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,WAAW,GAAG,kBAAkB,CAAC,GAAG,IAAI,CAAA;IAClF,GAAG,CAAC,EAAE,kBAAkB,CAAA;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,wBAAwB,EAAE,OAAO,CAAA;IACjC,mFAAmF;IACnF,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,OAAO,GAAG,aAAa,CAAA;IAC/B,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,YAAY,EAAE,sBAAsB,CAAA;CACrC;AAaD,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAEtG;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,2BAAgC,GAAG,IAAI,CAgBzF;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAYrG;AAiBD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE1F;AAED,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE3F;AAkDD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4E1B;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,EAChC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,CAAC,CAGnB"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import { AUTH_CONFIG } from "../config/index.js";
|
|
3
3
|
import { isPasswordLoginEnabled } from "./auth-methods.js";
|
|
4
|
+
import { warnClientIpSourceAtStartup } from "../api/client-ip.js";
|
|
5
|
+
import { warnSampleAccountsAtStartup } from "./sample-accounts.js";
|
|
4
6
|
import {
|
|
5
7
|
authReadinessConfigurationFromEnv,
|
|
6
8
|
evaluateAuthReadiness
|
|
@@ -19,6 +21,8 @@ function getRuntimeAuthReadiness(options = {}) {
|
|
|
19
21
|
return evaluateAuthReadiness(runtimeInput(options));
|
|
20
22
|
}
|
|
21
23
|
function logAuthReadinessAtStartup(options = {}) {
|
|
24
|
+
warnClientIpSourceAtStartup(options.env ?? process.env);
|
|
25
|
+
void warnSampleAccountsAtStartup(options.env ?? process.env);
|
|
22
26
|
try {
|
|
23
27
|
const env = options.env ?? process.env;
|
|
24
28
|
if (env.NODE_ENV !== "production") return;
|