@nextsparkjs/core 0.1.0-beta.82 → 0.1.0-beta.83
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/entities/wrappers/EntityDetailWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityDetailWrapper.js +11 -39
- package/dist/hooks/useEntityQuery.d.ts.map +1 -1
- package/dist/hooks/useEntityQuery.js +21 -3
- package/dist/lib/theme/get-default-theme-mode.d.ts +11 -0
- package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
- package/dist/lib/theme/get-default-theme-mode.js +42 -25
- package/dist/styles/classes.json +1 -1
- package/dist/types/theme.d.ts +2 -0
- package/dist/types/theme.d.ts.map +1 -1
- package/package.json +16 -16
- package/scripts/build/docs-registry.mjs +0 -0
- package/scripts/create-theme.mjs +0 -0
- package/scripts/deploy/release-version.mjs +0 -0
- package/scripts/deploy/vercel-deploy.mjs +0 -0
- package/scripts/dev/watch-plugins.mjs +0 -0
- package/scripts/maintenance/update-core.mjs +0 -0
- package/scripts/setup/npm-postinstall.mjs +0 -0
- package/scripts/setup/setup-claude.mjs +0 -0
- package/scripts/validation/check-imports.sh +0 -0
- package/dist/templates/app/(auth)/forgot-password/page.tsx +0 -216
- package/dist/templates/app/(auth)/layout.tsx +0 -51
- package/dist/templates/app/(auth)/login/page.tsx +0 -21
- package/dist/templates/app/(auth)/reset-password/page.tsx +0 -212
- package/dist/templates/app/(auth)/signup/page.tsx +0 -21
- package/dist/templates/app/(auth)/verify-email/page.tsx +0 -190
- package/dist/templates/app/(public)/[...slug]/page.tsx +0 -378
- package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +0 -90
- package/dist/templates/app/(public)/docs/layout.tsx +0 -25
- package/dist/templates/app/(public)/docs/page.tsx +0 -81
- package/dist/templates/app/(public)/layout.tsx +0 -41
- package/dist/templates/app/(public)/page.tsx +0 -19
- package/dist/templates/app/403/page.tsx +0 -89
- package/dist/templates/app/api/auth/[...all]/route.ts +0 -78
- package/dist/templates/app/api/cron/billing/lifecycle/route.ts +0 -98
- package/dist/templates/app/api/csp-report/route.ts +0 -175
- package/dist/templates/app/api/devtools/config/entities/route.ts +0 -108
- package/dist/templates/app/api/devtools/config/theme/route.ts +0 -66
- package/dist/templates/app/api/devtools/tests/[...path]/route.ts +0 -130
- package/dist/templates/app/api/devtools/tests/route.ts +0 -134
- package/dist/templates/app/api/health/route.ts +0 -29
- package/dist/templates/app/api/internal/user-metadata/route.ts +0 -36
- package/dist/templates/app/api/superadmin/subscriptions/route.ts +0 -310
- package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +0 -286
- package/dist/templates/app/api/superadmin/teams/route.ts +0 -188
- package/dist/templates/app/api/superadmin/users/[userId]/route.ts +0 -540
- package/dist/templates/app/api/superadmin/users/route.ts +0 -323
- package/dist/templates/app/api/user/delete-account/route.ts +0 -55
- package/dist/templates/app/api/user/plan-flags/route.ts +0 -283
- package/dist/templates/app/api/user/profile/route.ts +0 -133
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +0 -210
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +0 -331
- package/dist/templates/app/api/v1/[entity]/[id]/route.ts +0 -35
- package/dist/templates/app/api/v1/[entity]/docs.md +0 -369
- package/dist/templates/app/api/v1/[entity]/presets.ts +0 -194
- package/dist/templates/app/api/v1/[entity]/route.ts +0 -31
- package/dist/templates/app/api/v1/api-keys/[id]/route.ts +0 -303
- package/dist/templates/app/api/v1/api-keys/docs.md +0 -101
- package/dist/templates/app/api/v1/api-keys/presets.ts +0 -31
- package/dist/templates/app/api/v1/api-keys/route.ts +0 -250
- package/dist/templates/app/api/v1/auth/docs.md +0 -184
- package/dist/templates/app/api/v1/auth/presets.ts +0 -44
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +0 -227
- package/dist/templates/app/api/v1/billing/cancel/route.ts +0 -206
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +0 -97
- package/dist/templates/app/api/v1/billing/check-action/route.ts +0 -81
- package/dist/templates/app/api/v1/billing/checkout/route.ts +0 -124
- package/dist/templates/app/api/v1/billing/docs.md +0 -209
- package/dist/templates/app/api/v1/billing/plans/route.ts +0 -85
- package/dist/templates/app/api/v1/billing/portal/route.ts +0 -90
- package/dist/templates/app/api/v1/billing/presets.ts +0 -121
- package/dist/templates/app/api/v1/billing/webhooks/stripe/route.ts +0 -428
- package/dist/templates/app/api/v1/blocks/[slug]/route.ts +0 -29
- package/dist/templates/app/api/v1/blocks/docs.md +0 -173
- package/dist/templates/app/api/v1/blocks/presets.ts +0 -121
- package/dist/templates/app/api/v1/blocks/route.ts +0 -45
- package/dist/templates/app/api/v1/blocks/validate/route.ts +0 -45
- package/dist/templates/app/api/v1/cron/docs.md +0 -116
- package/dist/templates/app/api/v1/cron/presets.ts +0 -26
- package/dist/templates/app/api/v1/cron/process/route.ts +0 -108
- package/dist/templates/app/api/v1/devtools/blocks/route.ts +0 -82
- package/dist/templates/app/api/v1/devtools/docs/route.ts +0 -150
- package/dist/templates/app/api/v1/devtools/docs.md +0 -204
- package/dist/templates/app/api/v1/devtools/features/route.ts +0 -61
- package/dist/templates/app/api/v1/devtools/flows/route.ts +0 -61
- package/dist/templates/app/api/v1/devtools/presets.ts +0 -113
- package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +0 -120
- package/dist/templates/app/api/v1/devtools/testing/route.ts +0 -82
- package/dist/templates/app/api/v1/media/docs.md +0 -117
- package/dist/templates/app/api/v1/media/presets.ts +0 -24
- package/dist/templates/app/api/v1/media/upload/route.ts +0 -150
- package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +0 -116
- package/dist/templates/app/api/v1/plugin/[...path]/route.ts +0 -373
- package/dist/templates/app/api/v1/plugin/docs.md +0 -79
- package/dist/templates/app/api/v1/plugin/presets.ts +0 -21
- package/dist/templates/app/api/v1/plugin/route.ts +0 -96
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +0 -255
- package/dist/templates/app/api/v1/post-categories/docs.md +0 -134
- package/dist/templates/app/api/v1/post-categories/presets.ts +0 -78
- package/dist/templates/app/api/v1/post-categories/route.ts +0 -119
- package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +0 -179
- package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +0 -120
- package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +0 -89
- package/dist/templates/app/api/v1/team-invitations/docs.md +0 -88
- package/dist/templates/app/api/v1/team-invitations/presets.ts +0 -43
- package/dist/templates/app/api/v1/team-invitations/route.ts +0 -114
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +0 -171
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +0 -105
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +0 -125
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +0 -263
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +0 -358
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +0 -322
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +0 -50
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +0 -91
- package/dist/templates/app/api/v1/teams/docs.md +0 -320
- package/dist/templates/app/api/v1/teams/presets.ts +0 -178
- package/dist/templates/app/api/v1/teams/route.ts +0 -293
- package/dist/templates/app/api/v1/teams/switch/route.ts +0 -88
- package/dist/templates/app/api/v1/theme/[...path]/route.ts +0 -361
- package/dist/templates/app/api/v1/theme/docs.md +0 -74
- package/dist/templates/app/api/v1/theme/presets.ts +0 -21
- package/dist/templates/app/api/v1/theme/route.ts +0 -96
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +0 -363
- package/dist/templates/app/api/v1/users/[id]/route.ts +0 -302
- package/dist/templates/app/api/v1/users/docs.md +0 -93
- package/dist/templates/app/api/v1/users/presets.ts +0 -59
- package/dist/templates/app/api/v1/users/route.ts +0 -197
- package/dist/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +0 -117
- package/dist/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +0 -103
- package/dist/templates/app/dashboard/(main)/[entity]/create/page.tsx +0 -95
- package/dist/templates/app/dashboard/(main)/[entity]/error.tsx +0 -51
- package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +0 -113
- package/dist/templates/app/dashboard/(main)/[entity]/loading.tsx +0 -61
- package/dist/templates/app/dashboard/(main)/[entity]/page.tsx +0 -90
- package/dist/templates/app/dashboard/(main)/layout.tsx +0 -98
- package/dist/templates/app/dashboard/(main)/loading.tsx +0 -5
- package/dist/templates/app/dashboard/(main)/page.tsx +0 -201
- package/dist/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +0 -114
- package/dist/templates/app/dashboard/(main)/patterns/[id]/page.tsx +0 -20
- package/dist/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +0 -171
- package/dist/templates/app/dashboard/(main)/patterns/create/page.tsx +0 -86
- package/dist/templates/app/dashboard/(main)/patterns/page.tsx +0 -444
- package/dist/templates/app/dashboard/features/analytics/page.tsx +0 -35
- package/dist/templates/app/dashboard/features/automation/page.tsx +0 -35
- package/dist/templates/app/dashboard/features/layout.tsx +0 -13
- package/dist/templates/app/dashboard/features/loading.tsx +0 -5
- package/dist/templates/app/dashboard/features/webhooks/page.tsx +0 -35
- package/dist/templates/app/dashboard/layout.tsx +0 -86
- package/dist/templates/app/dashboard/permission-denied/page.tsx +0 -29
- package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/api-keys/page.tsx +0 -513
- package/dist/templates/app/dashboard/settings/billing/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/billing/page.tsx +0 -284
- package/dist/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +0 -222
- package/dist/templates/app/dashboard/settings/invoices/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/invoices/page.tsx +0 -82
- package/dist/templates/app/dashboard/settings/layout.tsx +0 -151
- package/dist/templates/app/dashboard/settings/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/notifications/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/notifications/page.tsx +0 -462
- package/dist/templates/app/dashboard/settings/page.tsx +0 -92
- package/dist/templates/app/dashboard/settings/password/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/password/page.tsx +0 -306
- package/dist/templates/app/dashboard/settings/plans/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/plans/page.tsx +0 -40
- package/dist/templates/app/dashboard/settings/profile/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/profile/page.tsx +0 -686
- package/dist/templates/app/dashboard/settings/security/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/security/page.tsx +0 -505
- package/dist/templates/app/dashboard/settings/teams/loading.tsx +0 -5
- package/dist/templates/app/dashboard/settings/teams/page.tsx +0 -272
- package/dist/templates/app/dashboard/settings/teams/permissions/page.tsx +0 -92
- package/dist/templates/app/devtools/blocks/[slug]/page.tsx +0 -39
- package/dist/templates/app/devtools/blocks/page.tsx +0 -31
- package/dist/templates/app/devtools/config/page.tsx +0 -31
- package/dist/templates/app/devtools/features/page.tsx +0 -31
- package/dist/templates/app/devtools/flows/page.tsx +0 -31
- package/dist/templates/app/devtools/layout.tsx +0 -58
- package/dist/templates/app/devtools/page.tsx +0 -121
- package/dist/templates/app/devtools/scheduled-actions/page.tsx +0 -157
- package/dist/templates/app/devtools/style/page.tsx +0 -330
- package/dist/templates/app/devtools/tags/page.tsx +0 -31
- package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +0 -47
- package/dist/templates/app/favicon.ico +0 -0
- package/dist/templates/app/globals.css +0 -12
- package/dist/templates/app/layout.tsx +0 -96
- package/dist/templates/app/public/page.tsx +0 -30
- package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +0 -92
- package/dist/templates/app/superadmin/docs/page.tsx +0 -75
- package/dist/templates/app/superadmin/layout.tsx +0 -67
- package/dist/templates/app/superadmin/page.tsx +0 -149
- package/dist/templates/app/superadmin/subscriptions/page.tsx +0 -655
- package/dist/templates/app/superadmin/team-roles/page.tsx +0 -493
- package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +0 -687
- package/dist/templates/app/superadmin/teams/page.tsx +0 -302
- package/dist/templates/app/superadmin/users/[userId]/page.tsx +0 -548
- package/dist/templates/app/superadmin/users/page.tsx +0 -528
- package/templates/app/(auth)/forgot-password/page.tsx +0 -216
- package/templates/app/(auth)/layout.tsx +0 -51
- package/templates/app/(auth)/login/page.tsx +0 -21
- package/templates/app/(auth)/reset-password/page.tsx +0 -212
- package/templates/app/(auth)/signup/page.tsx +0 -21
- package/templates/app/(auth)/verify-email/page.tsx +0 -190
- package/templates/app/(public)/[...slug]/page.tsx +0 -378
- package/templates/app/(public)/docs/[section]/[page]/page.tsx +0 -90
- package/templates/app/(public)/docs/layout.tsx +0 -25
- package/templates/app/(public)/docs/page.tsx +0 -81
- package/templates/app/(public)/layout.tsx +0 -41
- package/templates/app/(public)/page.tsx +0 -19
- package/templates/app/403/page.tsx +0 -89
- package/templates/app/api/auth/[...all]/route.ts +0 -78
- package/templates/app/api/cron/billing/lifecycle/route.ts +0 -98
- package/templates/app/api/csp-report/route.ts +0 -175
- package/templates/app/api/devtools/config/entities/route.ts +0 -108
- package/templates/app/api/devtools/config/theme/route.ts +0 -66
- package/templates/app/api/devtools/tests/[...path]/route.ts +0 -130
- package/templates/app/api/devtools/tests/route.ts +0 -134
- package/templates/app/api/health/route.ts +0 -29
- package/templates/app/api/internal/user-metadata/route.ts +0 -36
- package/templates/app/api/superadmin/subscriptions/route.ts +0 -310
- package/templates/app/api/superadmin/teams/[teamId]/route.ts +0 -286
- package/templates/app/api/superadmin/teams/route.ts +0 -188
- package/templates/app/api/superadmin/users/[userId]/route.ts +0 -540
- package/templates/app/api/superadmin/users/route.ts +0 -323
- package/templates/app/api/user/delete-account/route.ts +0 -55
- package/templates/app/api/user/plan-flags/route.ts +0 -283
- package/templates/app/api/user/profile/route.ts +0 -133
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +0 -210
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +0 -331
- package/templates/app/api/v1/[entity]/[id]/route.ts +0 -35
- package/templates/app/api/v1/[entity]/docs.md +0 -369
- package/templates/app/api/v1/[entity]/presets.ts +0 -194
- package/templates/app/api/v1/[entity]/route.ts +0 -31
- package/templates/app/api/v1/api-keys/[id]/route.ts +0 -303
- package/templates/app/api/v1/api-keys/docs.md +0 -101
- package/templates/app/api/v1/api-keys/presets.ts +0 -31
- package/templates/app/api/v1/api-keys/route.ts +0 -250
- package/templates/app/api/v1/auth/docs.md +0 -184
- package/templates/app/api/v1/auth/presets.ts +0 -44
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +0 -227
- package/templates/app/api/v1/billing/cancel/route.ts +0 -206
- package/templates/app/api/v1/billing/change-plan/route.ts +0 -97
- package/templates/app/api/v1/billing/check-action/route.ts +0 -81
- package/templates/app/api/v1/billing/checkout/route.ts +0 -124
- package/templates/app/api/v1/billing/docs.md +0 -209
- package/templates/app/api/v1/billing/plans/route.ts +0 -85
- package/templates/app/api/v1/billing/portal/route.ts +0 -90
- package/templates/app/api/v1/billing/presets.ts +0 -121
- package/templates/app/api/v1/billing/webhooks/stripe/route.ts +0 -428
- package/templates/app/api/v1/blocks/[slug]/route.ts +0 -29
- package/templates/app/api/v1/blocks/docs.md +0 -173
- package/templates/app/api/v1/blocks/presets.ts +0 -121
- package/templates/app/api/v1/blocks/route.ts +0 -45
- package/templates/app/api/v1/blocks/validate/route.ts +0 -45
- package/templates/app/api/v1/cron/docs.md +0 -116
- package/templates/app/api/v1/cron/presets.ts +0 -26
- package/templates/app/api/v1/cron/process/route.ts +0 -108
- package/templates/app/api/v1/devtools/blocks/route.ts +0 -82
- package/templates/app/api/v1/devtools/docs/route.ts +0 -150
- package/templates/app/api/v1/devtools/docs.md +0 -204
- package/templates/app/api/v1/devtools/features/route.ts +0 -61
- package/templates/app/api/v1/devtools/flows/route.ts +0 -61
- package/templates/app/api/v1/devtools/presets.ts +0 -113
- package/templates/app/api/v1/devtools/scheduled-actions/route.ts +0 -120
- package/templates/app/api/v1/devtools/testing/route.ts +0 -82
- package/templates/app/api/v1/media/docs.md +0 -117
- package/templates/app/api/v1/media/presets.ts +0 -24
- package/templates/app/api/v1/media/upload/route.ts +0 -150
- package/templates/app/api/v1/patterns/[id]/usages/route.ts +0 -116
- package/templates/app/api/v1/plugin/[...path]/route.ts +0 -373
- package/templates/app/api/v1/plugin/docs.md +0 -79
- package/templates/app/api/v1/plugin/presets.ts +0 -21
- package/templates/app/api/v1/plugin/route.ts +0 -96
- package/templates/app/api/v1/post-categories/[id]/route.ts +0 -255
- package/templates/app/api/v1/post-categories/docs.md +0 -134
- package/templates/app/api/v1/post-categories/presets.ts +0 -78
- package/templates/app/api/v1/post-categories/route.ts +0 -119
- package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +0 -179
- package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +0 -120
- package/templates/app/api/v1/team-invitations/[token]/route.ts +0 -89
- package/templates/app/api/v1/team-invitations/docs.md +0 -88
- package/templates/app/api/v1/team-invitations/presets.ts +0 -43
- package/templates/app/api/v1/team-invitations/route.ts +0 -114
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +0 -171
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +0 -105
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +0 -125
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +0 -263
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +0 -358
- package/templates/app/api/v1/teams/[teamId]/route.ts +0 -322
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +0 -50
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +0 -91
- package/templates/app/api/v1/teams/docs.md +0 -320
- package/templates/app/api/v1/teams/presets.ts +0 -178
- package/templates/app/api/v1/teams/route.ts +0 -293
- package/templates/app/api/v1/teams/switch/route.ts +0 -88
- package/templates/app/api/v1/theme/[...path]/route.ts +0 -361
- package/templates/app/api/v1/theme/docs.md +0 -74
- package/templates/app/api/v1/theme/presets.ts +0 -21
- package/templates/app/api/v1/theme/route.ts +0 -96
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +0 -363
- package/templates/app/api/v1/users/[id]/route.ts +0 -302
- package/templates/app/api/v1/users/docs.md +0 -93
- package/templates/app/api/v1/users/presets.ts +0 -59
- package/templates/app/api/v1/users/route.ts +0 -197
- package/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +0 -117
- package/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +0 -103
- package/templates/app/dashboard/(main)/[entity]/create/page.tsx +0 -95
- package/templates/app/dashboard/(main)/[entity]/error.tsx +0 -51
- package/templates/app/dashboard/(main)/[entity]/layout.tsx +0 -113
- package/templates/app/dashboard/(main)/[entity]/loading.tsx +0 -61
- package/templates/app/dashboard/(main)/[entity]/page.tsx +0 -90
- package/templates/app/dashboard/(main)/layout.tsx +0 -98
- package/templates/app/dashboard/(main)/loading.tsx +0 -5
- package/templates/app/dashboard/(main)/page.tsx +0 -201
- package/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +0 -114
- package/templates/app/dashboard/(main)/patterns/[id]/page.tsx +0 -20
- package/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +0 -171
- package/templates/app/dashboard/(main)/patterns/create/page.tsx +0 -86
- package/templates/app/dashboard/(main)/patterns/page.tsx +0 -444
- package/templates/app/dashboard/features/analytics/page.tsx +0 -35
- package/templates/app/dashboard/features/automation/page.tsx +0 -35
- package/templates/app/dashboard/features/layout.tsx +0 -13
- package/templates/app/dashboard/features/loading.tsx +0 -5
- package/templates/app/dashboard/features/webhooks/page.tsx +0 -35
- package/templates/app/dashboard/layout.tsx +0 -86
- package/templates/app/dashboard/permission-denied/page.tsx +0 -29
- package/templates/app/dashboard/settings/api-keys/loading.tsx +0 -5
- package/templates/app/dashboard/settings/api-keys/page.tsx +0 -513
- package/templates/app/dashboard/settings/billing/loading.tsx +0 -5
- package/templates/app/dashboard/settings/billing/page.tsx +0 -284
- package/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +0 -222
- package/templates/app/dashboard/settings/invoices/loading.tsx +0 -5
- package/templates/app/dashboard/settings/invoices/page.tsx +0 -82
- package/templates/app/dashboard/settings/layout.tsx +0 -151
- package/templates/app/dashboard/settings/loading.tsx +0 -5
- package/templates/app/dashboard/settings/notifications/loading.tsx +0 -5
- package/templates/app/dashboard/settings/notifications/page.tsx +0 -462
- package/templates/app/dashboard/settings/page.tsx +0 -92
- package/templates/app/dashboard/settings/password/loading.tsx +0 -5
- package/templates/app/dashboard/settings/password/page.tsx +0 -306
- package/templates/app/dashboard/settings/plans/loading.tsx +0 -5
- package/templates/app/dashboard/settings/plans/page.tsx +0 -40
- package/templates/app/dashboard/settings/profile/loading.tsx +0 -5
- package/templates/app/dashboard/settings/profile/page.tsx +0 -686
- package/templates/app/dashboard/settings/security/loading.tsx +0 -5
- package/templates/app/dashboard/settings/security/page.tsx +0 -505
- package/templates/app/dashboard/settings/teams/loading.tsx +0 -5
- package/templates/app/dashboard/settings/teams/page.tsx +0 -272
- package/templates/app/dashboard/settings/teams/permissions/page.tsx +0 -92
- package/templates/app/devtools/blocks/[slug]/page.tsx +0 -39
- package/templates/app/devtools/blocks/page.tsx +0 -31
- package/templates/app/devtools/config/page.tsx +0 -31
- package/templates/app/devtools/features/page.tsx +0 -31
- package/templates/app/devtools/flows/page.tsx +0 -31
- package/templates/app/devtools/layout.tsx +0 -58
- package/templates/app/devtools/page.tsx +0 -121
- package/templates/app/devtools/scheduled-actions/page.tsx +0 -157
- package/templates/app/devtools/style/page.tsx +0 -330
- package/templates/app/devtools/tags/page.tsx +0 -31
- package/templates/app/devtools/tests/[[...path]]/page.tsx +0 -47
- package/templates/app/favicon.ico +0 -0
- package/templates/app/globals.css +0 -12
- package/templates/app/layout.tsx +0 -96
- package/templates/app/public/page.tsx +0 -30
- package/templates/app/superadmin/docs/[section]/[page]/page.tsx +0 -92
- package/templates/app/superadmin/docs/page.tsx +0 -75
- package/templates/app/superadmin/layout.tsx +0 -67
- package/templates/app/superadmin/page.tsx +0 -149
- package/templates/app/superadmin/subscriptions/page.tsx +0 -655
- package/templates/app/superadmin/team-roles/page.tsx +0 -493
- package/templates/app/superadmin/teams/[teamId]/page.tsx +0 -687
- package/templates/app/superadmin/teams/page.tsx +0 -302
- package/templates/app/superadmin/users/[userId]/page.tsx +0 -548
- package/templates/app/superadmin/users/page.tsx +0 -528
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API Presets for Billing
|
|
3
|
-
*
|
|
4
|
-
* These presets appear in the DevTools API Explorer's "Presets" tab.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { defineApiEndpoint } from '@nextsparkjs/core/types/api-presets'
|
|
8
|
-
|
|
9
|
-
export default defineApiEndpoint({
|
|
10
|
-
endpoint: '/api/v1/billing',
|
|
11
|
-
summary: 'Manage subscriptions, plans, and billing operations',
|
|
12
|
-
presets: [
|
|
13
|
-
// Plans
|
|
14
|
-
{
|
|
15
|
-
id: 'list-plans',
|
|
16
|
-
title: 'List Plans',
|
|
17
|
-
description: 'Fetch all available subscription plans',
|
|
18
|
-
method: 'GET',
|
|
19
|
-
tags: ['read', 'plans']
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
// Checkout
|
|
23
|
-
{
|
|
24
|
-
id: 'checkout-pro-monthly',
|
|
25
|
-
title: 'Checkout Pro (Monthly)',
|
|
26
|
-
description: 'Create checkout session for Pro plan monthly',
|
|
27
|
-
method: 'POST',
|
|
28
|
-
payload: {
|
|
29
|
-
planSlug: 'pro',
|
|
30
|
-
billingPeriod: 'monthly'
|
|
31
|
-
},
|
|
32
|
-
tags: ['write', 'checkout']
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: 'checkout-pro-yearly',
|
|
36
|
-
title: 'Checkout Pro (Yearly)',
|
|
37
|
-
description: 'Create checkout session for Pro plan yearly (save 20%)',
|
|
38
|
-
method: 'POST',
|
|
39
|
-
payload: {
|
|
40
|
-
planSlug: 'pro',
|
|
41
|
-
billingPeriod: 'yearly'
|
|
42
|
-
},
|
|
43
|
-
tags: ['write', 'checkout']
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: 'checkout-enterprise',
|
|
47
|
-
title: 'Checkout Enterprise',
|
|
48
|
-
description: 'Create checkout session for Enterprise plan',
|
|
49
|
-
method: 'POST',
|
|
50
|
-
payload: {
|
|
51
|
-
planSlug: 'enterprise',
|
|
52
|
-
billingPeriod: 'yearly'
|
|
53
|
-
},
|
|
54
|
-
tags: ['write', 'checkout']
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
// Portal
|
|
58
|
-
{
|
|
59
|
-
id: 'open-portal',
|
|
60
|
-
title: 'Open Customer Portal',
|
|
61
|
-
description: 'Get URL for Stripe customer portal',
|
|
62
|
-
method: 'GET',
|
|
63
|
-
tags: ['read', 'portal']
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
// Change Plan
|
|
67
|
-
{
|
|
68
|
-
id: 'upgrade-to-pro',
|
|
69
|
-
title: 'Upgrade to Pro',
|
|
70
|
-
description: 'Change current subscription to Pro plan',
|
|
71
|
-
method: 'POST',
|
|
72
|
-
payload: {
|
|
73
|
-
planSlug: 'pro',
|
|
74
|
-
billingPeriod: 'monthly'
|
|
75
|
-
},
|
|
76
|
-
tags: ['write', 'change-plan']
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
// Cancel
|
|
80
|
-
{
|
|
81
|
-
id: 'cancel-subscription',
|
|
82
|
-
title: 'Cancel Subscription',
|
|
83
|
-
description: 'Cancel current subscription at end of billing period',
|
|
84
|
-
method: 'POST',
|
|
85
|
-
payload: {},
|
|
86
|
-
tags: ['write', 'cancel']
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
// Check Action
|
|
90
|
-
{
|
|
91
|
-
id: 'check-invite-member',
|
|
92
|
-
title: 'Check: Can Invite Member',
|
|
93
|
-
description: 'Check if team can invite more members (quota check)',
|
|
94
|
-
method: 'POST',
|
|
95
|
-
payload: {
|
|
96
|
-
action: 'team.invite_member'
|
|
97
|
-
},
|
|
98
|
-
tags: ['read', 'check-action']
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
id: 'check-create-project',
|
|
102
|
-
title: 'Check: Can Create Project',
|
|
103
|
-
description: 'Check if team can create more projects',
|
|
104
|
-
method: 'POST',
|
|
105
|
-
payload: {
|
|
106
|
-
action: 'projects.create'
|
|
107
|
-
},
|
|
108
|
-
tags: ['read', 'check-action']
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
id: 'check-api-access',
|
|
112
|
-
title: 'Check: API Access',
|
|
113
|
-
description: 'Check if plan includes API access feature',
|
|
114
|
-
method: 'POST',
|
|
115
|
-
payload: {
|
|
116
|
-
action: 'api.access'
|
|
117
|
-
},
|
|
118
|
-
tags: ['read', 'check-action']
|
|
119
|
-
}
|
|
120
|
-
]
|
|
121
|
-
})
|
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stripe Webhook Handler
|
|
3
|
-
*
|
|
4
|
-
* Processes Stripe webhook events for subscription lifecycle management.
|
|
5
|
-
* CRITICAL: Verifies webhook signatures for security.
|
|
6
|
-
*
|
|
7
|
-
* P2: Stripe Integration
|
|
8
|
-
*
|
|
9
|
-
* NOTE: This handler uses direct query() calls (bypassing RLS) because:
|
|
10
|
-
* 1. Webhooks have no user context (no session, no auth)
|
|
11
|
-
* 2. RLS policies require user membership which webhooks can't satisfy
|
|
12
|
-
* 3. Webhook signature verification provides security at the API level
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { NextRequest } from 'next/server'
|
|
16
|
-
import { verifyWebhookSignature } from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
17
|
-
import { query, queryOne } from '@nextsparkjs/core/lib/db'
|
|
18
|
-
import type Stripe from 'stripe'
|
|
19
|
-
import type { InvoiceStatus } from '@nextsparkjs/core/lib/billing/types'
|
|
20
|
-
|
|
21
|
-
export async function POST(request: NextRequest) {
|
|
22
|
-
// 1. Get raw body and signature
|
|
23
|
-
const payload = await request.text()
|
|
24
|
-
const signature = request.headers.get('stripe-signature')
|
|
25
|
-
|
|
26
|
-
if (!signature) {
|
|
27
|
-
return Response.json({ error: 'No signature provided' }, { status: 400 })
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 2. Verify webhook signature (MANDATORY for security)
|
|
31
|
-
let event: Stripe.Event
|
|
32
|
-
try {
|
|
33
|
-
event = verifyWebhookSignature(payload, signature)
|
|
34
|
-
} catch (error) {
|
|
35
|
-
console.error('[stripe-webhook] Signature verification failed:', error)
|
|
36
|
-
return Response.json({ error: 'Invalid signature' }, { status: 400 })
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// 3. Check for duplicate events (idempotency)
|
|
40
|
-
const eventId = event.id
|
|
41
|
-
|
|
42
|
-
const existing = await queryOne(
|
|
43
|
-
`SELECT id FROM "billing_events" WHERE metadata->>'stripeEventId' = $1`,
|
|
44
|
-
[eventId]
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
if (existing) {
|
|
48
|
-
console.log(`[stripe-webhook] Event ${eventId} already processed, skipping`)
|
|
49
|
-
return Response.json({ received: true, status: 'duplicate' })
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// 4. Handle events
|
|
53
|
-
try {
|
|
54
|
-
console.log(`[stripe-webhook] Processing event type: ${event.type}`)
|
|
55
|
-
|
|
56
|
-
switch (event.type) {
|
|
57
|
-
case 'checkout.session.completed':
|
|
58
|
-
await handleCheckoutCompleted(event.data.object as Stripe.Checkout.Session)
|
|
59
|
-
break
|
|
60
|
-
|
|
61
|
-
case 'invoice.paid':
|
|
62
|
-
await handleInvoicePaid(event.data.object as Stripe.Invoice)
|
|
63
|
-
break
|
|
64
|
-
|
|
65
|
-
case 'invoice.payment_failed':
|
|
66
|
-
await handlePaymentFailed(event.data.object as Stripe.Invoice)
|
|
67
|
-
break
|
|
68
|
-
|
|
69
|
-
case 'customer.subscription.updated':
|
|
70
|
-
await handleSubscriptionUpdated(event.data.object as Stripe.Subscription)
|
|
71
|
-
break
|
|
72
|
-
|
|
73
|
-
case 'customer.subscription.deleted':
|
|
74
|
-
await handleSubscriptionDeleted(event.data.object as Stripe.Subscription)
|
|
75
|
-
break
|
|
76
|
-
|
|
77
|
-
default:
|
|
78
|
-
console.log(`[stripe-webhook] Unhandled event type: ${event.type}`)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return Response.json({ received: true })
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error('[stripe-webhook] Handler error:', error)
|
|
84
|
-
return Response.json({ error: 'Handler failed' }, { status: 500 })
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Handle checkout.session.completed
|
|
90
|
-
* User successfully completed checkout, create or update subscription
|
|
91
|
-
*/
|
|
92
|
-
async function handleCheckoutCompleted(session: Stripe.Checkout.Session) {
|
|
93
|
-
const teamId = session.metadata?.teamId || session.client_reference_id
|
|
94
|
-
if (!teamId) {
|
|
95
|
-
throw new Error('No team ID in checkout session')
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const subscriptionId = session.subscription as string
|
|
99
|
-
const customerId = session.customer as string
|
|
100
|
-
const planSlug = session.metadata?.planSlug
|
|
101
|
-
const billingPeriod = session.metadata?.billingPeriod || 'monthly'
|
|
102
|
-
|
|
103
|
-
console.log(`[stripe-webhook] Checkout completed for team ${teamId}, plan: ${planSlug}`)
|
|
104
|
-
|
|
105
|
-
// Get plan ID from slug (CRITICAL: must update plan after checkout!)
|
|
106
|
-
let planId: string | null = null
|
|
107
|
-
if (planSlug) {
|
|
108
|
-
const planResult = await queryOne<{ id: string }>(
|
|
109
|
-
`SELECT id FROM plans WHERE slug = $1 LIMIT 1`,
|
|
110
|
-
[planSlug]
|
|
111
|
-
)
|
|
112
|
-
planId = planResult?.id || null
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (!planId) {
|
|
116
|
-
console.warn(`[stripe-webhook] Plan ${planSlug} not found in database, keeping current plan`)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Update subscription with Stripe IDs AND new plan (direct query - webhook has no user context)
|
|
120
|
-
if (planId) {
|
|
121
|
-
await query(
|
|
122
|
-
`UPDATE subscriptions
|
|
123
|
-
SET "externalSubscriptionId" = $1,
|
|
124
|
-
"externalCustomerId" = $2,
|
|
125
|
-
"paymentProvider" = 'stripe',
|
|
126
|
-
"planId" = $3,
|
|
127
|
-
"billingInterval" = $4,
|
|
128
|
-
status = 'active',
|
|
129
|
-
"updatedAt" = NOW()
|
|
130
|
-
WHERE "teamId" = $5
|
|
131
|
-
AND status IN ('active', 'trialing', 'past_due')`,
|
|
132
|
-
[subscriptionId, customerId, planId, billingPeriod, teamId]
|
|
133
|
-
)
|
|
134
|
-
} else {
|
|
135
|
-
// Fallback: update without changing plan (should not happen normally)
|
|
136
|
-
await query(
|
|
137
|
-
`UPDATE subscriptions
|
|
138
|
-
SET "externalSubscriptionId" = $1,
|
|
139
|
-
"externalCustomerId" = $2,
|
|
140
|
-
"paymentProvider" = 'stripe',
|
|
141
|
-
status = 'active',
|
|
142
|
-
"updatedAt" = NOW()
|
|
143
|
-
WHERE "teamId" = $3
|
|
144
|
-
AND status IN ('active', 'trialing', 'past_due')`,
|
|
145
|
-
[subscriptionId, customerId, teamId]
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Log billing event
|
|
150
|
-
await logBillingEvent({
|
|
151
|
-
teamId,
|
|
152
|
-
type: 'payment',
|
|
153
|
-
status: 'succeeded',
|
|
154
|
-
amount: session.amount_total || 0,
|
|
155
|
-
currency: session.currency || 'usd',
|
|
156
|
-
stripeEventId: session.id
|
|
157
|
-
})
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Handle invoice.paid
|
|
162
|
-
* Subscription payment succeeded, update period dates and sync invoice
|
|
163
|
-
*/
|
|
164
|
-
async function handleInvoicePaid(invoice: Stripe.Invoice) {
|
|
165
|
-
// Stripe webhook expands subscription field which is not in base type
|
|
166
|
-
const expandedInvoice = invoice as Stripe.Invoice & {
|
|
167
|
-
subscription?: string | Stripe.Subscription | null
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const subscriptionId = typeof expandedInvoice.subscription === 'string'
|
|
171
|
-
? expandedInvoice.subscription
|
|
172
|
-
: expandedInvoice.subscription?.id
|
|
173
|
-
|
|
174
|
-
if (!subscriptionId) {
|
|
175
|
-
console.log('[stripe-webhook] Invoice has no subscription ID, skipping')
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
console.log(`[stripe-webhook] Invoice paid for subscription ${subscriptionId}`)
|
|
180
|
-
|
|
181
|
-
// Only update period if invoice has period info (direct query - webhook has no user context)
|
|
182
|
-
if (invoice.lines?.data?.[0]) {
|
|
183
|
-
const line = invoice.lines.data[0]
|
|
184
|
-
await query(
|
|
185
|
-
`UPDATE subscriptions
|
|
186
|
-
SET status = 'active',
|
|
187
|
-
"currentPeriodStart" = to_timestamp($1),
|
|
188
|
-
"currentPeriodEnd" = to_timestamp($2),
|
|
189
|
-
"updatedAt" = NOW()
|
|
190
|
-
WHERE "externalSubscriptionId" = $3`,
|
|
191
|
-
[line.period.start, line.period.end, subscriptionId]
|
|
192
|
-
)
|
|
193
|
-
} else {
|
|
194
|
-
// Just mark as active without updating periods
|
|
195
|
-
await query(
|
|
196
|
-
`UPDATE subscriptions
|
|
197
|
-
SET status = 'active',
|
|
198
|
-
"updatedAt" = NOW()
|
|
199
|
-
WHERE "externalSubscriptionId" = $1`,
|
|
200
|
-
[subscriptionId]
|
|
201
|
-
)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Sync invoice to local database
|
|
205
|
-
await syncInvoiceToDatabase(invoice, 'paid')
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Handle invoice.payment_failed
|
|
210
|
-
* Payment failed, mark subscription as past_due and sync invoice
|
|
211
|
-
*/
|
|
212
|
-
async function handlePaymentFailed(invoice: Stripe.Invoice) {
|
|
213
|
-
// Stripe webhook expands subscription field which is not in base type
|
|
214
|
-
const expandedInvoice = invoice as Stripe.Invoice & {
|
|
215
|
-
subscription?: string | Stripe.Subscription | null
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const subscriptionId = typeof expandedInvoice.subscription === 'string'
|
|
219
|
-
? expandedInvoice.subscription
|
|
220
|
-
: expandedInvoice.subscription?.id
|
|
221
|
-
|
|
222
|
-
if (!subscriptionId) {
|
|
223
|
-
console.log('[stripe-webhook] Invoice has no subscription ID, skipping')
|
|
224
|
-
return
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
console.log(`[stripe-webhook] Payment failed for subscription ${subscriptionId}`)
|
|
228
|
-
|
|
229
|
-
await query(
|
|
230
|
-
`UPDATE subscriptions
|
|
231
|
-
SET status = 'past_due',
|
|
232
|
-
"updatedAt" = NOW()
|
|
233
|
-
WHERE "externalSubscriptionId" = $1`,
|
|
234
|
-
[subscriptionId]
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
// Sync invoice to local database with failed status
|
|
238
|
-
await syncInvoiceToDatabase(invoice, 'failed')
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Handle customer.subscription.updated
|
|
243
|
-
* Subscription status or settings changed (including plan changes)
|
|
244
|
-
*/
|
|
245
|
-
async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
|
|
246
|
-
// Map Stripe status to our status
|
|
247
|
-
const statusMap: Record<string, string> = {
|
|
248
|
-
trialing: 'trialing',
|
|
249
|
-
active: 'active',
|
|
250
|
-
past_due: 'past_due',
|
|
251
|
-
canceled: 'canceled',
|
|
252
|
-
unpaid: 'past_due',
|
|
253
|
-
incomplete: 'past_due',
|
|
254
|
-
incomplete_expired: 'expired',
|
|
255
|
-
paused: 'paused'
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const ourStatus = statusMap[subscription.status] || 'active'
|
|
259
|
-
|
|
260
|
-
console.log(
|
|
261
|
-
`[stripe-webhook] Subscription updated ${subscription.id}, status: ${subscription.status} -> ${ourStatus}`
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
// Stripe webhook includes current_period_end which is not in base type
|
|
265
|
-
const expandedSubscription = subscription as Stripe.Subscription & {
|
|
266
|
-
current_period_end?: number
|
|
267
|
-
current_period_start?: number
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Check if plan changed by looking up price ID
|
|
271
|
-
const priceId = subscription.items.data[0]?.price.id
|
|
272
|
-
let planUpdateClause = ''
|
|
273
|
-
const params: (string | number | boolean | null)[] = [
|
|
274
|
-
ourStatus,
|
|
275
|
-
subscription.cancel_at_period_end,
|
|
276
|
-
expandedSubscription.current_period_end ?? null,
|
|
277
|
-
]
|
|
278
|
-
|
|
279
|
-
if (priceId) {
|
|
280
|
-
// Find plan by Stripe price ID (monthly or yearly)
|
|
281
|
-
const planResult = await queryOne<{ id: string }>(
|
|
282
|
-
`SELECT id FROM plans
|
|
283
|
-
WHERE "stripePriceIdMonthly" = $1 OR "stripePriceIdYearly" = $1
|
|
284
|
-
LIMIT 1`,
|
|
285
|
-
[priceId]
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
if (planResult) {
|
|
289
|
-
planUpdateClause = ', "planId" = $5'
|
|
290
|
-
params.push(subscription.id, planResult.id)
|
|
291
|
-
} else {
|
|
292
|
-
params.push(subscription.id)
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
params.push(subscription.id)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
await query(
|
|
299
|
-
`UPDATE subscriptions
|
|
300
|
-
SET status = $1,
|
|
301
|
-
"cancelAtPeriodEnd" = $2,
|
|
302
|
-
"currentPeriodEnd" = to_timestamp($3),
|
|
303
|
-
"updatedAt" = NOW()${planUpdateClause}
|
|
304
|
-
WHERE "externalSubscriptionId" = $4`,
|
|
305
|
-
params
|
|
306
|
-
)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Handle customer.subscription.deleted
|
|
311
|
-
* Subscription was canceled
|
|
312
|
-
*/
|
|
313
|
-
async function handleSubscriptionDeleted(subscription: Stripe.Subscription) {
|
|
314
|
-
console.log(`[stripe-webhook] Subscription deleted ${subscription.id}`)
|
|
315
|
-
|
|
316
|
-
await query(
|
|
317
|
-
`UPDATE subscriptions
|
|
318
|
-
SET status = 'canceled',
|
|
319
|
-
"canceledAt" = NOW(),
|
|
320
|
-
"updatedAt" = NOW()
|
|
321
|
-
WHERE "externalSubscriptionId" = $1`,
|
|
322
|
-
[subscription.id]
|
|
323
|
-
)
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Log billing event for audit trail
|
|
328
|
-
* Uses direct query (bypasses RLS) since webhooks have no user context
|
|
329
|
-
*/
|
|
330
|
-
async function logBillingEvent(params: {
|
|
331
|
-
teamId: string
|
|
332
|
-
type: string
|
|
333
|
-
status: string
|
|
334
|
-
amount: number
|
|
335
|
-
currency: string
|
|
336
|
-
stripeEventId: string
|
|
337
|
-
}) {
|
|
338
|
-
// Get subscription ID (direct query - webhook has no user context)
|
|
339
|
-
const sub = await queryOne<{ id: string }>(
|
|
340
|
-
`SELECT id FROM subscriptions WHERE "teamId" = $1 LIMIT 1`,
|
|
341
|
-
[params.teamId]
|
|
342
|
-
)
|
|
343
|
-
|
|
344
|
-
if (!sub) {
|
|
345
|
-
console.warn(`[stripe-webhook] No subscription found for team ${params.teamId}, cannot log billing event`)
|
|
346
|
-
return
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
await query(
|
|
350
|
-
`INSERT INTO "billing_events" ("subscriptionId", type, status, amount, currency, metadata)
|
|
351
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
352
|
-
[
|
|
353
|
-
sub.id,
|
|
354
|
-
params.type,
|
|
355
|
-
params.status,
|
|
356
|
-
params.amount,
|
|
357
|
-
params.currency,
|
|
358
|
-
JSON.stringify({ stripeEventId: params.stripeEventId })
|
|
359
|
-
]
|
|
360
|
-
)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Sync Stripe invoice to local database
|
|
365
|
-
* Uses direct query (bypasses RLS) since webhooks have no user context
|
|
366
|
-
*/
|
|
367
|
-
async function syncInvoiceToDatabase(
|
|
368
|
-
invoice: Stripe.Invoice,
|
|
369
|
-
status: InvoiceStatus
|
|
370
|
-
) {
|
|
371
|
-
// Stripe webhook expands subscription field which is not in base type
|
|
372
|
-
const expandedInvoice = invoice as Stripe.Invoice & {
|
|
373
|
-
subscription?: string | Stripe.Subscription | null
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Get subscription ID from invoice
|
|
377
|
-
const subscriptionId = typeof expandedInvoice.subscription === 'string'
|
|
378
|
-
? expandedInvoice.subscription
|
|
379
|
-
: expandedInvoice.subscription?.id
|
|
380
|
-
|
|
381
|
-
if (!subscriptionId) {
|
|
382
|
-
console.warn('[stripe-webhook] Invoice has no subscription, cannot sync to invoices table')
|
|
383
|
-
return
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Find team from subscription (using direct query - no RLS needed for system operation)
|
|
387
|
-
const subResult = await query<{ teamId: string }>(
|
|
388
|
-
`SELECT "teamId" FROM subscriptions WHERE "externalSubscriptionId" = $1`,
|
|
389
|
-
[subscriptionId]
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
if (!subResult.rows[0]) {
|
|
393
|
-
console.warn(`[stripe-webhook] No subscription found for ${subscriptionId}, cannot sync invoice`)
|
|
394
|
-
return
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const teamId = subResult.rows[0].teamId
|
|
398
|
-
const invoiceNumber = invoice.number || invoice.id
|
|
399
|
-
|
|
400
|
-
// Upsert invoice (ON CONFLICT for idempotency)
|
|
401
|
-
// Uses direct query to bypass RLS (webhook has no user context)
|
|
402
|
-
// NOTE: invoice.total is in cents from Stripe, invoices.amount is DECIMAL(10,2) in dollars
|
|
403
|
-
const amountInDollars = invoice.total / 100
|
|
404
|
-
|
|
405
|
-
await query(
|
|
406
|
-
`INSERT INTO invoices (
|
|
407
|
-
id, "teamId", "invoiceNumber", date, amount, currency, status, "pdfUrl", description
|
|
408
|
-
) VALUES (
|
|
409
|
-
gen_random_uuid()::text, $1, $2, to_timestamp($3), $4, $5, $6::invoice_status, $7, $8
|
|
410
|
-
)
|
|
411
|
-
ON CONFLICT ("teamId", "invoiceNumber") DO UPDATE SET
|
|
412
|
-
status = EXCLUDED.status,
|
|
413
|
-
"pdfUrl" = EXCLUDED."pdfUrl",
|
|
414
|
-
"updatedAt" = NOW()`,
|
|
415
|
-
[
|
|
416
|
-
teamId,
|
|
417
|
-
invoiceNumber,
|
|
418
|
-
invoice.created,
|
|
419
|
-
amountInDollars, // Convert from cents to dollars for DECIMAL(10,2) column
|
|
420
|
-
invoice.currency.toUpperCase(),
|
|
421
|
-
status,
|
|
422
|
-
invoice.invoice_pdf || invoice.hosted_invoice_url || null,
|
|
423
|
-
invoice.description || `Invoice ${invoiceNumber}`
|
|
424
|
-
]
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
console.log(`[stripe-webhook] Invoice ${invoiceNumber} synced for team ${teamId} with status ${status}`)
|
|
428
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server'
|
|
2
|
-
import { BLOCK_REGISTRY } from '@nextsparkjs/registries/block-registry'
|
|
3
|
-
|
|
4
|
-
export async function GET(
|
|
5
|
-
request: NextRequest,
|
|
6
|
-
{ params }: { params: Promise<{ slug: string }> }
|
|
7
|
-
): Promise<NextResponse> {
|
|
8
|
-
try {
|
|
9
|
-
const { slug } = await params
|
|
10
|
-
const block = BLOCK_REGISTRY[slug]
|
|
11
|
-
|
|
12
|
-
if (!block) {
|
|
13
|
-
return NextResponse.json({ error: 'Block not found' }, { status: 404 })
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return NextResponse.json({
|
|
17
|
-
slug: block.slug,
|
|
18
|
-
name: block.name,
|
|
19
|
-
description: block.description,
|
|
20
|
-
category: block.category,
|
|
21
|
-
icon: block.icon,
|
|
22
|
-
thumbnail: block.thumbnail,
|
|
23
|
-
fieldDefinitions: block.fieldDefinitions
|
|
24
|
-
})
|
|
25
|
-
} catch (err) {
|
|
26
|
-
console.error('Error fetching block:', err)
|
|
27
|
-
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
28
|
-
}
|
|
29
|
-
}
|