@nextsparkjs/core 0.1.0-beta.104 → 0.1.0-beta.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/dashboard/misc/QuickCreateDropdown.d.ts.map +1 -1
- package/dist/components/dashboard/misc/QuickCreateDropdown.js +12 -9
- package/dist/lib/entities/registry.client.d.ts +7 -1
- package/dist/lib/entities/registry.client.d.ts.map +1 -1
- package/dist/lib/entities/registry.client.js +12 -1
- package/dist/lib/entities/types.d.ts +6 -3
- package/dist/lib/entities/types.d.ts.map +1 -1
- package/dist/styles/classes.json +1 -1
- package/dist/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -1
- package/dist/templates/app/(auth)/signup/page.tsx +1 -1
- package/dist/templates/app/api/auth/[...all]/route.ts +39 -10
- package/dist/templates/app/api/v1/billing/cancel/route.ts +8 -5
- package/dist/templates/app/api/v1/billing/checkout/route.ts +3 -3
- package/dist/templates/app/api/v1/billing/portal/route.ts +2 -2
- package/dist/templates/app/dashboard/(main)/[entity]/loading.tsx +5 -2
- package/dist/templates/app/dashboard/(main)/loading.tsx +4 -1
- package/dist/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +4 -1
- package/dist/templates/app/dashboard/(main)/patterns/[id]/page.tsx +4 -1
- package/dist/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +4 -1
- package/dist/templates/app/dashboard/(main)/patterns/create/page.tsx +4 -1
- package/dist/templates/app/dashboard/(main)/patterns/page.tsx +4 -1
- package/dist/templates/app/dashboard/features/analytics/page.tsx +4 -1
- package/dist/templates/app/dashboard/features/automation/page.tsx +4 -1
- package/dist/templates/app/dashboard/features/layout.tsx +5 -2
- package/dist/templates/app/dashboard/features/loading.tsx +4 -1
- package/dist/templates/app/dashboard/features/webhooks/page.tsx +4 -1
- package/dist/templates/app/dashboard/permission-denied/page.tsx +4 -1
- package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/billing/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/invoices/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/notifications/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/page.tsx +2 -1
- package/dist/templates/app/dashboard/settings/password/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/plans/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/profile/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/security/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/teams/loading.tsx +4 -1
- package/dist/templates/app/dashboard/settings/teams/permissions/page.tsx +4 -1
- package/dist/templates/contents/themes/starter/messages/de/navigation.json +1 -0
- package/dist/templates/contents/themes/starter/messages/en/navigation.json +1 -0
- package/dist/templates/contents/themes/starter/messages/es/navigation.json +1 -0
- package/dist/templates/contents/themes/starter/messages/fr/navigation.json +1 -0
- package/dist/templates/contents/themes/starter/messages/it/navigation.json +1 -0
- package/dist/templates/contents/themes/starter/messages/pt/navigation.json +1 -0
- package/package.json +2 -2
- package/scripts/build/registry/generators/entity-registry.mjs +13 -1
- package/scripts/build/registry/generators/template-registry.mjs +9 -2
- package/templates/app/(auth)/accept-invite/[token]/page.tsx +4 -1
- package/templates/app/(auth)/signup/page.tsx +1 -1
- package/templates/app/api/auth/[...all]/route.ts +39 -10
- package/templates/app/api/v1/billing/cancel/route.ts +8 -5
- package/templates/app/api/v1/billing/checkout/route.ts +3 -3
- package/templates/app/api/v1/billing/portal/route.ts +2 -2
- package/templates/app/dashboard/(main)/[entity]/loading.tsx +5 -2
- package/templates/app/dashboard/(main)/loading.tsx +4 -1
- package/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +4 -1
- package/templates/app/dashboard/(main)/patterns/[id]/page.tsx +4 -1
- package/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +4 -1
- package/templates/app/dashboard/(main)/patterns/create/page.tsx +4 -1
- package/templates/app/dashboard/(main)/patterns/page.tsx +4 -1
- package/templates/app/dashboard/features/analytics/page.tsx +4 -1
- package/templates/app/dashboard/features/automation/page.tsx +4 -1
- package/templates/app/dashboard/features/layout.tsx +5 -2
- package/templates/app/dashboard/features/loading.tsx +4 -1
- package/templates/app/dashboard/features/webhooks/page.tsx +4 -1
- package/templates/app/dashboard/permission-denied/page.tsx +4 -1
- package/templates/app/dashboard/settings/api-keys/loading.tsx +4 -1
- package/templates/app/dashboard/settings/billing/loading.tsx +4 -1
- package/templates/app/dashboard/settings/invoices/loading.tsx +4 -1
- package/templates/app/dashboard/settings/loading.tsx +4 -1
- package/templates/app/dashboard/settings/notifications/loading.tsx +4 -1
- package/templates/app/dashboard/settings/page.tsx +2 -1
- package/templates/app/dashboard/settings/password/loading.tsx +4 -1
- package/templates/app/dashboard/settings/plans/loading.tsx +4 -1
- package/templates/app/dashboard/settings/profile/loading.tsx +4 -1
- package/templates/app/dashboard/settings/security/loading.tsx +4 -1
- package/templates/app/dashboard/settings/teams/loading.tsx +4 -1
- package/templates/app/dashboard/settings/teams/permissions/page.tsx +4 -1
- package/templates/contents/themes/starter/messages/de/navigation.json +1 -0
- package/templates/contents/themes/starter/messages/en/navigation.json +1 -0
- package/templates/contents/themes/starter/messages/es/navigation.json +1 -0
- package/templates/contents/themes/starter/messages/fr/navigation.json +1 -0
- package/templates/contents/themes/starter/messages/it/navigation.json +1 -0
- package/templates/contents/themes/starter/messages/pt/navigation.json +1 -0
- package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +0 -410
- package/templates/app/api/v1/billing/webhooks/polar/route.ts +0 -410
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonApiKeysPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function ApiKeysLoading() {
|
|
4
5
|
return <SkeletonApiKeysPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/api-keys/loading.tsx', ApiKeysLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonBillingPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function BillingLoading() {
|
|
4
5
|
return <SkeletonBillingPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/billing/loading.tsx', BillingLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonInvoicesPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function InvoicesLoading() {
|
|
4
5
|
return <SkeletonInvoicesPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/invoices/loading.tsx', InvoicesLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonSettingsOverview } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function SettingsLoading() {
|
|
4
5
|
return <SkeletonSettingsOverview />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/loading.tsx', SettingsLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonNotificationsPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function NotificationsLoading() {
|
|
4
5
|
return <SkeletonNotificationsPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/notifications/loading.tsx', NotificationsLoading)
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
Key,
|
|
15
15
|
type LucideIcon
|
|
16
16
|
} from 'lucide-react'
|
|
17
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
17
18
|
|
|
18
19
|
// Icon mapping for settings pages
|
|
19
20
|
const SETTINGS_ICONS: Record<string, LucideIcon> = {
|
|
@@ -89,4 +90,4 @@ function SettingsPage() {
|
|
|
89
90
|
)
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
export default SettingsPage
|
|
93
|
+
export default getTemplateOrDefaultClient('app/dashboard/settings/page.tsx', SettingsPage)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonPasswordPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function PasswordLoading() {
|
|
4
5
|
return <SkeletonPasswordPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/password/loading.tsx', PasswordLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonPlansPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function PlansLoading() {
|
|
4
5
|
return <SkeletonPlansPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/plans/loading.tsx', PlansLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonProfileForm } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function ProfileLoading() {
|
|
4
5
|
return <SkeletonProfileForm />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/profile/loading.tsx', ProfileLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonSecurityPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function SecurityLoading() {
|
|
4
5
|
return <SkeletonSecurityPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/security/loading.tsx', SecurityLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonTeamsPage } from '@nextsparkjs/core/components/ui/skeleton-settings'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function TeamsLoading() {
|
|
4
5
|
return <SkeletonTeamsPage />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/settings/teams/loading.tsx', TeamsLoading)
|
|
@@ -4,6 +4,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@nextsparkjs/core/comp
|
|
|
4
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@nextsparkjs/core/components/ui/card'
|
|
5
5
|
// Use PermissionService which reads from the build-time generated registry
|
|
6
6
|
import { PermissionService } from '@nextsparkjs/core/lib/services/permission.service'
|
|
7
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Team Permissions Page
|
|
@@ -11,7 +12,7 @@ import { PermissionService } from '@nextsparkjs/core/lib/services/permission.ser
|
|
|
11
12
|
* Displays a visual permissions matrix showing what each team role can do.
|
|
12
13
|
* Organized by categories with tabs for easy navigation.
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
+
function TeamPermissionsPage() {
|
|
15
16
|
const t = useTranslations('permissions')
|
|
16
17
|
|
|
17
18
|
// Get all unique categories from registry
|
|
@@ -90,3 +91,5 @@ function RoleCard({ role }: { role: 'owner' | 'admin' | 'member' | 'viewer' }) {
|
|
|
90
91
|
</div>
|
|
91
92
|
)
|
|
92
93
|
}
|
|
94
|
+
|
|
95
|
+
export default getTemplateOrDefault('app/dashboard/settings/teams/permissions/page.tsx', TeamPermissionsPage)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextsparkjs/core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.106",
|
|
4
4
|
"description": "NextSpark - The complete SaaS framework for Next.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "NextSpark <hello@nextspark.dev>",
|
|
@@ -453,7 +453,7 @@
|
|
|
453
453
|
"tailwind-merge": "^3.3.1",
|
|
454
454
|
"uuid": "^13.0.0",
|
|
455
455
|
"zod": "^4.1.5",
|
|
456
|
-
"@nextsparkjs/testing": "0.1.0-beta.
|
|
456
|
+
"@nextsparkjs/testing": "0.1.0-beta.106"
|
|
457
457
|
},
|
|
458
458
|
"scripts": {
|
|
459
459
|
"postinstall": "node scripts/postinstall.mjs || true",
|
|
@@ -212,7 +212,15 @@ export function generateEntityRegistryClient(entities, config) {
|
|
|
212
212
|
return ` '${slug}': '${entity.name}'`
|
|
213
213
|
}).join(',\n')
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
// Generate icon registration calls
|
|
216
|
+
const iconRegistrations = entities
|
|
217
|
+
.filter(entity => entity.exportName)
|
|
218
|
+
.map(entity => `registerEntityIcon('${entity.name}', ${entity.exportName}.icon)`)
|
|
219
|
+
.join('\n')
|
|
220
|
+
|
|
221
|
+
return `'use client'
|
|
222
|
+
|
|
223
|
+
/**
|
|
216
224
|
* Client-Safe Entity Registry
|
|
217
225
|
*
|
|
218
226
|
* Generated at: ${new Date().toISOString()}
|
|
@@ -226,6 +234,10 @@ export function generateEntityRegistryClient(entities, config) {
|
|
|
226
234
|
*/
|
|
227
235
|
|
|
228
236
|
${imports}
|
|
237
|
+
import { registerEntityIcon } from '${convertCorePath('@/core/lib/entities/registry.client', outputFilePath, config)}'
|
|
238
|
+
|
|
239
|
+
// Pre-register entity icons so non-lucide icons survive server→client serialization
|
|
240
|
+
${iconRegistrations}
|
|
229
241
|
|
|
230
242
|
export interface ClientEntityConfig {
|
|
231
243
|
name: string
|
|
@@ -171,9 +171,16 @@ async function hasServerOnlyExports(filePath) {
|
|
|
171
171
|
/export\s+const\s+dynamic\s*=/,
|
|
172
172
|
/export\s+const\s+fetchCache\s*=/,
|
|
173
173
|
/export\s+const\s+runtime\s*=/,
|
|
174
|
+
/export\s+const\s+metadata\s*[=:]/,
|
|
174
175
|
]
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
// Check for server-only module imports
|
|
178
|
+
const serverOnlyImports = [
|
|
179
|
+
/import\s+.*from\s+['"]next\/headers['"]/,
|
|
180
|
+
/import\s+.*from\s+['"]server-only['"]/,
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
for (const pattern of [...serverFunctionExports, ...serverConstExports, ...serverOnlyImports]) {
|
|
177
184
|
if (pattern.test(content)) {
|
|
178
185
|
return true
|
|
179
186
|
}
|
|
@@ -215,7 +222,7 @@ export async function generateTemplateRegistryClient(templates, config) {
|
|
|
215
222
|
.filter(([appPath, pathTemplates]) => {
|
|
216
223
|
const template = pathTemplates[0]
|
|
217
224
|
// Only include page templates (not layouts) that can override components
|
|
218
|
-
return template.templateType === 'page' && canOverrideComponent(appPath)
|
|
225
|
+
return (template.templateType === 'page' || template.templateType === 'layout') && canOverrideComponent(appPath)
|
|
219
226
|
})
|
|
220
227
|
.map(async ([appPath, pathTemplates]) => {
|
|
221
228
|
const template = pathTemplates[0]
|
|
@@ -9,6 +9,7 @@ import { Loader2, CheckCircle, XCircle, Users, LogIn, UserPlus } from 'lucide-re
|
|
|
9
9
|
import { toast } from 'sonner'
|
|
10
10
|
import Link from 'next/link'
|
|
11
11
|
import { sel } from '@nextsparkjs/core/selectors'
|
|
12
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
12
13
|
|
|
13
14
|
type InvitationStatus = 'loading' | 'valid' | 'accepting' | 'accepted' | 'error' | 'expired' | 'not_found' | 'email_mismatch' | 'already_member' | 'requires_auth'
|
|
14
15
|
|
|
@@ -19,7 +20,7 @@ interface InvitationInfo {
|
|
|
19
20
|
email: string
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
function AcceptInvitePage() {
|
|
23
24
|
const params = useParams()
|
|
24
25
|
const router = useRouter()
|
|
25
26
|
const { user, isLoading: authLoading } = useAuth()
|
|
@@ -333,3 +334,5 @@ export default function AcceptInvitePage() {
|
|
|
333
334
|
</div>
|
|
334
335
|
)
|
|
335
336
|
}
|
|
337
|
+
|
|
338
|
+
export default getTemplateOrDefaultClient('app/(auth)/accept-invite/[token]/page.tsx', AcceptInvitePage)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { auth } from "@nextsparkjs/core/lib/auth";
|
|
2
2
|
import { toNextJsHandler } from "better-auth/next-js";
|
|
3
3
|
import { NextRequest, NextResponse } from "next/server";
|
|
4
|
-
import { TEAMS_CONFIG } from "@nextsparkjs/core/lib/config";
|
|
4
|
+
import { TEAMS_CONFIG, AUTH_CONFIG } from "@nextsparkjs/core/lib/config";
|
|
5
5
|
import { isPublicSignupRestricted } from "@nextsparkjs/core/lib/teams/helpers";
|
|
6
|
+
// Registration helpers available if needed: shouldBlockSignup, isDomainAllowed
|
|
7
|
+
// Currently domain validation happens in auth.ts databaseHooks
|
|
6
8
|
import { TeamService } from "@nextsparkjs/core/lib/services";
|
|
7
9
|
import { wrapAuthHandlerWithCors, handleCorsPreflightRequest, addCorsHeaders } from "@nextsparkjs/core/lib/api/helpers";
|
|
8
10
|
|
|
@@ -41,25 +43,52 @@ export async function GET(req: NextRequest, context: { params: Promise<{ all: st
|
|
|
41
43
|
return wrapAuthHandlerWithCors(() => handlers.GET(req), req);
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
// Intercept signup requests to validate
|
|
46
|
+
// Intercept signup requests to validate registration mode
|
|
45
47
|
export async function POST(req: NextRequest) {
|
|
46
48
|
const pathname = req.nextUrl.pathname;
|
|
47
49
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
// Determine request type
|
|
51
|
+
// Comprehensive signup endpoint detection to prevent bypasses
|
|
52
|
+
const signupEndpoints = [
|
|
53
|
+
'/sign-up/email',
|
|
54
|
+
'/sign-up/credentials',
|
|
55
|
+
'/signup',
|
|
56
|
+
'/register',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const isSignupAttempt = signupEndpoints.some(endpoint => pathname.includes(endpoint));
|
|
60
|
+
const isOAuthCallback = pathname.includes('/api/auth/callback/');
|
|
61
|
+
const isSignupRequest = isSignupAttempt || isOAuthCallback;
|
|
52
62
|
|
|
53
63
|
if (isSignupRequest) {
|
|
64
|
+
const registrationMode = AUTH_CONFIG?.registration?.mode ?? 'open';
|
|
54
65
|
const teamsMode = TEAMS_CONFIG.mode;
|
|
55
66
|
|
|
56
|
-
//
|
|
57
|
-
|
|
67
|
+
// --- Registration mode enforcement ---
|
|
68
|
+
|
|
69
|
+
// 1. Domain-restricted mode: block email signup, allow OAuth (validated in database hooks)
|
|
70
|
+
if (registrationMode === 'domain-restricted' && isSignupAttempt && !isOAuthCallback) {
|
|
71
|
+
// Block direct email/password signup in domain-restricted mode
|
|
72
|
+
// Only Google OAuth is allowed (domain validation happens in database hooks)
|
|
73
|
+
const errorResponse = NextResponse.json(
|
|
74
|
+
{
|
|
75
|
+
error: 'Email signup disabled',
|
|
76
|
+
message: 'Please sign up with Google using an authorized email domain.',
|
|
77
|
+
code: 'EMAIL_SIGNUP_DISABLED',
|
|
78
|
+
},
|
|
79
|
+
{ status: 403 }
|
|
80
|
+
);
|
|
81
|
+
return await addCorsHeaders(errorResponse, req);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Note: OAuth domain validation happens in auth.ts databaseHooks (user.create.before)
|
|
85
|
+
// The hook throws an error if the email domain is not in allowedDomains
|
|
86
|
+
|
|
87
|
+
// 2. Invitation-only mode OR single-tenant teams mode: existing behavior
|
|
88
|
+
if (registrationMode === 'invitation-only' || isPublicSignupRestricted(teamsMode)) {
|
|
58
89
|
const teamExists = await TeamService.hasGlobal();
|
|
59
90
|
|
|
60
91
|
if (teamExists) {
|
|
61
|
-
// Block public signup - users must be invited
|
|
62
|
-
// Add CORS headers so mobile apps can read the error message
|
|
63
92
|
const errorResponse = NextResponse.json(
|
|
64
93
|
{
|
|
65
94
|
error: 'Registration is closed',
|
|
@@ -11,7 +11,11 @@ import { NextRequest, NextResponse } from 'next/server'
|
|
|
11
11
|
import { z } from 'zod'
|
|
12
12
|
import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
13
13
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
cancelSubscriptionAtPeriodEnd,
|
|
16
|
+
cancelSubscriptionImmediately,
|
|
17
|
+
reactivateSubscription
|
|
18
|
+
} from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
15
19
|
import { queryWithRLS } from '@nextsparkjs/core/lib/db'
|
|
16
20
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
17
21
|
|
|
@@ -104,11 +108,10 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
104
108
|
|
|
105
109
|
// 6. Cancel via Stripe
|
|
106
110
|
try {
|
|
107
|
-
const gateway = getBillingGateway()
|
|
108
111
|
if (immediate) {
|
|
109
|
-
await
|
|
112
|
+
await cancelSubscriptionImmediately(subscription.externalSubscriptionId)
|
|
110
113
|
} else {
|
|
111
|
-
await
|
|
114
|
+
await cancelSubscriptionAtPeriodEnd(subscription.externalSubscriptionId)
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
// 7. Update local DB
|
|
@@ -171,7 +174,7 @@ async function handleReactivation(teamId: string) {
|
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
try {
|
|
174
|
-
await
|
|
177
|
+
await reactivateSubscription(subscription.externalSubscriptionId)
|
|
175
178
|
|
|
176
179
|
// Update local DB
|
|
177
180
|
await queryWithRLS(
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { NextRequest, NextResponse } from 'next/server'
|
|
13
13
|
import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
14
|
-
import {
|
|
14
|
+
import { createCheckoutSession } from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
15
15
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
16
16
|
import { z } from 'zod'
|
|
17
17
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
@@ -94,8 +94,8 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
94
94
|
const successUrl = `${appUrl}/dashboard/settings/billing?success=true`
|
|
95
95
|
const cancelUrl = `${appUrl}/dashboard/settings/billing?canceled=true`
|
|
96
96
|
|
|
97
|
-
// Create
|
|
98
|
-
const session = await
|
|
97
|
+
// Create Stripe Checkout session
|
|
98
|
+
const session = await createCheckoutSession({
|
|
99
99
|
teamId,
|
|
100
100
|
planSlug,
|
|
101
101
|
billingPeriod,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { NextRequest, NextResponse } from 'next/server'
|
|
11
11
|
import { authenticateRequest, createAuthError } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
12
|
-
import {
|
|
12
|
+
import { createPortalSession } from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
13
13
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
14
14
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
15
15
|
|
|
@@ -69,7 +69,7 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
69
69
|
const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:5173'
|
|
70
70
|
const returnUrl = `${appUrl}/dashboard/settings/billing`
|
|
71
71
|
|
|
72
|
-
const session = await
|
|
72
|
+
const session = await createPortalSession({
|
|
73
73
|
customerId: subscription.externalCustomerId,
|
|
74
74
|
returnUrl
|
|
75
75
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Skeleton } from '@nextsparkjs/core/components/ui/skeleton'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function EntityLoading() {
|
|
4
5
|
return (
|
|
5
6
|
<div className="space-y-6 p-6">
|
|
6
7
|
{/* Header skeleton */}
|
|
@@ -58,4 +59,6 @@ export default function EntityLoading() {
|
|
|
58
59
|
</div>
|
|
59
60
|
</div>
|
|
60
61
|
)
|
|
61
|
-
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default getTemplateOrDefault('app/dashboard/(main)/[entity]/loading.tsx', EntityLoading)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SkeletonDashboardHome } from '@nextsparkjs/core/components/ui/skeleton-dashboard'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function DashboardLoading() {
|
|
4
5
|
return <SkeletonDashboardHome />
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export default getTemplateOrDefault('app/dashboard/(main)/loading.tsx', DashboardLoading)
|
|
@@ -14,8 +14,9 @@ import { EntityFormWrapper } from '@nextsparkjs/core/components/entities/wrapper
|
|
|
14
14
|
import { BuilderEditorView } from '@nextsparkjs/core/components/dashboard/block-editor/builder-editor-view'
|
|
15
15
|
import { Alert, AlertDescription } from '@nextsparkjs/core/components/ui/alert'
|
|
16
16
|
import { getEntityData } from '@nextsparkjs/core/lib/api/entities'
|
|
17
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
function PatternEditPage() {
|
|
19
20
|
const params = useParams()
|
|
20
21
|
const router = useRouter()
|
|
21
22
|
const [entityConfig, setEntityConfig] = useState<ClientEntityConfig | null>(null)
|
|
@@ -112,3 +113,5 @@ export default function PatternEditPage() {
|
|
|
112
113
|
/>
|
|
113
114
|
)
|
|
114
115
|
}
|
|
116
|
+
|
|
117
|
+
export default getTemplateOrDefaultClient('app/dashboard/(main)/patterns/[id]/edit/page.tsx', PatternEditPage)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { redirect } from 'next/navigation'
|
|
8
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
8
9
|
|
|
9
10
|
interface PageProps {
|
|
10
11
|
params: Promise<{
|
|
@@ -12,9 +13,11 @@ interface PageProps {
|
|
|
12
13
|
}>
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
async function PatternDetailPage({ params }: PageProps) {
|
|
16
17
|
const resolvedParams = await params
|
|
17
18
|
|
|
18
19
|
// Patterns are builder-enabled, so redirect to edit view
|
|
19
20
|
redirect(`/dashboard/patterns/${resolvedParams.id}/edit`)
|
|
20
21
|
}
|
|
22
|
+
|
|
23
|
+
export default getTemplateOrDefault('app/dashboard/(main)/patterns/[id]/page.tsx', PatternDetailPage)
|
|
@@ -17,6 +17,7 @@ import { Button } from '@nextsparkjs/core/components/ui/button'
|
|
|
17
17
|
import { Alert, AlertDescription, AlertTitle } from '@nextsparkjs/core/components/ui/alert'
|
|
18
18
|
import { Skeleton } from '@nextsparkjs/core/components/ui/skeleton'
|
|
19
19
|
import { getEntityData } from '@nextsparkjs/core/lib/api/entities'
|
|
20
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
20
21
|
|
|
21
22
|
interface PatternData {
|
|
22
23
|
id: string
|
|
@@ -24,7 +25,7 @@ interface PatternData {
|
|
|
24
25
|
title?: string
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
function PatternReportsPage() {
|
|
28
29
|
const params = useParams()
|
|
29
30
|
const router = useRouter()
|
|
30
31
|
const patternId = params.id as string
|
|
@@ -169,3 +170,5 @@ export default function PatternReportsPage() {
|
|
|
169
170
|
</div>
|
|
170
171
|
)
|
|
171
172
|
}
|
|
173
|
+
|
|
174
|
+
export default getTemplateOrDefaultClient('app/dashboard/(main)/patterns/[id]/reports/page.tsx', PatternReportsPage)
|
|
@@ -12,8 +12,9 @@ import { clientEntityRegistry, ensureClientInitialized, type ClientEntityConfig
|
|
|
12
12
|
import { EntityFormWrapper } from '@nextsparkjs/core/components/entities/wrappers/EntityFormWrapper'
|
|
13
13
|
import { BuilderEditorView } from '@nextsparkjs/core/components/dashboard/block-editor/builder-editor-view'
|
|
14
14
|
import { Alert, AlertDescription } from '@nextsparkjs/core/components/ui/alert'
|
|
15
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
function PatternsCreatePage() {
|
|
17
18
|
const router = useRouter()
|
|
18
19
|
const [entityConfig, setEntityConfig] = useState<ClientEntityConfig | null>(null)
|
|
19
20
|
const [loading, setLoading] = useState(true)
|
|
@@ -84,3 +85,5 @@ export default function PatternsCreatePage() {
|
|
|
84
85
|
/>
|
|
85
86
|
)
|
|
86
87
|
}
|
|
88
|
+
|
|
89
|
+
export default getTemplateOrDefaultClient('app/dashboard/(main)/patterns/create/page.tsx', PatternsCreatePage)
|
|
@@ -28,6 +28,7 @@ import type { Permission } from '@nextsparkjs/core/lib/permissions/types'
|
|
|
28
28
|
import type { QuickAction, DropdownAction } from '@nextsparkjs/core/components/entities/entity-table.types'
|
|
29
29
|
import { sel } from '@nextsparkjs/core/lib/test'
|
|
30
30
|
import { toast } from 'sonner'
|
|
31
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
31
32
|
|
|
32
33
|
interface PatternItem {
|
|
33
34
|
id: string
|
|
@@ -37,7 +38,7 @@ interface PatternItem {
|
|
|
37
38
|
[key: string]: unknown
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
function PatternsListPage() {
|
|
41
42
|
const entityType = 'patterns'
|
|
42
43
|
const router = useRouter()
|
|
43
44
|
|
|
@@ -442,3 +443,5 @@ export default function PatternsListPage() {
|
|
|
442
443
|
</div>
|
|
443
444
|
)
|
|
444
445
|
}
|
|
446
|
+
|
|
447
|
+
export default getTemplateOrDefaultClient('app/dashboard/(main)/patterns/page.tsx', PatternsListPage)
|
|
@@ -4,8 +4,9 @@ import { FeatureGate } from '@nextsparkjs/core/components/billing/FeatureGate'
|
|
|
4
4
|
import { FeaturePlaceholder } from '@nextsparkjs/core/components/billing/FeaturePlaceholder'
|
|
5
5
|
import { BarChart3 } from 'lucide-react'
|
|
6
6
|
import { useTranslations } from 'next-intl'
|
|
7
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
function AdvancedAnalyticsPage() {
|
|
9
10
|
const t = useTranslations('features')
|
|
10
11
|
|
|
11
12
|
return (
|
|
@@ -33,3 +34,5 @@ export default function AdvancedAnalyticsPage() {
|
|
|
33
34
|
</div>
|
|
34
35
|
)
|
|
35
36
|
}
|
|
37
|
+
|
|
38
|
+
export default getTemplateOrDefaultClient('app/dashboard/features/analytics/page.tsx', AdvancedAnalyticsPage)
|
|
@@ -4,8 +4,9 @@ import { FeatureGate } from '@nextsparkjs/core/components/billing/FeatureGate'
|
|
|
4
4
|
import { FeaturePlaceholder } from '@nextsparkjs/core/components/billing/FeaturePlaceholder'
|
|
5
5
|
import { Zap } from 'lucide-react'
|
|
6
6
|
import { useTranslations } from 'next-intl'
|
|
7
|
+
import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
function TaskAutomationPage() {
|
|
9
10
|
const t = useTranslations('features')
|
|
10
11
|
|
|
11
12
|
return (
|
|
@@ -33,3 +34,5 @@ export default function TaskAutomationPage() {
|
|
|
33
34
|
</div>
|
|
34
35
|
)
|
|
35
36
|
}
|
|
37
|
+
|
|
38
|
+
export default getTemplateOrDefaultClient('app/dashboard/features/automation/page.tsx', TaskAutomationPage)
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { ReactNode } from 'react'
|
|
1
|
+
import { type ReactNode } from 'react'
|
|
2
|
+
import { getTemplateOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
2
3
|
|
|
3
4
|
interface FeaturesLayoutProps {
|
|
4
5
|
children: ReactNode
|
|
5
6
|
}
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
function FeaturesLayout({ children }: FeaturesLayoutProps) {
|
|
8
9
|
return (
|
|
9
10
|
<div className="container py-8" data-cy="features-layout">
|
|
10
11
|
{children}
|
|
11
12
|
</div>
|
|
12
13
|
)
|
|
13
14
|
}
|
|
15
|
+
|
|
16
|
+
export default getTemplateOrDefault('app/dashboard/features/layout.tsx', FeaturesLayout)
|