@nextsparkjs/core 0.1.0-beta.83 → 0.1.0-beta.85
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/styles/classes.json +1 -1
- package/dist/templates/app/(auth)/forgot-password/page.tsx +216 -0
- package/dist/templates/app/(auth)/layout.tsx +51 -0
- package/dist/templates/app/(auth)/login/page.tsx +21 -0
- package/dist/templates/app/(auth)/reset-password/page.tsx +212 -0
- package/dist/templates/app/(auth)/signup/page.tsx +21 -0
- package/dist/templates/app/(auth)/verify-email/page.tsx +190 -0
- package/dist/templates/app/(public)/[...slug]/page.tsx +378 -0
- package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +90 -0
- package/dist/templates/app/(public)/docs/layout.tsx +25 -0
- package/dist/templates/app/(public)/docs/page.tsx +81 -0
- package/dist/templates/app/(public)/layout.tsx +41 -0
- package/dist/templates/app/(public)/page.tsx +19 -0
- package/dist/templates/app/403/page.tsx +89 -0
- package/dist/templates/app/api/auth/[...all]/route.ts +78 -0
- package/dist/templates/app/api/cron/billing/lifecycle/route.ts +98 -0
- package/dist/templates/app/api/csp-report/route.ts +175 -0
- package/dist/templates/app/api/devtools/config/entities/route.ts +108 -0
- package/dist/templates/app/api/devtools/config/theme/route.ts +66 -0
- package/dist/templates/app/api/devtools/tests/[...path]/route.ts +130 -0
- package/dist/templates/app/api/devtools/tests/route.ts +134 -0
- package/dist/templates/app/api/health/route.ts +29 -0
- package/dist/templates/app/api/internal/user-metadata/route.ts +36 -0
- package/dist/templates/app/api/superadmin/subscriptions/route.ts +310 -0
- package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +286 -0
- package/dist/templates/app/api/superadmin/teams/route.ts +188 -0
- package/dist/templates/app/api/superadmin/users/[userId]/route.ts +540 -0
- package/dist/templates/app/api/superadmin/users/route.ts +323 -0
- package/dist/templates/app/api/user/delete-account/route.ts +55 -0
- package/dist/templates/app/api/user/plan-flags/route.ts +283 -0
- package/dist/templates/app/api/user/profile/route.ts +133 -0
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +210 -0
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +331 -0
- package/dist/templates/app/api/v1/[entity]/[id]/route.ts +35 -0
- package/dist/templates/app/api/v1/[entity]/docs.md +369 -0
- package/dist/templates/app/api/v1/[entity]/presets.ts +194 -0
- package/dist/templates/app/api/v1/[entity]/route.ts +31 -0
- package/dist/templates/app/api/v1/api-keys/[id]/route.ts +303 -0
- package/dist/templates/app/api/v1/api-keys/docs.md +101 -0
- package/dist/templates/app/api/v1/api-keys/presets.ts +31 -0
- package/dist/templates/app/api/v1/api-keys/route.ts +250 -0
- package/dist/templates/app/api/v1/auth/docs.md +184 -0
- package/dist/templates/app/api/v1/auth/presets.ts +44 -0
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +227 -0
- package/dist/templates/app/api/v1/billing/cancel/route.ts +206 -0
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +97 -0
- package/dist/templates/app/api/v1/billing/check-action/route.ts +81 -0
- package/dist/templates/app/api/v1/billing/checkout/route.ts +124 -0
- package/dist/templates/app/api/v1/billing/docs.md +209 -0
- package/dist/templates/app/api/v1/billing/plans/route.ts +85 -0
- package/dist/templates/app/api/v1/billing/portal/route.ts +90 -0
- package/dist/templates/app/api/v1/billing/presets.ts +121 -0
- package/dist/templates/app/api/v1/billing/webhooks/stripe/route.ts +428 -0
- package/dist/templates/app/api/v1/blocks/[slug]/route.ts +29 -0
- package/dist/templates/app/api/v1/blocks/docs.md +173 -0
- package/dist/templates/app/api/v1/blocks/presets.ts +121 -0
- package/dist/templates/app/api/v1/blocks/route.ts +45 -0
- package/dist/templates/app/api/v1/blocks/validate/route.ts +45 -0
- package/dist/templates/app/api/v1/cron/docs.md +116 -0
- package/dist/templates/app/api/v1/cron/presets.ts +26 -0
- package/dist/templates/app/api/v1/cron/process/route.ts +108 -0
- package/dist/templates/app/api/v1/devtools/blocks/route.ts +82 -0
- package/dist/templates/app/api/v1/devtools/docs/route.ts +150 -0
- package/dist/templates/app/api/v1/devtools/docs.md +204 -0
- package/dist/templates/app/api/v1/devtools/features/route.ts +61 -0
- package/dist/templates/app/api/v1/devtools/flows/route.ts +61 -0
- package/dist/templates/app/api/v1/devtools/presets.ts +113 -0
- package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +120 -0
- package/dist/templates/app/api/v1/devtools/testing/route.ts +82 -0
- package/dist/templates/app/api/v1/media/docs.md +117 -0
- package/dist/templates/app/api/v1/media/presets.ts +24 -0
- package/dist/templates/app/api/v1/media/upload/route.ts +150 -0
- package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +116 -0
- package/dist/templates/app/api/v1/plugin/[...path]/route.ts +373 -0
- package/dist/templates/app/api/v1/plugin/docs.md +79 -0
- package/dist/templates/app/api/v1/plugin/presets.ts +21 -0
- package/dist/templates/app/api/v1/plugin/route.ts +96 -0
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +255 -0
- package/dist/templates/app/api/v1/post-categories/docs.md +134 -0
- package/dist/templates/app/api/v1/post-categories/presets.ts +78 -0
- package/dist/templates/app/api/v1/post-categories/route.ts +119 -0
- package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +179 -0
- package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +120 -0
- package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +89 -0
- package/dist/templates/app/api/v1/team-invitations/docs.md +88 -0
- package/dist/templates/app/api/v1/team-invitations/presets.ts +43 -0
- package/dist/templates/app/api/v1/team-invitations/route.ts +114 -0
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +171 -0
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +105 -0
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +125 -0
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +263 -0
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +358 -0
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +322 -0
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +50 -0
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +91 -0
- package/dist/templates/app/api/v1/teams/docs.md +320 -0
- package/dist/templates/app/api/v1/teams/presets.ts +178 -0
- package/dist/templates/app/api/v1/teams/route.ts +293 -0
- package/dist/templates/app/api/v1/teams/switch/route.ts +88 -0
- package/dist/templates/app/api/v1/theme/[...path]/route.ts +361 -0
- package/dist/templates/app/api/v1/theme/docs.md +74 -0
- package/dist/templates/app/api/v1/theme/presets.ts +21 -0
- package/dist/templates/app/api/v1/theme/route.ts +96 -0
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +363 -0
- package/dist/templates/app/api/v1/users/[id]/route.ts +302 -0
- package/dist/templates/app/api/v1/users/docs.md +93 -0
- package/dist/templates/app/api/v1/users/presets.ts +59 -0
- package/dist/templates/app/api/v1/users/route.ts +197 -0
- package/dist/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +117 -0
- package/dist/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +103 -0
- package/dist/templates/app/dashboard/(main)/[entity]/create/page.tsx +95 -0
- package/dist/templates/app/dashboard/(main)/[entity]/error.tsx +51 -0
- package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +113 -0
- package/dist/templates/app/dashboard/(main)/[entity]/loading.tsx +61 -0
- package/dist/templates/app/dashboard/(main)/[entity]/page.tsx +90 -0
- package/dist/templates/app/dashboard/(main)/layout.tsx +98 -0
- package/dist/templates/app/dashboard/(main)/loading.tsx +5 -0
- package/dist/templates/app/dashboard/(main)/page.tsx +201 -0
- package/dist/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +114 -0
- package/dist/templates/app/dashboard/(main)/patterns/[id]/page.tsx +20 -0
- package/dist/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +171 -0
- package/dist/templates/app/dashboard/(main)/patterns/create/page.tsx +86 -0
- package/dist/templates/app/dashboard/(main)/patterns/page.tsx +444 -0
- package/dist/templates/app/dashboard/features/analytics/page.tsx +35 -0
- package/dist/templates/app/dashboard/features/automation/page.tsx +35 -0
- package/dist/templates/app/dashboard/features/layout.tsx +13 -0
- package/dist/templates/app/dashboard/features/loading.tsx +5 -0
- package/dist/templates/app/dashboard/features/webhooks/page.tsx +35 -0
- package/dist/templates/app/dashboard/layout.tsx +86 -0
- package/dist/templates/app/dashboard/permission-denied/page.tsx +29 -0
- package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/api-keys/page.tsx +513 -0
- package/dist/templates/app/dashboard/settings/billing/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/billing/page.tsx +284 -0
- package/dist/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +222 -0
- package/dist/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/invoices/page.tsx +82 -0
- package/dist/templates/app/dashboard/settings/layout.tsx +151 -0
- package/dist/templates/app/dashboard/settings/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/notifications/page.tsx +462 -0
- package/dist/templates/app/dashboard/settings/page.tsx +92 -0
- package/dist/templates/app/dashboard/settings/password/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/password/page.tsx +306 -0
- package/dist/templates/app/dashboard/settings/plans/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/plans/page.tsx +40 -0
- package/dist/templates/app/dashboard/settings/profile/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/profile/page.tsx +686 -0
- package/dist/templates/app/dashboard/settings/security/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/security/page.tsx +505 -0
- package/dist/templates/app/dashboard/settings/teams/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/teams/page.tsx +272 -0
- package/dist/templates/app/dashboard/settings/teams/permissions/page.tsx +92 -0
- package/dist/templates/app/devtools/blocks/[slug]/page.tsx +39 -0
- package/dist/templates/app/devtools/blocks/page.tsx +31 -0
- package/dist/templates/app/devtools/config/page.tsx +31 -0
- package/dist/templates/app/devtools/features/page.tsx +31 -0
- package/dist/templates/app/devtools/flows/page.tsx +31 -0
- package/dist/templates/app/devtools/layout.tsx +58 -0
- package/dist/templates/app/devtools/page.tsx +121 -0
- package/dist/templates/app/devtools/scheduled-actions/page.tsx +157 -0
- package/dist/templates/app/devtools/style/page.tsx +330 -0
- package/dist/templates/app/devtools/tags/page.tsx +31 -0
- package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +47 -0
- package/dist/templates/app/favicon.ico +0 -0
- package/dist/templates/app/globals.css +12 -0
- package/dist/templates/app/layout.tsx +96 -0
- package/dist/templates/app/public/page.tsx +30 -0
- package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +92 -0
- package/dist/templates/app/superadmin/docs/page.tsx +75 -0
- package/dist/templates/app/superadmin/layout.tsx +67 -0
- package/dist/templates/app/superadmin/page.tsx +149 -0
- package/dist/templates/app/superadmin/subscriptions/page.tsx +655 -0
- package/dist/templates/app/superadmin/team-roles/page.tsx +493 -0
- package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +687 -0
- package/dist/templates/app/superadmin/teams/page.tsx +302 -0
- package/dist/templates/app/superadmin/users/[userId]/page.tsx +548 -0
- package/dist/templates/app/superadmin/users/page.tsx +528 -0
- package/package.json +15 -15
- 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/templates/app/(auth)/forgot-password/page.tsx +216 -0
- package/templates/app/(auth)/layout.tsx +51 -0
- package/templates/app/(auth)/login/page.tsx +21 -0
- package/templates/app/(auth)/reset-password/page.tsx +212 -0
- package/templates/app/(auth)/signup/page.tsx +21 -0
- package/templates/app/(auth)/verify-email/page.tsx +190 -0
- package/templates/app/(public)/[...slug]/page.tsx +378 -0
- package/templates/app/(public)/docs/[section]/[page]/page.tsx +90 -0
- package/templates/app/(public)/docs/layout.tsx +25 -0
- package/templates/app/(public)/docs/page.tsx +81 -0
- package/templates/app/(public)/layout.tsx +41 -0
- package/templates/app/(public)/page.tsx +19 -0
- package/templates/app/403/page.tsx +89 -0
- package/templates/app/api/auth/[...all]/route.ts +78 -0
- package/templates/app/api/cron/billing/lifecycle/route.ts +98 -0
- package/templates/app/api/csp-report/route.ts +175 -0
- package/templates/app/api/devtools/config/entities/route.ts +108 -0
- package/templates/app/api/devtools/config/theme/route.ts +66 -0
- package/templates/app/api/devtools/tests/[...path]/route.ts +130 -0
- package/templates/app/api/devtools/tests/route.ts +134 -0
- package/templates/app/api/health/route.ts +29 -0
- package/templates/app/api/internal/user-metadata/route.ts +36 -0
- package/templates/app/api/superadmin/subscriptions/route.ts +310 -0
- package/templates/app/api/superadmin/teams/[teamId]/route.ts +286 -0
- package/templates/app/api/superadmin/teams/route.ts +188 -0
- package/templates/app/api/superadmin/users/[userId]/route.ts +540 -0
- package/templates/app/api/superadmin/users/route.ts +323 -0
- package/templates/app/api/user/delete-account/route.ts +55 -0
- package/templates/app/api/user/plan-flags/route.ts +283 -0
- package/templates/app/api/user/profile/route.ts +133 -0
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +210 -0
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +331 -0
- package/templates/app/api/v1/[entity]/[id]/route.ts +35 -0
- package/templates/app/api/v1/[entity]/docs.md +369 -0
- package/templates/app/api/v1/[entity]/presets.ts +194 -0
- package/templates/app/api/v1/[entity]/route.ts +31 -0
- package/templates/app/api/v1/api-keys/[id]/route.ts +303 -0
- package/templates/app/api/v1/api-keys/docs.md +101 -0
- package/templates/app/api/v1/api-keys/presets.ts +31 -0
- package/templates/app/api/v1/api-keys/route.ts +250 -0
- package/templates/app/api/v1/auth/docs.md +184 -0
- package/templates/app/api/v1/auth/presets.ts +44 -0
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +227 -0
- package/templates/app/api/v1/billing/cancel/route.ts +206 -0
- package/templates/app/api/v1/billing/change-plan/route.ts +97 -0
- package/templates/app/api/v1/billing/check-action/route.ts +81 -0
- package/templates/app/api/v1/billing/checkout/route.ts +124 -0
- package/templates/app/api/v1/billing/docs.md +209 -0
- package/templates/app/api/v1/billing/plans/route.ts +85 -0
- package/templates/app/api/v1/billing/portal/route.ts +90 -0
- package/templates/app/api/v1/billing/presets.ts +121 -0
- package/templates/app/api/v1/billing/webhooks/stripe/route.ts +428 -0
- package/templates/app/api/v1/blocks/[slug]/route.ts +29 -0
- package/templates/app/api/v1/blocks/docs.md +173 -0
- package/templates/app/api/v1/blocks/presets.ts +121 -0
- package/templates/app/api/v1/blocks/route.ts +45 -0
- package/templates/app/api/v1/blocks/validate/route.ts +45 -0
- package/templates/app/api/v1/cron/docs.md +116 -0
- package/templates/app/api/v1/cron/presets.ts +26 -0
- package/templates/app/api/v1/cron/process/route.ts +108 -0
- package/templates/app/api/v1/devtools/blocks/route.ts +82 -0
- package/templates/app/api/v1/devtools/docs/route.ts +150 -0
- package/templates/app/api/v1/devtools/docs.md +204 -0
- package/templates/app/api/v1/devtools/features/route.ts +61 -0
- package/templates/app/api/v1/devtools/flows/route.ts +61 -0
- package/templates/app/api/v1/devtools/presets.ts +113 -0
- package/templates/app/api/v1/devtools/scheduled-actions/route.ts +120 -0
- package/templates/app/api/v1/devtools/testing/route.ts +82 -0
- package/templates/app/api/v1/media/docs.md +117 -0
- package/templates/app/api/v1/media/presets.ts +24 -0
- package/templates/app/api/v1/media/upload/route.ts +150 -0
- package/templates/app/api/v1/patterns/[id]/usages/route.ts +116 -0
- package/templates/app/api/v1/plugin/[...path]/route.ts +373 -0
- package/templates/app/api/v1/plugin/docs.md +79 -0
- package/templates/app/api/v1/plugin/presets.ts +21 -0
- package/templates/app/api/v1/plugin/route.ts +96 -0
- package/templates/app/api/v1/post-categories/[id]/route.ts +255 -0
- package/templates/app/api/v1/post-categories/docs.md +134 -0
- package/templates/app/api/v1/post-categories/presets.ts +78 -0
- package/templates/app/api/v1/post-categories/route.ts +119 -0
- package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +179 -0
- package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +120 -0
- package/templates/app/api/v1/team-invitations/[token]/route.ts +89 -0
- package/templates/app/api/v1/team-invitations/docs.md +88 -0
- package/templates/app/api/v1/team-invitations/presets.ts +43 -0
- package/templates/app/api/v1/team-invitations/route.ts +114 -0
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +171 -0
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +105 -0
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +125 -0
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +263 -0
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +358 -0
- package/templates/app/api/v1/teams/[teamId]/route.ts +322 -0
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +50 -0
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +91 -0
- package/templates/app/api/v1/teams/docs.md +320 -0
- package/templates/app/api/v1/teams/presets.ts +178 -0
- package/templates/app/api/v1/teams/route.ts +293 -0
- package/templates/app/api/v1/teams/switch/route.ts +88 -0
- package/templates/app/api/v1/theme/[...path]/route.ts +361 -0
- package/templates/app/api/v1/theme/docs.md +74 -0
- package/templates/app/api/v1/theme/presets.ts +21 -0
- package/templates/app/api/v1/theme/route.ts +96 -0
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +363 -0
- package/templates/app/api/v1/users/[id]/route.ts +302 -0
- package/templates/app/api/v1/users/docs.md +93 -0
- package/templates/app/api/v1/users/presets.ts +59 -0
- package/templates/app/api/v1/users/route.ts +197 -0
- package/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +117 -0
- package/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +103 -0
- package/templates/app/dashboard/(main)/[entity]/create/page.tsx +95 -0
- package/templates/app/dashboard/(main)/[entity]/error.tsx +51 -0
- package/templates/app/dashboard/(main)/[entity]/layout.tsx +113 -0
- package/templates/app/dashboard/(main)/[entity]/loading.tsx +61 -0
- package/templates/app/dashboard/(main)/[entity]/page.tsx +90 -0
- package/templates/app/dashboard/(main)/layout.tsx +98 -0
- package/templates/app/dashboard/(main)/loading.tsx +5 -0
- package/templates/app/dashboard/(main)/page.tsx +201 -0
- package/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +114 -0
- package/templates/app/dashboard/(main)/patterns/[id]/page.tsx +20 -0
- package/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +171 -0
- package/templates/app/dashboard/(main)/patterns/create/page.tsx +86 -0
- package/templates/app/dashboard/(main)/patterns/page.tsx +444 -0
- package/templates/app/dashboard/features/analytics/page.tsx +35 -0
- package/templates/app/dashboard/features/automation/page.tsx +35 -0
- package/templates/app/dashboard/features/layout.tsx +13 -0
- package/templates/app/dashboard/features/loading.tsx +5 -0
- package/templates/app/dashboard/features/webhooks/page.tsx +35 -0
- package/templates/app/dashboard/layout.tsx +86 -0
- package/templates/app/dashboard/permission-denied/page.tsx +29 -0
- package/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
- package/templates/app/dashboard/settings/api-keys/page.tsx +513 -0
- package/templates/app/dashboard/settings/billing/loading.tsx +5 -0
- package/templates/app/dashboard/settings/billing/page.tsx +284 -0
- package/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +222 -0
- package/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
- package/templates/app/dashboard/settings/invoices/page.tsx +82 -0
- package/templates/app/dashboard/settings/layout.tsx +151 -0
- package/templates/app/dashboard/settings/loading.tsx +5 -0
- package/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
- package/templates/app/dashboard/settings/notifications/page.tsx +462 -0
- package/templates/app/dashboard/settings/page.tsx +92 -0
- package/templates/app/dashboard/settings/password/loading.tsx +5 -0
- package/templates/app/dashboard/settings/password/page.tsx +306 -0
- package/templates/app/dashboard/settings/plans/loading.tsx +5 -0
- package/templates/app/dashboard/settings/plans/page.tsx +40 -0
- package/templates/app/dashboard/settings/profile/loading.tsx +5 -0
- package/templates/app/dashboard/settings/profile/page.tsx +686 -0
- package/templates/app/dashboard/settings/security/loading.tsx +5 -0
- package/templates/app/dashboard/settings/security/page.tsx +505 -0
- package/templates/app/dashboard/settings/teams/loading.tsx +5 -0
- package/templates/app/dashboard/settings/teams/page.tsx +272 -0
- package/templates/app/dashboard/settings/teams/permissions/page.tsx +92 -0
- package/templates/app/devtools/blocks/[slug]/page.tsx +39 -0
- package/templates/app/devtools/blocks/page.tsx +31 -0
- package/templates/app/devtools/config/page.tsx +31 -0
- package/templates/app/devtools/features/page.tsx +31 -0
- package/templates/app/devtools/flows/page.tsx +31 -0
- package/templates/app/devtools/layout.tsx +58 -0
- package/templates/app/devtools/page.tsx +121 -0
- package/templates/app/devtools/scheduled-actions/page.tsx +157 -0
- package/templates/app/devtools/style/page.tsx +330 -0
- package/templates/app/devtools/tags/page.tsx +31 -0
- package/templates/app/devtools/tests/[[...path]]/page.tsx +47 -0
- package/templates/app/favicon.ico +0 -0
- package/templates/app/globals.css +12 -0
- package/templates/app/layout.tsx +96 -0
- package/templates/app/public/page.tsx +30 -0
- package/templates/app/superadmin/docs/[section]/[page]/page.tsx +92 -0
- package/templates/app/superadmin/docs/page.tsx +75 -0
- package/templates/app/superadmin/layout.tsx +67 -0
- package/templates/app/superadmin/page.tsx +149 -0
- package/templates/app/superadmin/subscriptions/page.tsx +655 -0
- package/templates/app/superadmin/team-roles/page.tsx +493 -0
- package/templates/app/superadmin/teams/[teamId]/page.tsx +687 -0
- package/templates/app/superadmin/teams/page.tsx +302 -0
- package/templates/app/superadmin/users/[userId]/page.tsx +548 -0
- package/templates/app/superadmin/users/page.tsx +528 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@nextsparkjs/core/lib/auth';
|
|
3
|
+
import { queryWithRLS } from '@nextsparkjs/core/lib/db';
|
|
4
|
+
import type { User } from '@nextsparkjs/core/types/user.types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* GET /api/superadmin/users
|
|
8
|
+
*
|
|
9
|
+
* Retrieves users with pagination, search, and filters.
|
|
10
|
+
* Only accessible by superadmin or developer users.
|
|
11
|
+
*
|
|
12
|
+
* Query params:
|
|
13
|
+
* - search: Filter by name or email
|
|
14
|
+
* - role: Filter by role (member, superadmin, etc.)
|
|
15
|
+
* - status: Filter by email verification status (verified, unverified)
|
|
16
|
+
* - tab: Which tab to paginate (users, superadmins, all)
|
|
17
|
+
* - page: Page number (default: 1)
|
|
18
|
+
* - limit: Items per page (default: 20)
|
|
19
|
+
*
|
|
20
|
+
* Returns:
|
|
21
|
+
* - regularUsers: Array of users excluding superadmins
|
|
22
|
+
* - superadmins: Array of superadmin users
|
|
23
|
+
* - counts: Object with user counts
|
|
24
|
+
* - pagination: Pagination info for the active tab
|
|
25
|
+
*/
|
|
26
|
+
export async function GET(request: NextRequest) {
|
|
27
|
+
try {
|
|
28
|
+
// Get the current session using Better Auth
|
|
29
|
+
const session = await auth.api.getSession({
|
|
30
|
+
headers: request.headers
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Check if user is authenticated
|
|
34
|
+
if (!session?.user) {
|
|
35
|
+
return NextResponse.json(
|
|
36
|
+
{ error: 'Unauthorized - No session found' },
|
|
37
|
+
{ status: 401 }
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if user is superadmin or developer
|
|
42
|
+
if (session.user.role !== 'superadmin' && session.user.role !== 'developer') {
|
|
43
|
+
return NextResponse.json(
|
|
44
|
+
{ error: 'Forbidden - Superadmin or developer access required' },
|
|
45
|
+
{ status: 403 }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Parse query params
|
|
50
|
+
const searchParams = request.nextUrl.searchParams;
|
|
51
|
+
const search = searchParams.get('search') || '';
|
|
52
|
+
const roleFilter = searchParams.get('role') || '';
|
|
53
|
+
const statusFilter = searchParams.get('status') || '';
|
|
54
|
+
const tab = searchParams.get('tab') || 'users'; // users, superadmins, all
|
|
55
|
+
const page = Math.max(1, parseInt(searchParams.get('page') || '1'));
|
|
56
|
+
const limit = Math.min(100, Math.max(1, parseInt(searchParams.get('limit') || '20')));
|
|
57
|
+
const offset = (page - 1) * limit;
|
|
58
|
+
|
|
59
|
+
// Build WHERE conditions
|
|
60
|
+
const buildWhereClause = (isSuperadmin: boolean) => {
|
|
61
|
+
const conditions: string[] = [];
|
|
62
|
+
const params: (string | boolean)[] = [];
|
|
63
|
+
let paramIndex = 1;
|
|
64
|
+
|
|
65
|
+
// Role filter for regular users vs superadmins
|
|
66
|
+
if (isSuperadmin) {
|
|
67
|
+
conditions.push(`role = $${paramIndex}`);
|
|
68
|
+
params.push('superadmin');
|
|
69
|
+
paramIndex++;
|
|
70
|
+
} else {
|
|
71
|
+
conditions.push(`role != $${paramIndex}`);
|
|
72
|
+
params.push('superadmin');
|
|
73
|
+
paramIndex++;
|
|
74
|
+
|
|
75
|
+
// Additional role filter for regular users
|
|
76
|
+
if (roleFilter && roleFilter !== 'superadmin') {
|
|
77
|
+
conditions.push(`role = $${paramIndex}`);
|
|
78
|
+
params.push(roleFilter);
|
|
79
|
+
paramIndex++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Search filter
|
|
84
|
+
if (search) {
|
|
85
|
+
conditions.push(`(
|
|
86
|
+
"firstName" ILIKE $${paramIndex} OR
|
|
87
|
+
"lastName" ILIKE $${paramIndex} OR
|
|
88
|
+
email ILIKE $${paramIndex} OR
|
|
89
|
+
CONCAT("firstName", ' ', "lastName") ILIKE $${paramIndex}
|
|
90
|
+
)`);
|
|
91
|
+
params.push(`%${search}%`);
|
|
92
|
+
paramIndex++;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Status filter
|
|
96
|
+
if (statusFilter === 'verified') {
|
|
97
|
+
conditions.push(`"emailVerified" = $${paramIndex}`);
|
|
98
|
+
params.push(true);
|
|
99
|
+
paramIndex++;
|
|
100
|
+
} else if (statusFilter === 'unverified') {
|
|
101
|
+
conditions.push(`("emailVerified" = $${paramIndex} OR "emailVerified" IS NULL)`);
|
|
102
|
+
params.push(false);
|
|
103
|
+
paramIndex++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
whereClause: conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '',
|
|
108
|
+
params
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Build queries for regular users
|
|
113
|
+
const regularWhere = buildWhereClause(false);
|
|
114
|
+
const regularUsersQuery = `
|
|
115
|
+
SELECT
|
|
116
|
+
id,
|
|
117
|
+
"firstName",
|
|
118
|
+
"lastName",
|
|
119
|
+
email,
|
|
120
|
+
role,
|
|
121
|
+
"emailVerified",
|
|
122
|
+
"createdAt",
|
|
123
|
+
"updatedAt"
|
|
124
|
+
FROM "users"
|
|
125
|
+
${regularWhere.whereClause}
|
|
126
|
+
ORDER BY "createdAt" DESC
|
|
127
|
+
${tab === 'users' ? `LIMIT ${limit} OFFSET ${offset}` : ''}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
const regularCountQuery = `
|
|
131
|
+
SELECT COUNT(*)::int as total
|
|
132
|
+
FROM "users"
|
|
133
|
+
${regularWhere.whereClause}
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
// Build queries for superadmins
|
|
137
|
+
const superadminWhere = buildWhereClause(true);
|
|
138
|
+
const superadminsQuery = `
|
|
139
|
+
SELECT
|
|
140
|
+
id,
|
|
141
|
+
"firstName",
|
|
142
|
+
"lastName",
|
|
143
|
+
email,
|
|
144
|
+
role,
|
|
145
|
+
"emailVerified",
|
|
146
|
+
"createdAt",
|
|
147
|
+
"updatedAt"
|
|
148
|
+
FROM "users"
|
|
149
|
+
${superadminWhere.whereClause}
|
|
150
|
+
ORDER BY "createdAt" DESC
|
|
151
|
+
${tab === 'superadmins' ? `LIMIT ${limit} OFFSET ${offset}` : ''}
|
|
152
|
+
`;
|
|
153
|
+
|
|
154
|
+
const superadminCountQuery = `
|
|
155
|
+
SELECT COUNT(*)::int as total
|
|
156
|
+
FROM "users"
|
|
157
|
+
${superadminWhere.whereClause}
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
// Query for status distribution (all users)
|
|
161
|
+
const statusDistributionQuery = `
|
|
162
|
+
SELECT
|
|
163
|
+
CASE WHEN "emailVerified" = true THEN 'verified' ELSE 'unverified' END as status,
|
|
164
|
+
COUNT(*)::int as count
|
|
165
|
+
FROM "users"
|
|
166
|
+
GROUP BY "emailVerified"
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
// Query for team role distribution
|
|
170
|
+
const teamRoleDistributionQuery = `
|
|
171
|
+
SELECT
|
|
172
|
+
role,
|
|
173
|
+
COUNT(*)::int as count
|
|
174
|
+
FROM "team_members"
|
|
175
|
+
GROUP BY role
|
|
176
|
+
`;
|
|
177
|
+
|
|
178
|
+
// Query for teams count
|
|
179
|
+
const teamsCountQuery = `
|
|
180
|
+
SELECT COUNT(*)::int as total
|
|
181
|
+
FROM "teams"
|
|
182
|
+
`;
|
|
183
|
+
|
|
184
|
+
// Execute all queries in parallel
|
|
185
|
+
const [
|
|
186
|
+
regularUsers,
|
|
187
|
+
regularCount,
|
|
188
|
+
superadmins,
|
|
189
|
+
superadminCount,
|
|
190
|
+
statusDistribution,
|
|
191
|
+
teamRoleDistribution,
|
|
192
|
+
teamsCount
|
|
193
|
+
] = await Promise.all([
|
|
194
|
+
queryWithRLS(regularUsersQuery, regularWhere.params, session.user.id) as Promise<User[]>,
|
|
195
|
+
queryWithRLS(regularCountQuery, regularWhere.params, session.user.id) as Promise<{ total: number }[]>,
|
|
196
|
+
queryWithRLS(superadminsQuery, superadminWhere.params, session.user.id) as Promise<User[]>,
|
|
197
|
+
queryWithRLS(superadminCountQuery, superadminWhere.params, session.user.id) as Promise<{ total: number }[]>,
|
|
198
|
+
queryWithRLS(statusDistributionQuery, [], session.user.id) as Promise<{ status: string; count: number }[]>,
|
|
199
|
+
queryWithRLS(teamRoleDistributionQuery, [], session.user.id) as Promise<{ role: string; count: number }[]>,
|
|
200
|
+
queryWithRLS(teamsCountQuery, [], session.user.id) as Promise<{ total: number }[]>
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
const regularTotal = regularCount[0]?.total || 0;
|
|
204
|
+
const superadminTotal = superadminCount[0]?.total || 0;
|
|
205
|
+
|
|
206
|
+
// Calculate counts by role
|
|
207
|
+
const roleCounts = regularUsers.reduce((acc: Record<string, number>, user: User) => {
|
|
208
|
+
acc[user.role] = (acc[user.role] || 0) + 1;
|
|
209
|
+
return acc;
|
|
210
|
+
}, {});
|
|
211
|
+
|
|
212
|
+
// Build status distribution object
|
|
213
|
+
const statusDist: Record<string, number> = { verified: 0, unverified: 0 };
|
|
214
|
+
statusDistribution.forEach(({ status, count }) => {
|
|
215
|
+
statusDist[status] = count;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Build team role distribution object
|
|
219
|
+
const teamRoleDist: Record<string, number> = { owner: 0, admin: 0, member: 0, viewer: 0 };
|
|
220
|
+
teamRoleDistribution.forEach(({ role, count }) => {
|
|
221
|
+
teamRoleDist[role] = count;
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Determine pagination based on active tab
|
|
225
|
+
let paginationTotal = 0;
|
|
226
|
+
if (tab === 'users') {
|
|
227
|
+
paginationTotal = regularTotal;
|
|
228
|
+
} else if (tab === 'superadmins') {
|
|
229
|
+
paginationTotal = superadminTotal;
|
|
230
|
+
} else {
|
|
231
|
+
paginationTotal = regularTotal + superadminTotal;
|
|
232
|
+
}
|
|
233
|
+
const totalPages = Math.ceil(paginationTotal / limit);
|
|
234
|
+
|
|
235
|
+
// Format user data
|
|
236
|
+
const formatUser = (user: User) => ({
|
|
237
|
+
id: user.id,
|
|
238
|
+
firstName: user.firstName || '',
|
|
239
|
+
lastName: user.lastName || '',
|
|
240
|
+
email: user.email,
|
|
241
|
+
role: user.role,
|
|
242
|
+
emailVerified: user.emailVerified,
|
|
243
|
+
createdAt: user.createdAt,
|
|
244
|
+
updatedAt: user.updatedAt,
|
|
245
|
+
fullName: [user.firstName, user.lastName].filter(Boolean).join(' ') || user.email
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Prepare response data
|
|
249
|
+
const responseData = {
|
|
250
|
+
regularUsers: regularUsers.map(formatUser),
|
|
251
|
+
superadmins: superadmins.map(formatUser),
|
|
252
|
+
counts: {
|
|
253
|
+
total: regularTotal + superadminTotal,
|
|
254
|
+
regularUsers: regularTotal,
|
|
255
|
+
superadmins: superadminTotal,
|
|
256
|
+
teams: teamsCount[0]?.total || 0,
|
|
257
|
+
byRole: {
|
|
258
|
+
...roleCounts,
|
|
259
|
+
superadmin: superadminTotal
|
|
260
|
+
},
|
|
261
|
+
statusDistribution: statusDist,
|
|
262
|
+
teamRoleDistribution: teamRoleDist
|
|
263
|
+
},
|
|
264
|
+
pagination: {
|
|
265
|
+
page,
|
|
266
|
+
limit,
|
|
267
|
+
total: paginationTotal,
|
|
268
|
+
totalPages,
|
|
269
|
+
hasMore: page < totalPages
|
|
270
|
+
},
|
|
271
|
+
filters: {
|
|
272
|
+
search,
|
|
273
|
+
role: roleFilter,
|
|
274
|
+
status: statusFilter,
|
|
275
|
+
tab
|
|
276
|
+
},
|
|
277
|
+
metadata: {
|
|
278
|
+
requestedBy: session.user.id,
|
|
279
|
+
requestedAt: new Date().toISOString(),
|
|
280
|
+
source: 'superadmin-api'
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
return NextResponse.json(responseData);
|
|
285
|
+
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error('Error fetching users data:', error);
|
|
288
|
+
|
|
289
|
+
return NextResponse.json(
|
|
290
|
+
{
|
|
291
|
+
error: 'Internal server error',
|
|
292
|
+
message: 'Failed to retrieve users data'
|
|
293
|
+
},
|
|
294
|
+
{ status: 500 }
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* POST /api/superadmin/users
|
|
301
|
+
*
|
|
302
|
+
* Future endpoint for user management actions (create, update roles, etc.)
|
|
303
|
+
* Currently returns not implemented.
|
|
304
|
+
*/
|
|
305
|
+
export async function POST() {
|
|
306
|
+
return NextResponse.json(
|
|
307
|
+
{ error: 'Not implemented yet' },
|
|
308
|
+
{ status: 501 }
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* PUT /api/superadmin/users
|
|
314
|
+
*
|
|
315
|
+
* Future endpoint for bulk user operations
|
|
316
|
+
* Currently returns not implemented.
|
|
317
|
+
*/
|
|
318
|
+
export async function PUT() {
|
|
319
|
+
return NextResponse.json(
|
|
320
|
+
{ error: 'Not implemented yet' },
|
|
321
|
+
{ status: 501 }
|
|
322
|
+
);
|
|
323
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { auth } from "@nextsparkjs/core/lib/auth";
|
|
3
|
+
import { mutateWithRLS } from "@nextsparkjs/core/lib/db";
|
|
4
|
+
|
|
5
|
+
export async function DELETE(req: NextRequest) {
|
|
6
|
+
try {
|
|
7
|
+
// Get session from Better Auth
|
|
8
|
+
const session = await auth.api.getSession({
|
|
9
|
+
headers: req.headers,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!session?.user) {
|
|
13
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const userId = session.user.id;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// Sign out the user first (while the user still exists)
|
|
20
|
+
try {
|
|
21
|
+
await auth.api.signOut({
|
|
22
|
+
headers: req.headers,
|
|
23
|
+
});
|
|
24
|
+
} catch (signOutError) {
|
|
25
|
+
console.warn("Sign out failed (user may already be signed out):", signOutError);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Delete user account and all associated data
|
|
29
|
+
// RLS policies will ensure only the user's own data is deleted
|
|
30
|
+
await mutateWithRLS(
|
|
31
|
+
'DELETE FROM "users" WHERE id = $1',
|
|
32
|
+
[userId],
|
|
33
|
+
userId
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return NextResponse.json({
|
|
37
|
+
message: "Account deleted successfully"
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
} catch (dbError) {
|
|
41
|
+
console.error("Failed to delete user account:", dbError);
|
|
42
|
+
return NextResponse.json(
|
|
43
|
+
{ error: "Failed to delete account" },
|
|
44
|
+
{ status: 500 }
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error("Delete account error:", error);
|
|
50
|
+
return NextResponse.json(
|
|
51
|
+
{ error: "Internal server error" },
|
|
52
|
+
{ status: 500 }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Plan & Flags API Route
|
|
3
|
+
*
|
|
4
|
+
* Handles fetching and updating user plan and flags data
|
|
5
|
+
* for the entity system permission integration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
9
|
+
import { auth } from '@nextsparkjs/core/lib/auth'
|
|
10
|
+
import { getUserPlanAndFlags, updateUserPlan, updateUserFlags } from '@nextsparkjs/core/lib/user-data'
|
|
11
|
+
import { z } from 'zod'
|
|
12
|
+
import type { UserRole } from '@nextsparkjs/core/types/user.types'
|
|
13
|
+
|
|
14
|
+
// Validation schemas
|
|
15
|
+
const planFlagsQuerySchema = z.object({
|
|
16
|
+
userId: z.string().optional()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const planFlagsUpdateSchema = z.object({
|
|
20
|
+
userId: z.string(),
|
|
21
|
+
plan: z.enum(['free', 'starter', 'premium']).optional(),
|
|
22
|
+
flags: z.array(z.enum(['beta_tester', 'early_adopter', 'limited_access', 'vip', 'restricted', 'experimental'])).optional()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* GET /api/user/plan-flags
|
|
27
|
+
* Fetch user plan and flags data
|
|
28
|
+
*/
|
|
29
|
+
export async function GET(request: NextRequest) {
|
|
30
|
+
try {
|
|
31
|
+
// Get session
|
|
32
|
+
const session = await auth.api.getSession({
|
|
33
|
+
headers: request.headers,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if (!session) {
|
|
37
|
+
return NextResponse.json(
|
|
38
|
+
{ error: 'Authentication required' },
|
|
39
|
+
{ status: 401 }
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { searchParams } = new URL(request.url)
|
|
44
|
+
const validation = planFlagsQuerySchema.safeParse({
|
|
45
|
+
userId: searchParams.get('userId')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (!validation.success) {
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{
|
|
51
|
+
error: 'Invalid parameters',
|
|
52
|
+
details: validation.error.flatten().fieldErrors
|
|
53
|
+
},
|
|
54
|
+
{ status: 400 }
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { userId } = validation.data
|
|
59
|
+
const targetUserId = userId || session.user.id
|
|
60
|
+
|
|
61
|
+
// Security check: users can only access their own data unless admin
|
|
62
|
+
const userRole = session.user.role as UserRole
|
|
63
|
+
if (targetUserId !== session.user.id && !['admin', 'superadmin'].includes(userRole)) {
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ error: 'Permission denied' },
|
|
66
|
+
{ status: 403 }
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get user plan and flags
|
|
71
|
+
const planData = await getUserPlanAndFlags(targetUserId)
|
|
72
|
+
|
|
73
|
+
return NextResponse.json({
|
|
74
|
+
userId: targetUserId,
|
|
75
|
+
plan: planData.plan,
|
|
76
|
+
flags: planData.flags,
|
|
77
|
+
cached: planData.cached
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('GET user plan-flags error:', error)
|
|
82
|
+
return NextResponse.json(
|
|
83
|
+
{ error: 'Internal server error' },
|
|
84
|
+
{ status: 500 }
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* PATCH /api/user/plan-flags
|
|
91
|
+
* Update user plan and/or flags
|
|
92
|
+
*/
|
|
93
|
+
export async function PATCH(request: NextRequest) {
|
|
94
|
+
try {
|
|
95
|
+
// Get session
|
|
96
|
+
const session = await auth.api.getSession({
|
|
97
|
+
headers: request.headers,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
if (!session) {
|
|
101
|
+
return NextResponse.json(
|
|
102
|
+
{ error: 'Authentication required' },
|
|
103
|
+
{ status: 401 }
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const body = await request.json()
|
|
108
|
+
const validation = planFlagsUpdateSchema.safeParse(body)
|
|
109
|
+
|
|
110
|
+
if (!validation.success) {
|
|
111
|
+
return NextResponse.json(
|
|
112
|
+
{
|
|
113
|
+
error: 'Invalid request body',
|
|
114
|
+
details: validation.error.flatten().fieldErrors
|
|
115
|
+
},
|
|
116
|
+
{ status: 400 }
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const { userId, plan, flags } = validation.data
|
|
121
|
+
const userRole = session.user.role as UserRole
|
|
122
|
+
|
|
123
|
+
// Security check: users can only update their own data unless admin
|
|
124
|
+
if (userId !== session.user.id && !['admin', 'superadmin'].includes(userRole)) {
|
|
125
|
+
return NextResponse.json(
|
|
126
|
+
{ error: 'Permission denied' },
|
|
127
|
+
{ status: 403 }
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Additional security: only admins can update other users' data
|
|
132
|
+
if (userId !== session.user.id && !['admin', 'superadmin'].includes(userRole)) {
|
|
133
|
+
return NextResponse.json(
|
|
134
|
+
{ error: 'Only admins can update other users data' },
|
|
135
|
+
{ status: 403 }
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const results: { plan?: boolean; flags?: boolean } = {}
|
|
140
|
+
|
|
141
|
+
// Update plan if provided
|
|
142
|
+
if (plan) {
|
|
143
|
+
const planSuccess = await updateUserPlan(userId, plan)
|
|
144
|
+
if (!planSuccess) {
|
|
145
|
+
return NextResponse.json(
|
|
146
|
+
{ error: 'Failed to update user plan' },
|
|
147
|
+
{ status: 500 }
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
results.plan = true
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Update flags if provided
|
|
154
|
+
if (flags) {
|
|
155
|
+
const flagsSuccess = await updateUserFlags(userId, flags)
|
|
156
|
+
if (!flagsSuccess) {
|
|
157
|
+
return NextResponse.json(
|
|
158
|
+
{ error: 'Failed to update user flags' },
|
|
159
|
+
{ status: 500 }
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
results.flags = true
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Get updated data
|
|
166
|
+
const updatedData = await getUserPlanAndFlags(userId)
|
|
167
|
+
|
|
168
|
+
return NextResponse.json({
|
|
169
|
+
success: true,
|
|
170
|
+
updated: results,
|
|
171
|
+
userId,
|
|
172
|
+
plan: updatedData.plan,
|
|
173
|
+
flags: updatedData.flags
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('PATCH user plan-flags error:', error)
|
|
178
|
+
return NextResponse.json(
|
|
179
|
+
{ error: 'Internal server error' },
|
|
180
|
+
{ status: 500 }
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* POST /api/user/plan-flags/bulk
|
|
187
|
+
* Bulk update user plans and flags (admin only)
|
|
188
|
+
*/
|
|
189
|
+
export async function POST(request: NextRequest) {
|
|
190
|
+
try {
|
|
191
|
+
// Get session
|
|
192
|
+
const session = await auth.api.getSession({
|
|
193
|
+
headers: request.headers,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
if (!session) {
|
|
197
|
+
return NextResponse.json(
|
|
198
|
+
{ error: 'Authentication required' },
|
|
199
|
+
{ status: 401 }
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const userRole = session.user.role as UserRole
|
|
204
|
+
|
|
205
|
+
// Only admins can perform bulk operations
|
|
206
|
+
if (!['admin', 'superadmin'].includes(userRole)) {
|
|
207
|
+
return NextResponse.json(
|
|
208
|
+
{ error: 'Admin access required for bulk operations' },
|
|
209
|
+
{ status: 403 }
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const body = await request.json()
|
|
214
|
+
const bulkSchema = z.object({
|
|
215
|
+
updates: z.array(z.object({
|
|
216
|
+
userId: z.string(),
|
|
217
|
+
plan: z.enum(['free', 'starter', 'premium']).optional(),
|
|
218
|
+
flags: z.array(z.enum(['beta_tester', 'early_adopter', 'limited_access', 'vip', 'restricted', 'experimental'])).optional()
|
|
219
|
+
})).max(100) // Limit bulk operations
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const validation = bulkSchema.safeParse(body)
|
|
223
|
+
|
|
224
|
+
if (!validation.success) {
|
|
225
|
+
return NextResponse.json(
|
|
226
|
+
{
|
|
227
|
+
error: 'Invalid bulk request',
|
|
228
|
+
details: validation.error.flatten().fieldErrors
|
|
229
|
+
},
|
|
230
|
+
{ status: 400 }
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const { updates } = validation.data
|
|
235
|
+
const results = []
|
|
236
|
+
|
|
237
|
+
// Process each update
|
|
238
|
+
for (const update of updates) {
|
|
239
|
+
try {
|
|
240
|
+
const updateResult: { userId: string; plan?: boolean; flags?: boolean } = {
|
|
241
|
+
userId: update.userId
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (update.plan) {
|
|
245
|
+
updateResult.plan = await updateUserPlan(update.userId, update.plan)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (update.flags) {
|
|
249
|
+
updateResult.flags = await updateUserFlags(update.userId, update.flags)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
results.push({
|
|
253
|
+
...updateResult,
|
|
254
|
+
success: true
|
|
255
|
+
})
|
|
256
|
+
} catch (error) {
|
|
257
|
+
results.push({
|
|
258
|
+
userId: update.userId,
|
|
259
|
+
success: false,
|
|
260
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const successCount = results.filter(r => r.success).length
|
|
266
|
+
const failureCount = results.length - successCount
|
|
267
|
+
|
|
268
|
+
return NextResponse.json({
|
|
269
|
+
success: failureCount === 0,
|
|
270
|
+
processed: results.length,
|
|
271
|
+
succeeded: successCount,
|
|
272
|
+
failed: failureCount,
|
|
273
|
+
results
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error('POST bulk user plan-flags error:', error)
|
|
278
|
+
return NextResponse.json(
|
|
279
|
+
{ error: 'Internal server error' },
|
|
280
|
+
{ status: 500 }
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
}
|