@nextsparkjs/core 0.1.0-beta.92 → 0.1.0-beta.94
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/dashboard/block-editor/array-field.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/array-field.js +55 -3
- package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/dynamic-form.js +82 -2
- package/dist/components/dashboard/navigation/DynamicNavigation.d.ts.map +1 -1
- package/dist/components/dashboard/navigation/DynamicNavigation.js +7 -1
- package/dist/components/devtools/scheduled-actions/actions-table.d.ts +1 -0
- package/dist/components/devtools/scheduled-actions/actions-table.d.ts.map +1 -1
- package/dist/components/devtools/scheduled-actions/actions-table.js +182 -46
- package/dist/components/devtools/scheduled-actions/types.d.ts +1 -0
- package/dist/components/devtools/scheduled-actions/types.d.ts.map +1 -1
- package/dist/components/media/MediaCard.d.ts +23 -0
- package/dist/components/media/MediaCard.d.ts.map +1 -0
- package/dist/components/media/MediaCard.js +154 -0
- package/dist/components/media/MediaDetailPanel.d.ts +17 -0
- package/dist/components/media/MediaDetailPanel.d.ts.map +1 -0
- package/dist/components/media/MediaDetailPanel.js +331 -0
- package/dist/components/media/MediaGrid.d.ts +26 -0
- package/dist/components/media/MediaGrid.d.ts.map +1 -0
- package/dist/components/media/MediaGrid.js +77 -0
- package/dist/components/media/MediaLibrary.d.ts +20 -0
- package/dist/components/media/MediaLibrary.d.ts.map +1 -0
- package/dist/components/media/MediaLibrary.js +229 -0
- package/dist/components/media/MediaList.d.ts +24 -0
- package/dist/components/media/MediaList.d.ts.map +1 -0
- package/dist/components/media/MediaList.js +181 -0
- package/dist/components/media/MediaSelector.d.ts +19 -0
- package/dist/components/media/MediaSelector.d.ts.map +1 -0
- package/dist/components/media/MediaSelector.js +145 -0
- package/dist/components/media/MediaTagFilter.d.ts +16 -0
- package/dist/components/media/MediaTagFilter.d.ts.map +1 -0
- package/dist/components/media/MediaTagFilter.js +122 -0
- package/dist/components/media/MediaToolbar.d.ts +25 -0
- package/dist/components/media/MediaToolbar.d.ts.map +1 -0
- package/dist/components/media/MediaToolbar.js +136 -0
- package/dist/components/media/MediaUploadZone.d.ts +19 -0
- package/dist/components/media/MediaUploadZone.d.ts.map +1 -0
- package/dist/components/media/MediaUploadZone.js +248 -0
- package/dist/components/media/index.d.ts +15 -0
- package/dist/components/media/index.d.ts.map +1 -0
- package/dist/components/media/index.js +20 -0
- package/dist/contexts/TeamContext.js +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts +4 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
- package/dist/hooks/useEnsureUserMetadata.js +85 -60
- package/dist/hooks/useEntityMutations.d.ts.map +1 -1
- package/dist/hooks/useEntityMutations.js +5 -9
- package/dist/hooks/useMedia.d.ts +56 -0
- package/dist/hooks/useMedia.d.ts.map +1 -0
- package/dist/hooks/useMedia.js +181 -0
- package/dist/hooks/useMediaUpload.d.ts +27 -0
- package/dist/hooks/useMediaUpload.d.ts.map +1 -0
- package/dist/hooks/useMediaUpload.js +36 -0
- package/dist/hooks/useUserSettings.d.ts +5 -4
- package/dist/hooks/useUserSettings.d.ts.map +1 -1
- package/dist/hooks/useUserSettings.js +42 -40
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/lib/api/auth/dual-auth.d.ts +6 -2
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +5 -9
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +3 -3
- package/dist/lib/config/app.config.d.ts.map +1 -1
- package/dist/lib/config/app.config.js +37 -0
- package/dist/lib/config/config-sync.d.ts +1 -0
- package/dist/lib/config/config-sync.d.ts.map +1 -1
- package/dist/lib/config/config-sync.js +2 -0
- package/dist/lib/config/types.d.ts +29 -0
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/media/schemas.d.ts +39 -0
- package/dist/lib/media/schemas.d.ts.map +1 -0
- package/dist/lib/media/schemas.js +32 -0
- package/dist/lib/media/types.d.ts +69 -0
- package/dist/lib/media/types.d.ts.map +1 -0
- package/dist/lib/media/types.js +0 -0
- package/dist/lib/media/utils.d.ts +26 -0
- package/dist/lib/media/utils.d.ts.map +1 -0
- package/dist/lib/media/utils.js +33 -0
- package/dist/lib/rate-limit-redis.d.ts.map +1 -1
- package/dist/lib/rate-limit-redis.js +13 -4
- package/dist/lib/scheduled-actions/initializer.d.ts +6 -3
- package/dist/lib/scheduled-actions/initializer.d.ts.map +1 -1
- package/dist/lib/scheduled-actions/initializer.js +11 -6
- package/dist/lib/scheduled-actions/processor.d.ts +20 -4
- package/dist/lib/scheduled-actions/processor.d.ts.map +1 -1
- package/dist/lib/scheduled-actions/processor.js +128 -34
- package/dist/lib/scheduled-actions/registry.d.ts +3 -0
- package/dist/lib/scheduled-actions/registry.d.ts.map +1 -1
- package/dist/lib/scheduled-actions/registry.js +2 -1
- package/dist/lib/scheduled-actions/scheduler.d.ts +1 -1
- package/dist/lib/scheduled-actions/scheduler.d.ts.map +1 -1
- package/dist/lib/scheduled-actions/scheduler.js +76 -38
- package/dist/lib/scheduled-actions/types.d.ts +73 -0
- package/dist/lib/scheduled-actions/types.d.ts.map +1 -1
- package/dist/lib/selectors/core-selectors.d.ts +102 -0
- package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
- package/dist/lib/selectors/core-selectors.js +3 -1
- package/dist/lib/selectors/domains/block-editor.selectors.d.ts +8 -0
- package/dist/lib/selectors/domains/block-editor.selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/block-editor.selectors.js +9 -0
- package/dist/lib/selectors/domains/devtools.selectors.d.ts +6 -0
- package/dist/lib/selectors/domains/devtools.selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/devtools.selectors.js +6 -0
- package/dist/lib/selectors/domains/index.d.ts +1 -0
- package/dist/lib/selectors/domains/index.d.ts.map +1 -1
- package/dist/lib/selectors/domains/index.js +2 -0
- package/dist/lib/selectors/domains/media.selectors.d.ts +96 -0
- package/dist/lib/selectors/domains/media.selectors.d.ts.map +1 -0
- package/dist/lib/selectors/domains/media.selectors.js +103 -0
- package/dist/lib/selectors/selectors.d.ts +204 -0
- package/dist/lib/selectors/selectors.d.ts.map +1 -1
- package/dist/lib/services/index.d.ts +2 -0
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +2 -0
- package/dist/lib/services/media.service.d.ts +158 -0
- package/dist/lib/services/media.service.d.ts.map +1 -0
- package/dist/lib/services/media.service.js +410 -0
- package/dist/messages/de/devtools.json +16 -0
- package/dist/messages/de/index.d.ts +16 -0
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/en/admin.json +4 -1
- package/dist/messages/en/devtools.json +16 -0
- package/dist/messages/en/index.d.ts +167 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/index.js +2 -0
- package/dist/messages/en/index.ts +2 -0
- package/dist/messages/en/media.json +147 -0
- package/dist/messages/en/navigation.json +1 -0
- package/dist/messages/es/admin.json +4 -1
- package/dist/messages/es/devtools.json +16 -0
- package/dist/messages/es/index.d.ts +167 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/index.js +2 -0
- package/dist/messages/es/index.ts +2 -0
- package/dist/messages/es/media.json +147 -0
- package/dist/messages/es/navigation.json +1 -0
- package/dist/messages/fr/devtools.json +16 -0
- package/dist/messages/fr/index.d.ts +16 -0
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/it/devtools.json +16 -0
- package/dist/messages/it/index.d.ts +16 -0
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/pt/devtools.json +16 -0
- package/dist/messages/pt/index.d.ts +16 -0
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/migrations/017_scheduled_actions_table.sql +21 -0
- package/dist/migrations/021_media.sql +154 -0
- package/dist/migrations/090_sample_data.sql +53 -0
- package/dist/styles/classes.json +36 -3
- package/dist/styles/ui.css +1 -1
- package/dist/templates/app/api/devtools/config/entities/route.ts +18 -11
- package/dist/templates/app/api/devtools/config/theme/route.ts +5 -4
- package/dist/templates/app/api/devtools/tests/[...path]/route.ts +6 -5
- package/dist/templates/app/api/devtools/tests/route.ts +5 -4
- package/dist/templates/app/api/health/route.ts +6 -4
- package/dist/templates/app/api/internal/user-metadata/route.ts +3 -2
- package/dist/templates/app/api/superadmin/subscriptions/route.ts +5 -6
- package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +6 -7
- package/dist/templates/app/api/superadmin/teams/route.ts +5 -6
- package/dist/templates/app/api/superadmin/users/[userId]/route.ts +11 -16
- package/dist/templates/app/api/superadmin/users/route.ts +9 -10
- package/dist/templates/app/api/user/delete-account/route.ts +3 -2
- package/dist/templates/app/api/user/plan-flags/route.ts +11 -24
- package/dist/templates/app/api/user/profile/route.ts +7 -6
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +16 -18
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +17 -19
- package/dist/templates/app/api/v1/[entity]/[id]/route.ts +10 -12
- package/dist/templates/app/api/v1/[entity]/route.ts +9 -11
- package/dist/templates/app/api/v1/api-keys/[id]/route.ts +9 -8
- package/dist/templates/app/api/v1/api-keys/route.ts +7 -6
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +3 -2
- package/dist/templates/app/api/v1/billing/cancel/route.ts +15 -14
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +10 -9
- package/dist/templates/app/api/v1/billing/check-action/route.ts +8 -7
- package/dist/templates/app/api/v1/billing/checkout/route.ts +10 -9
- package/dist/templates/app/api/v1/billing/plans/route.ts +5 -4
- package/dist/templates/app/api/v1/billing/portal/route.ts +9 -8
- package/dist/templates/app/api/v1/blocks/[slug]/route.ts +4 -3
- package/dist/templates/app/api/v1/blocks/route.ts +3 -2
- package/dist/templates/app/api/v1/blocks/validate/route.ts +5 -3
- package/dist/templates/app/api/v1/cron/process/route.ts +4 -6
- package/dist/templates/app/api/v1/devtools/blocks/route.ts +3 -2
- package/dist/templates/app/api/v1/devtools/docs/route.ts +3 -2
- package/dist/templates/app/api/v1/devtools/features/route.ts +3 -2
- package/dist/templates/app/api/v1/devtools/flows/route.ts +3 -2
- package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +125 -3
- package/dist/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +110 -0
- package/dist/templates/app/api/v1/devtools/testing/route.ts +3 -2
- package/dist/templates/app/api/v1/media/[id]/route.ts +144 -0
- package/dist/templates/app/api/v1/media/[id]/tags/route.ts +154 -0
- package/dist/templates/app/api/v1/media/check-duplicates/route.ts +56 -0
- package/dist/templates/app/api/v1/media/route.ts +56 -0
- package/dist/templates/app/api/v1/media/upload/route.ts +157 -33
- package/dist/templates/app/api/v1/media-tags/route.ts +65 -0
- package/dist/templates/app/api/v1/plugin/[...path]/route.ts +16 -15
- package/dist/templates/app/api/v1/plugin/route.ts +3 -2
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +10 -9
- package/dist/templates/app/api/v1/post-categories/route.ts +5 -4
- package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +3 -3
- package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +3 -3
- package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +3 -2
- package/dist/templates/app/api/v1/team-invitations/route.ts +3 -2
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +5 -4
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +3 -2
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +3 -2
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +5 -4
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +5 -5
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +31 -58
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -2
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +5 -4
- package/dist/templates/app/api/v1/teams/route.ts +18 -17
- package/dist/templates/app/api/v1/teams/switch/route.ts +3 -2
- package/dist/templates/app/api/v1/theme/[...path]/route.ts +16 -15
- package/dist/templates/app/api/v1/theme/route.ts +3 -2
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +7 -6
- package/dist/templates/app/api/v1/users/[id]/route.ts +9 -8
- package/dist/templates/app/api/v1/users/route.ts +7 -6
- package/dist/templates/app/dashboard/(main)/media/page.tsx +607 -0
- package/dist/templates/contents/themes/starter/messages/de/dev.json +106 -0
- package/dist/templates/contents/themes/starter/messages/de/index.ts +2 -0
- package/dist/templates/contents/themes/starter/messages/en/dev.json +106 -0
- package/dist/templates/contents/themes/starter/messages/en/index.ts +2 -0
- package/dist/templates/contents/themes/starter/messages/es/dev.json +106 -0
- package/dist/templates/contents/themes/starter/messages/es/index.ts +2 -0
- package/dist/templates/contents/themes/starter/messages/fr/dev.json +106 -0
- package/dist/templates/contents/themes/starter/messages/fr/index.ts +2 -0
- package/dist/templates/contents/themes/starter/messages/it/dev.json +106 -0
- package/dist/templates/contents/themes/starter/messages/it/index.ts +2 -0
- package/dist/templates/contents/themes/starter/messages/pt/dev.json +106 -0
- package/dist/templates/contents/themes/starter/messages/pt/index.ts +2 -0
- package/dist/templates/contents/themes/starter/styles/globals.css +14 -0
- package/dist/templates/instrumentation.ts +33 -0
- package/dist/types/blocks.d.ts +1 -1
- package/dist/types/blocks.d.ts.map +1 -1
- package/migrations/017_scheduled_actions_table.sql +21 -0
- package/migrations/021_media.sql +154 -0
- package/migrations/090_sample_data.sql +53 -0
- package/package.json +3 -2
- package/scripts/build/registry/config.mjs +41 -0
- package/scripts/build/registry/discovery/templates.mjs +0 -1
- package/scripts/build/registry/generators/entity-registry.mjs +16 -6
- package/scripts/build/registry/generators/route-handlers.mjs +8 -2
- package/scripts/build/registry/generators/template-registry.mjs +16 -4
- package/scripts/build/registry/post-build/route-cleanup.mjs +0 -1
- package/scripts/build/registry/validate-env.test.mjs +92 -0
- package/scripts/build/registry.mjs +18 -1
- package/scripts/deploy/vercel-deploy.mjs +1 -1
- package/templates/app/api/devtools/config/entities/route.ts +18 -11
- package/templates/app/api/devtools/config/theme/route.ts +5 -4
- package/templates/app/api/devtools/tests/[...path]/route.ts +6 -5
- package/templates/app/api/devtools/tests/route.ts +5 -4
- package/templates/app/api/health/route.ts +6 -4
- package/templates/app/api/internal/user-metadata/route.ts +3 -2
- package/templates/app/api/superadmin/subscriptions/route.ts +5 -6
- package/templates/app/api/superadmin/teams/[teamId]/route.ts +6 -7
- package/templates/app/api/superadmin/teams/route.ts +5 -6
- package/templates/app/api/superadmin/users/[userId]/route.ts +11 -16
- package/templates/app/api/superadmin/users/route.ts +9 -10
- package/templates/app/api/user/delete-account/route.ts +3 -2
- package/templates/app/api/user/plan-flags/route.ts +11 -24
- package/templates/app/api/user/profile/route.ts +7 -6
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +16 -18
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +17 -19
- package/templates/app/api/v1/[entity]/[id]/route.ts +10 -12
- package/templates/app/api/v1/[entity]/route.ts +9 -11
- package/templates/app/api/v1/api-keys/[id]/route.ts +9 -8
- package/templates/app/api/v1/api-keys/route.ts +7 -6
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +3 -2
- package/templates/app/api/v1/billing/cancel/route.ts +15 -14
- package/templates/app/api/v1/billing/change-plan/route.ts +10 -9
- package/templates/app/api/v1/billing/check-action/route.ts +8 -7
- package/templates/app/api/v1/billing/checkout/route.ts +10 -9
- package/templates/app/api/v1/billing/plans/route.ts +5 -4
- package/templates/app/api/v1/billing/portal/route.ts +9 -8
- package/templates/app/api/v1/blocks/[slug]/route.ts +4 -3
- package/templates/app/api/v1/blocks/route.ts +3 -2
- package/templates/app/api/v1/blocks/validate/route.ts +5 -3
- package/templates/app/api/v1/cron/process/route.ts +4 -6
- package/templates/app/api/v1/devtools/blocks/route.ts +3 -2
- package/templates/app/api/v1/devtools/docs/route.ts +3 -2
- package/templates/app/api/v1/devtools/features/route.ts +3 -2
- package/templates/app/api/v1/devtools/flows/route.ts +3 -2
- package/templates/app/api/v1/devtools/scheduled-actions/route.ts +125 -3
- package/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +110 -0
- package/templates/app/api/v1/devtools/testing/route.ts +3 -2
- package/templates/app/api/v1/media/[id]/route.ts +144 -0
- package/templates/app/api/v1/media/[id]/tags/route.ts +154 -0
- package/templates/app/api/v1/media/check-duplicates/route.ts +56 -0
- package/templates/app/api/v1/media/route.ts +56 -0
- package/templates/app/api/v1/media/upload/route.ts +157 -33
- package/templates/app/api/v1/media-tags/route.ts +65 -0
- package/templates/app/api/v1/plugin/[...path]/route.ts +16 -15
- package/templates/app/api/v1/plugin/route.ts +3 -2
- package/templates/app/api/v1/post-categories/[id]/route.ts +10 -9
- package/templates/app/api/v1/post-categories/route.ts +5 -4
- package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +3 -3
- package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +3 -3
- package/templates/app/api/v1/team-invitations/[token]/route.ts +3 -2
- package/templates/app/api/v1/team-invitations/route.ts +3 -2
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +5 -4
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +3 -2
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +3 -2
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +5 -4
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +5 -5
- package/templates/app/api/v1/teams/[teamId]/route.ts +31 -58
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +3 -2
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +5 -4
- package/templates/app/api/v1/teams/route.ts +18 -17
- package/templates/app/api/v1/teams/switch/route.ts +3 -2
- package/templates/app/api/v1/theme/[...path]/route.ts +16 -15
- package/templates/app/api/v1/theme/route.ts +3 -2
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +7 -6
- package/templates/app/api/v1/users/[id]/route.ts +9 -8
- package/templates/app/api/v1/users/route.ts +7 -6
- package/templates/app/dashboard/(main)/media/page.tsx +607 -0
- package/templates/contents/themes/starter/messages/de/dev.json +106 -0
- package/templates/contents/themes/starter/messages/de/index.ts +2 -0
- package/templates/contents/themes/starter/messages/en/dev.json +106 -0
- package/templates/contents/themes/starter/messages/en/index.ts +2 -0
- package/templates/contents/themes/starter/messages/es/dev.json +106 -0
- package/templates/contents/themes/starter/messages/es/index.ts +2 -0
- package/templates/contents/themes/starter/messages/fr/dev.json +106 -0
- package/templates/contents/themes/starter/messages/fr/index.ts +2 -0
- package/templates/contents/themes/starter/messages/it/dev.json +106 -0
- package/templates/contents/themes/starter/messages/it/index.ts +2 -0
- package/templates/contents/themes/starter/messages/pt/dev.json +106 -0
- package/templates/contents/themes/starter/messages/pt/index.ts +2 -0
- package/templates/contents/themes/starter/styles/globals.css +14 -0
- package/templates/instrumentation.ts +33 -0
- package/dist/presets/plugin/.env.example.template +0 -19
- package/dist/presets/plugin/entities/.gitkeep +0 -18
- package/dist/presets/theme/blocks/.gitkeep +0 -17
- package/dist/presets/theme/public/brand/.gitkeep +0 -8
- package/dist/presets/theme/tests/cypress/.gitkeep +0 -10
- package/dist/templates/contents/plugins/starter/plugin/.env.example.template +0 -19
- package/templates/contents/plugins/starter/plugin/.env.example.template +0 -19
|
@@ -9,18 +9,19 @@
|
|
|
9
9
|
* P2: Stripe Integration
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { NextRequest } from 'next/server'
|
|
12
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
13
13
|
import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
14
14
|
import { createCheckoutSession } from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
15
15
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
16
16
|
import { z } from 'zod'
|
|
17
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
17
18
|
|
|
18
19
|
const checkoutSchema = z.object({
|
|
19
20
|
planSlug: z.string().min(1, 'Plan slug is required'),
|
|
20
21
|
billingPeriod: z.enum(['monthly', 'yearly']).default('monthly')
|
|
21
22
|
})
|
|
22
23
|
|
|
23
|
-
export
|
|
24
|
+
export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
24
25
|
// 1. Dual authentication
|
|
25
26
|
const authResult = await authenticateRequest(request)
|
|
26
27
|
|
|
@@ -33,7 +34,7 @@ export async function POST(request: NextRequest) {
|
|
|
33
34
|
try {
|
|
34
35
|
body = await request.json()
|
|
35
36
|
} catch {
|
|
36
|
-
return
|
|
37
|
+
return NextResponse.json(
|
|
37
38
|
{ success: false, error: 'Invalid JSON body' },
|
|
38
39
|
{ status: 400 }
|
|
39
40
|
)
|
|
@@ -41,7 +42,7 @@ export async function POST(request: NextRequest) {
|
|
|
41
42
|
|
|
42
43
|
const parseResult = checkoutSchema.safeParse(body)
|
|
43
44
|
if (!parseResult.success) {
|
|
44
|
-
return
|
|
45
|
+
return NextResponse.json(
|
|
45
46
|
{
|
|
46
47
|
success: false,
|
|
47
48
|
error: 'Validation failed',
|
|
@@ -59,7 +60,7 @@ export async function POST(request: NextRequest) {
|
|
|
59
60
|
authResult.user.defaultTeamId
|
|
60
61
|
|
|
61
62
|
if (!teamId) {
|
|
62
|
-
return
|
|
63
|
+
return NextResponse.json(
|
|
63
64
|
{
|
|
64
65
|
success: false,
|
|
65
66
|
error: 'No team context available. Please provide x-team-id header.'
|
|
@@ -73,7 +74,7 @@ export async function POST(request: NextRequest) {
|
|
|
73
74
|
const actionResult = membership.canPerformAction('billing.checkout')
|
|
74
75
|
|
|
75
76
|
if (!actionResult.allowed) {
|
|
76
|
-
return
|
|
77
|
+
return NextResponse.json(
|
|
77
78
|
{
|
|
78
79
|
success: false,
|
|
79
80
|
error: actionResult.message,
|
|
@@ -104,7 +105,7 @@ export async function POST(request: NextRequest) {
|
|
|
104
105
|
customerId: subscription?.externalCustomerId || undefined
|
|
105
106
|
})
|
|
106
107
|
|
|
107
|
-
return
|
|
108
|
+
return NextResponse.json({
|
|
108
109
|
success: true,
|
|
109
110
|
data: {
|
|
110
111
|
url: session.url,
|
|
@@ -113,7 +114,7 @@ export async function POST(request: NextRequest) {
|
|
|
113
114
|
})
|
|
114
115
|
} catch (error) {
|
|
115
116
|
console.error('[checkout] Error creating checkout session:', error)
|
|
116
|
-
return
|
|
117
|
+
return NextResponse.json(
|
|
117
118
|
{
|
|
118
119
|
success: false,
|
|
119
120
|
error: error instanceof Error ? error.message : 'Failed to create checkout session'
|
|
@@ -121,4 +122,4 @@ export async function POST(request: NextRequest) {
|
|
|
121
122
|
{ status: 500 }
|
|
122
123
|
)
|
|
123
124
|
}
|
|
124
|
-
}
|
|
125
|
+
}, 'write');
|
|
@@ -10,8 +10,9 @@ import { validateAndAuthenticateRequest, createApiResponse, createApiError } fro
|
|
|
10
10
|
import { PlanService } from '@nextsparkjs/core/lib/services'
|
|
11
11
|
import { createPlanSchema } from '@nextsparkjs/core/lib/billing/schema'
|
|
12
12
|
import { mutateWithRLS } from '@nextsparkjs/core/lib/db'
|
|
13
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
13
14
|
|
|
14
|
-
export
|
|
15
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
15
16
|
// Plans list is partially public (public plans visible to all, hidden plans only to superadmin)
|
|
16
17
|
let includeHidden = false
|
|
17
18
|
|
|
@@ -31,9 +32,9 @@ export async function GET(request: NextRequest) {
|
|
|
31
32
|
console.error('[Billing API] Error fetching plans:', error)
|
|
32
33
|
return createApiError('Failed to fetch plans', 500)
|
|
33
34
|
}
|
|
34
|
-
}
|
|
35
|
+
}, 'read');
|
|
35
36
|
|
|
36
|
-
export
|
|
37
|
+
export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
37
38
|
// Authenticate request
|
|
38
39
|
const { auth, rateLimitResponse } = await validateAndAuthenticateRequest(request)
|
|
39
40
|
if (rateLimitResponse) return rateLimitResponse
|
|
@@ -82,4 +83,4 @@ export async function POST(request: NextRequest) {
|
|
|
82
83
|
console.error('[Billing API] Error creating plan:', error)
|
|
83
84
|
return createApiError('Failed to create plan', 500)
|
|
84
85
|
}
|
|
85
|
-
}
|
|
86
|
+
}, 'strict');
|
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
* P6: Customer Portal
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { NextRequest } from 'next/server'
|
|
10
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
11
11
|
import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
12
12
|
import { createPortalSession } from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
13
13
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
14
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
14
15
|
|
|
15
|
-
export
|
|
16
|
+
export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
16
17
|
// 1. Dual authentication
|
|
17
18
|
const authResult = await authenticateRequest(request)
|
|
18
19
|
|
|
@@ -26,7 +27,7 @@ export async function POST(request: NextRequest) {
|
|
|
26
27
|
authResult.user.defaultTeamId
|
|
27
28
|
|
|
28
29
|
if (!teamId) {
|
|
29
|
-
return
|
|
30
|
+
return NextResponse.json(
|
|
30
31
|
{
|
|
31
32
|
success: false,
|
|
32
33
|
error: 'No team context available. Please provide x-team-id header.'
|
|
@@ -40,7 +41,7 @@ export async function POST(request: NextRequest) {
|
|
|
40
41
|
const actionResult = membership.canPerformAction('billing.portal')
|
|
41
42
|
|
|
42
43
|
if (!actionResult.allowed) {
|
|
43
|
-
return
|
|
44
|
+
return NextResponse.json(
|
|
44
45
|
{
|
|
45
46
|
success: false,
|
|
46
47
|
error: actionResult.message,
|
|
@@ -56,7 +57,7 @@ export async function POST(request: NextRequest) {
|
|
|
56
57
|
const subscription = await SubscriptionService.getActive(teamId)
|
|
57
58
|
|
|
58
59
|
if (!subscription?.externalCustomerId) {
|
|
59
|
-
return
|
|
60
|
+
return NextResponse.json(
|
|
60
61
|
{
|
|
61
62
|
success: false,
|
|
62
63
|
error: 'No billing account found. Please upgrade to a paid plan first.'
|
|
@@ -73,13 +74,13 @@ export async function POST(request: NextRequest) {
|
|
|
73
74
|
returnUrl
|
|
74
75
|
})
|
|
75
76
|
|
|
76
|
-
return
|
|
77
|
+
return NextResponse.json({
|
|
77
78
|
success: true,
|
|
78
79
|
data: { url: session.url }
|
|
79
80
|
})
|
|
80
81
|
} catch (error) {
|
|
81
82
|
console.error('[portal] Error creating portal session:', error)
|
|
82
|
-
return
|
|
83
|
+
return NextResponse.json(
|
|
83
84
|
{
|
|
84
85
|
success: false,
|
|
85
86
|
error: error instanceof Error ? error.message : 'Failed to create portal session'
|
|
@@ -87,4 +88,4 @@ export async function POST(request: NextRequest) {
|
|
|
87
88
|
{ status: 500 }
|
|
88
89
|
)
|
|
89
90
|
}
|
|
90
|
-
}
|
|
91
|
+
}, 'write');
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
2
2
|
import { BLOCK_REGISTRY } from '@nextsparkjs/registries/block-registry'
|
|
3
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
3
4
|
|
|
4
|
-
export
|
|
5
|
+
export const GET = withRateLimitTier(async (
|
|
5
6
|
request: NextRequest,
|
|
6
7
|
{ params }: { params: Promise<{ slug: string }> }
|
|
7
|
-
): Promise<NextResponse> {
|
|
8
|
+
): Promise<NextResponse> => {
|
|
8
9
|
try {
|
|
9
10
|
const { slug } = await params
|
|
10
11
|
const block = BLOCK_REGISTRY[slug]
|
|
@@ -26,4 +27,4 @@ export async function GET(
|
|
|
26
27
|
console.error('Error fetching block:', err)
|
|
27
28
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
28
29
|
}
|
|
29
|
-
}
|
|
30
|
+
}, 'read');
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
2
2
|
import { BLOCK_REGISTRY, BLOCK_CATEGORIES } from '@nextsparkjs/registries/block-registry'
|
|
3
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
3
4
|
|
|
4
|
-
export
|
|
5
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
5
6
|
try {
|
|
6
7
|
const { searchParams } = new URL(request.url)
|
|
7
8
|
const category = searchParams.get('category')
|
|
@@ -42,4 +43,4 @@ export async function GET(request: NextRequest) {
|
|
|
42
43
|
console.error('Error listing blocks:', err)
|
|
43
44
|
return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
|
|
44
45
|
}
|
|
45
|
-
}
|
|
46
|
+
}, 'read');
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server'
|
|
2
2
|
import { BLOCK_REGISTRY } from '@nextsparkjs/registries/block-registry'
|
|
3
3
|
import { z } from 'zod'
|
|
4
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
4
5
|
|
|
5
6
|
const requestSchema = z.object({
|
|
6
7
|
blockSlug: z.string(),
|
|
7
8
|
props: z.record(z.string(), z.unknown())
|
|
8
9
|
})
|
|
9
10
|
|
|
10
|
-
export
|
|
11
|
+
export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
11
12
|
try {
|
|
12
13
|
const body = await request.json()
|
|
13
14
|
const { blockSlug, props } = requestSchema.parse(body)
|
|
@@ -24,7 +25,8 @@ export async function POST(request: NextRequest) {
|
|
|
24
25
|
|
|
25
26
|
try {
|
|
26
27
|
// Dynamic import is allowed here (server-side validation, not registry loading)
|
|
27
|
-
|
|
28
|
+
// webpackIgnore tells webpack to skip static analysis for this intentional dynamic import
|
|
29
|
+
const schemaModule = await import(/* webpackIgnore: true */ block.schemaPath)
|
|
28
30
|
const schema = schemaModule.schema
|
|
29
31
|
|
|
30
32
|
schema.parse(props)
|
|
@@ -42,4 +44,4 @@ export async function POST(request: NextRequest) {
|
|
|
42
44
|
console.error('Error validating block:', err)
|
|
43
45
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
44
46
|
}
|
|
45
|
-
}
|
|
47
|
+
}, 'write');
|
|
@@ -12,22 +12,20 @@
|
|
|
12
12
|
import { NextRequest, NextResponse } from 'next/server'
|
|
13
13
|
import {
|
|
14
14
|
processPendingActions,
|
|
15
|
-
cleanupOldActions
|
|
16
|
-
initializeScheduledActions
|
|
15
|
+
cleanupOldActions
|
|
17
16
|
} from '@nextsparkjs/core/lib/scheduled-actions'
|
|
18
17
|
import type { ProcessResult } from '@nextsparkjs/core/lib/scheduled-actions'
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* Process pending scheduled actions
|
|
22
21
|
* Protected by CRON_SECRET for security
|
|
22
|
+
*
|
|
23
|
+
* Note: Handler initialization is handled by instrumentation.ts at server startup.
|
|
24
|
+
* This endpoint only processes actions, not initialization.
|
|
23
25
|
*/
|
|
24
26
|
export async function GET(request: NextRequest): Promise<NextResponse> {
|
|
25
27
|
const startTime = Date.now()
|
|
26
28
|
|
|
27
|
-
// Ensure action handlers are registered in this context
|
|
28
|
-
// (initializeScheduledActions has its own guard against duplicates)
|
|
29
|
-
initializeScheduledActions()
|
|
30
|
-
|
|
31
29
|
try {
|
|
32
30
|
// Validate CRON_SECRET
|
|
33
31
|
const cronSecret = request.headers.get('x-cron-secret')
|
|
@@ -16,8 +16,9 @@ import {
|
|
|
16
16
|
} from '@nextsparkjs/core/lib/api/auth/devtools-auth'
|
|
17
17
|
import { BLOCK_REGISTRY } from '@nextsparkjs/registries/block-registry'
|
|
18
18
|
import { TAGS_REGISTRY, COVERAGE_SUMMARY } from '@nextsparkjs/registries/testing-registry'
|
|
19
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
19
20
|
|
|
20
|
-
export
|
|
21
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
21
22
|
// Authenticate request
|
|
22
23
|
const authResult = await authenticateRequest(request)
|
|
23
24
|
|
|
@@ -69,7 +70,7 @@ export async function GET(request: NextRequest) {
|
|
|
69
70
|
},
|
|
70
71
|
},
|
|
71
72
|
})
|
|
72
|
-
}
|
|
73
|
+
}, 'read');
|
|
73
74
|
|
|
74
75
|
export async function OPTIONS() {
|
|
75
76
|
return new NextResponse(null, {
|
|
@@ -15,6 +15,7 @@ import { NextRequest, NextResponse } from 'next/server'
|
|
|
15
15
|
import { readFile } from 'fs/promises'
|
|
16
16
|
import { join, dirname } from 'path'
|
|
17
17
|
import { existsSync } from 'fs'
|
|
18
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Valid path patterns for documentation files
|
|
@@ -80,7 +81,7 @@ function getBasePaths(): string[] {
|
|
|
80
81
|
return paths
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
export
|
|
84
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
84
85
|
const searchParams = request.nextUrl.searchParams
|
|
85
86
|
const docPath = searchParams.get('path')
|
|
86
87
|
|
|
@@ -147,4 +148,4 @@ export async function GET(request: NextRequest) {
|
|
|
147
148
|
{ status: 500 }
|
|
148
149
|
)
|
|
149
150
|
}
|
|
150
|
-
}
|
|
151
|
+
}, 'read');
|
|
@@ -18,8 +18,9 @@ import {
|
|
|
18
18
|
FEATURE_REGISTRY,
|
|
19
19
|
COVERAGE_SUMMARY,
|
|
20
20
|
} from '@nextsparkjs/registries/testing-registry'
|
|
21
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
21
22
|
|
|
22
|
-
export
|
|
23
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
23
24
|
// Authenticate request
|
|
24
25
|
const authResult = await authenticateRequest(request)
|
|
25
26
|
|
|
@@ -48,7 +49,7 @@ export async function GET(request: NextRequest) {
|
|
|
48
49
|
},
|
|
49
50
|
},
|
|
50
51
|
})
|
|
51
|
-
}
|
|
52
|
+
}, 'read');
|
|
52
53
|
|
|
53
54
|
export async function OPTIONS() {
|
|
54
55
|
return new NextResponse(null, {
|
|
@@ -18,8 +18,9 @@ import {
|
|
|
18
18
|
FLOW_REGISTRY,
|
|
19
19
|
COVERAGE_SUMMARY,
|
|
20
20
|
} from '@nextsparkjs/registries/testing-registry'
|
|
21
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
21
22
|
|
|
22
|
-
export
|
|
23
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
23
24
|
// Authenticate request
|
|
24
25
|
const authResult = await authenticateRequest(request)
|
|
25
26
|
|
|
@@ -48,7 +49,7 @@ export async function GET(request: NextRequest) {
|
|
|
48
49
|
},
|
|
49
50
|
},
|
|
50
51
|
})
|
|
51
|
-
}
|
|
52
|
+
}, 'read');
|
|
52
53
|
|
|
53
54
|
export async function OPTIONS() {
|
|
54
55
|
return new NextResponse(null, {
|
|
@@ -17,8 +17,10 @@ import {
|
|
|
17
17
|
import { queryWithRLS } from '@nextsparkjs/core/lib/db'
|
|
18
18
|
import type { ScheduledAction, ScheduledActionStatus } from '@nextsparkjs/core/lib/scheduled-actions/types'
|
|
19
19
|
import { getAllRegisteredActions } from '@nextsparkjs/core/lib/scheduled-actions/registry'
|
|
20
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
21
|
+
import { scheduleAction } from '@nextsparkjs/core/lib/scheduled-actions/scheduler'
|
|
20
22
|
|
|
21
|
-
export
|
|
23
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
22
24
|
// Authenticate request
|
|
23
25
|
const authResult = await authenticateRequest(request)
|
|
24
26
|
|
|
@@ -78,6 +80,7 @@ export async function GET(request: NextRequest) {
|
|
|
78
80
|
"completedAt",
|
|
79
81
|
"errorMessage",
|
|
80
82
|
attempts,
|
|
83
|
+
"maxRetries",
|
|
81
84
|
"recurringInterval",
|
|
82
85
|
"createdAt",
|
|
83
86
|
"updatedAt"
|
|
@@ -92,6 +95,16 @@ export async function GET(request: NextRequest) {
|
|
|
92
95
|
// Get all registered action types
|
|
93
96
|
const registeredActions = getAllRegisteredActions()
|
|
94
97
|
|
|
98
|
+
// Count failed actions (last 24 hours)
|
|
99
|
+
const failedCountQuery = `
|
|
100
|
+
SELECT COUNT(*) as count
|
|
101
|
+
FROM "scheduled_actions"
|
|
102
|
+
WHERE status = 'failed'
|
|
103
|
+
AND "completedAt" > NOW() - INTERVAL '24 hours'
|
|
104
|
+
`
|
|
105
|
+
const failedCountResult = await queryWithRLS<{ count: string }>(failedCountQuery, [], null)
|
|
106
|
+
const failedCount = parseInt(failedCountResult[0]?.count || '0', 10)
|
|
107
|
+
|
|
95
108
|
return NextResponse.json({
|
|
96
109
|
success: true,
|
|
97
110
|
data: {
|
|
@@ -104,16 +117,125 @@ export async function GET(request: NextRequest) {
|
|
|
104
117
|
},
|
|
105
118
|
meta: {
|
|
106
119
|
registeredActionTypes: registeredActions,
|
|
120
|
+
failedCount,
|
|
107
121
|
},
|
|
108
122
|
},
|
|
109
123
|
})
|
|
110
|
-
}
|
|
124
|
+
}, 'read');
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* POST /api/v1/devtools/scheduled-actions
|
|
128
|
+
*
|
|
129
|
+
* Retry a failed action by creating a new action with the same payload.
|
|
130
|
+
* Requires superadmin or developer user role.
|
|
131
|
+
*
|
|
132
|
+
* Body:
|
|
133
|
+
* {
|
|
134
|
+
* "actionId": "uuid-of-failed-action"
|
|
135
|
+
* }
|
|
136
|
+
*/
|
|
137
|
+
export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
138
|
+
// Authenticate request
|
|
139
|
+
const authResult = await authenticateRequest(request)
|
|
140
|
+
|
|
141
|
+
if (!authResult.success) {
|
|
142
|
+
return createDevtoolsUnauthorizedResponse()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check DevTools access permission
|
|
146
|
+
if (!canAccessDevtoolsApi(authResult)) {
|
|
147
|
+
return createDevtoolsAccessDeniedResponse()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Parse request body
|
|
151
|
+
const body = await request.json()
|
|
152
|
+
const { actionId } = body
|
|
153
|
+
|
|
154
|
+
if (!actionId) {
|
|
155
|
+
return NextResponse.json({
|
|
156
|
+
success: false,
|
|
157
|
+
error: 'actionId is required'
|
|
158
|
+
}, { status: 400 })
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Fetch the failed action
|
|
162
|
+
const actions = await queryWithRLS<ScheduledAction>(
|
|
163
|
+
`SELECT
|
|
164
|
+
id,
|
|
165
|
+
"actionType",
|
|
166
|
+
status,
|
|
167
|
+
payload,
|
|
168
|
+
"teamId",
|
|
169
|
+
"lockGroup"
|
|
170
|
+
FROM "scheduled_actions"
|
|
171
|
+
WHERE id = $1`,
|
|
172
|
+
[actionId],
|
|
173
|
+
null
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if (actions.length === 0) {
|
|
177
|
+
return NextResponse.json({
|
|
178
|
+
success: false,
|
|
179
|
+
error: 'Action not found'
|
|
180
|
+
}, { status: 404 })
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const failedAction = actions[0]
|
|
184
|
+
|
|
185
|
+
// Only allow retrying failed actions
|
|
186
|
+
if (failedAction.status !== 'failed') {
|
|
187
|
+
return NextResponse.json({
|
|
188
|
+
success: false,
|
|
189
|
+
error: 'Only failed actions can be retried'
|
|
190
|
+
}, { status: 400 })
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check if there's already a pending action with the same actionType and payload
|
|
194
|
+
// Use JSONB containment operator for reliable comparison
|
|
195
|
+
const existingPending = await queryWithRLS<{ id: string }>(
|
|
196
|
+
`SELECT id
|
|
197
|
+
FROM "scheduled_actions"
|
|
198
|
+
WHERE "actionType" = $1
|
|
199
|
+
AND payload @> $2::jsonb
|
|
200
|
+
AND $2::jsonb @> payload
|
|
201
|
+
AND status = 'pending'
|
|
202
|
+
LIMIT 1`,
|
|
203
|
+
[failedAction.actionType, JSON.stringify(failedAction.payload)],
|
|
204
|
+
null
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
if (existingPending.length > 0) {
|
|
208
|
+
return NextResponse.json({
|
|
209
|
+
success: false,
|
|
210
|
+
error: 'A pending action with the same payload already exists',
|
|
211
|
+
existingActionId: existingPending[0].id
|
|
212
|
+
}, { status: 409 })
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Create a new action with the same payload
|
|
216
|
+
const newActionId = await scheduleAction(
|
|
217
|
+
failedAction.actionType,
|
|
218
|
+
failedAction.payload,
|
|
219
|
+
{
|
|
220
|
+
scheduledAt: new Date(), // Schedule immediately
|
|
221
|
+
teamId: failedAction.teamId ?? undefined,
|
|
222
|
+
lockGroup: failedAction.lockGroup ?? undefined
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return NextResponse.json({
|
|
227
|
+
success: true,
|
|
228
|
+
data: {
|
|
229
|
+
newActionId
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
}, 'write');
|
|
111
233
|
|
|
112
234
|
export async function OPTIONS() {
|
|
113
235
|
return new NextResponse(null, {
|
|
114
236
|
status: 204,
|
|
115
237
|
headers: {
|
|
116
|
-
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
|
238
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
117
239
|
'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key',
|
|
118
240
|
},
|
|
119
241
|
})
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevTools Scheduled Actions Run API
|
|
3
|
+
*
|
|
4
|
+
* POST /api/v1/devtools/scheduled-actions/run
|
|
5
|
+
*
|
|
6
|
+
* Executes a pending action immediately.
|
|
7
|
+
* Requires superadmin or developer user role.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
11
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
12
|
+
import {
|
|
13
|
+
canAccessDevtoolsApi,
|
|
14
|
+
createDevtoolsAccessDeniedResponse,
|
|
15
|
+
createDevtoolsUnauthorizedResponse,
|
|
16
|
+
} from '@nextsparkjs/core/lib/api/auth/devtools-auth'
|
|
17
|
+
import { queryWithRLS } from '@nextsparkjs/core/lib/db'
|
|
18
|
+
import type { ScheduledAction } from '@nextsparkjs/core/lib/scheduled-actions/types'
|
|
19
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
20
|
+
import { executeAction } from '@nextsparkjs/core/lib/scheduled-actions/processor'
|
|
21
|
+
|
|
22
|
+
export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
23
|
+
// Authenticate request
|
|
24
|
+
const authResult = await authenticateRequest(request)
|
|
25
|
+
|
|
26
|
+
if (!authResult.success) {
|
|
27
|
+
return createDevtoolsUnauthorizedResponse()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check DevTools access permission
|
|
31
|
+
if (!canAccessDevtoolsApi(authResult)) {
|
|
32
|
+
return createDevtoolsAccessDeniedResponse()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Parse request body
|
|
36
|
+
const body = await request.json()
|
|
37
|
+
const { actionId } = body
|
|
38
|
+
|
|
39
|
+
if (!actionId) {
|
|
40
|
+
return NextResponse.json({
|
|
41
|
+
success: false,
|
|
42
|
+
error: 'actionId is required'
|
|
43
|
+
}, { status: 400 })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fetch the pending action
|
|
47
|
+
const actions = await queryWithRLS<ScheduledAction>(
|
|
48
|
+
`SELECT
|
|
49
|
+
id,
|
|
50
|
+
"actionType",
|
|
51
|
+
status,
|
|
52
|
+
payload,
|
|
53
|
+
"teamId",
|
|
54
|
+
"scheduledAt",
|
|
55
|
+
attempts,
|
|
56
|
+
"maxRetries",
|
|
57
|
+
"recurringInterval",
|
|
58
|
+
"recurrenceType",
|
|
59
|
+
"lockGroup"
|
|
60
|
+
FROM "scheduled_actions"
|
|
61
|
+
WHERE id = $1`,
|
|
62
|
+
[actionId],
|
|
63
|
+
null
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if (actions.length === 0) {
|
|
67
|
+
return NextResponse.json({
|
|
68
|
+
success: false,
|
|
69
|
+
error: 'Action not found'
|
|
70
|
+
}, { status: 404 })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const action = actions[0]
|
|
74
|
+
|
|
75
|
+
// Only allow running pending actions
|
|
76
|
+
if (action.status !== 'pending') {
|
|
77
|
+
return NextResponse.json({
|
|
78
|
+
success: false,
|
|
79
|
+
error: 'Only pending actions can be executed'
|
|
80
|
+
}, { status: 400 })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
// Execute the action immediately
|
|
85
|
+
await executeAction(action)
|
|
86
|
+
|
|
87
|
+
return NextResponse.json({
|
|
88
|
+
success: true,
|
|
89
|
+
data: {
|
|
90
|
+
executed: true
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('[DevTools] Failed to execute action:', error)
|
|
95
|
+
return NextResponse.json({
|
|
96
|
+
success: false,
|
|
97
|
+
error: error instanceof Error ? error.message : 'Failed to execute action'
|
|
98
|
+
}, { status: 500 })
|
|
99
|
+
}
|
|
100
|
+
}, 'write');
|
|
101
|
+
|
|
102
|
+
export async function OPTIONS() {
|
|
103
|
+
return new NextResponse(null, {
|
|
104
|
+
status: 204,
|
|
105
|
+
headers: {
|
|
106
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
107
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key',
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
}
|
|
@@ -20,8 +20,9 @@ import {
|
|
|
20
20
|
FEATURE_REGISTRY,
|
|
21
21
|
FLOW_REGISTRY,
|
|
22
22
|
} from '@nextsparkjs/registries/testing-registry'
|
|
23
|
+
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
23
24
|
|
|
24
|
-
export
|
|
25
|
+
export const GET = withRateLimitTier(async (request: NextRequest) => {
|
|
25
26
|
// Authenticate request
|
|
26
27
|
const authResult = await authenticateRequest(request)
|
|
27
28
|
|
|
@@ -69,7 +70,7 @@ export async function GET(request: NextRequest) {
|
|
|
69
70
|
},
|
|
70
71
|
},
|
|
71
72
|
})
|
|
72
|
-
}
|
|
73
|
+
}, 'read');
|
|
73
74
|
|
|
74
75
|
export async function OPTIONS() {
|
|
75
76
|
return new NextResponse(null, {
|