@nextsparkjs/core 0.1.0-beta.56 → 0.1.0-beta.58
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/api/csp-report/route.ts +6 -75
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +2 -2
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +2 -2
- package/dist/templates/app/api/v1/billing/cancel/route.ts +5 -2
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +5 -2
- package/dist/templates/app/api/v1/billing/check-action/route.ts +5 -2
- package/dist/templates/app/api/v1/billing/checkout/route.ts +5 -2
- package/dist/templates/app/api/v1/billing/portal/route.ts +5 -2
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +4 -4
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +2 -2
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +2 -2
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +4 -4
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +4 -4
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +6 -6
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +2 -2
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +4 -4
- package/dist/templates/app/api/v1/teams/route.ts +4 -4
- package/dist/templates/app/api/v1/teams/switch/route.ts +2 -2
- package/dist/templates/app/dashboard/(main)/patterns/page.tsx +1 -1
- package/dist/templates/app/globals.css +8 -5
- package/dist/templates/contents/themes/starter/config/features.config.ts +156 -0
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/patterns.cy.ts +19 -19
- package/package.json +1 -1
- package/scripts/build/registry.mjs +7 -0
- package/scripts/build/theme.mjs +18 -14
- package/templates/app/api/csp-report/route.ts +6 -75
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +2 -2
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +2 -2
- package/templates/app/api/v1/billing/cancel/route.ts +5 -2
- package/templates/app/api/v1/billing/change-plan/route.ts +5 -2
- package/templates/app/api/v1/billing/check-action/route.ts +5 -2
- package/templates/app/api/v1/billing/checkout/route.ts +5 -2
- package/templates/app/api/v1/billing/portal/route.ts +5 -2
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +4 -4
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +2 -2
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +2 -2
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +4 -4
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +4 -4
- package/templates/app/api/v1/teams/[teamId]/route.ts +6 -6
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +2 -2
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +4 -4
- package/templates/app/api/v1/teams/route.ts +4 -4
- package/templates/app/api/v1/teams/switch/route.ts +2 -2
- package/templates/app/dashboard/(main)/patterns/page.tsx +1 -1
- package/templates/app/globals.css +8 -5
- package/templates/contents/themes/starter/config/features.config.ts +156 -0
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/patterns.cy.ts +19 -19
package/dist/styles/classes.json
CHANGED
|
@@ -1,34 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
|
-
// Dynamic import for rate limiting - graceful fallback if not available
|
|
5
|
-
let checkDistributedRateLimit: ((id: string, tier: string) => Promise<{ allowed: boolean; limit: number; remaining: number; resetTime: number; retryAfter?: number }>) | null = null;
|
|
6
|
-
let createRateLimitErrorResponse: ((result: { allowed: boolean; limit: number; remaining: number; resetTime: number; retryAfter?: number }) => NextResponse) | null = null;
|
|
7
|
-
|
|
8
|
-
// Try to load rate limiting functions
|
|
9
|
-
try {
|
|
10
|
-
const rateLimitModule = require('@nextsparkjs/core/lib/api');
|
|
11
|
-
checkDistributedRateLimit = rateLimitModule.checkDistributedRateLimit;
|
|
12
|
-
createRateLimitErrorResponse = rateLimitModule.createRateLimitErrorResponse;
|
|
13
|
-
} catch {
|
|
14
|
-
// Rate limiting not available - will skip rate limit checks
|
|
15
|
-
console.warn('[CSP Report] Rate limiting not available - running without rate limits');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
4
|
/**
|
|
19
5
|
* CSP Violation Report Endpoint
|
|
20
6
|
*
|
|
21
7
|
* Receives Content-Security-Policy violation reports from browsers.
|
|
22
8
|
* Reports are logged for monitoring and debugging CSP issues.
|
|
23
9
|
*
|
|
24
|
-
* Rate limiting: Uses 'api' tier (100 requests/minute per IP) to prevent abuse.
|
|
25
|
-
* Falls back gracefully if rate limiting is not available.
|
|
26
|
-
*
|
|
27
|
-
* NOTE: This file exists in both apps/dev/app/api/csp-report/ and
|
|
28
|
-
* packages/core/templates/app/api/csp-report/. The template version
|
|
29
|
-
* is used when creating new projects from the core package.
|
|
30
|
-
* Changes should be synchronized between both files.
|
|
31
|
-
*
|
|
32
10
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#violation_report_syntax
|
|
33
11
|
*/
|
|
34
12
|
|
|
@@ -54,52 +32,8 @@ const getAllowedOrigin = () => {
|
|
|
54
32
|
return process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
|
|
55
33
|
};
|
|
56
34
|
|
|
57
|
-
/**
|
|
58
|
-
* Get client IP address from request headers.
|
|
59
|
-
* Handles various proxy scenarios (X-Forwarded-For, X-Real-IP, etc.)
|
|
60
|
-
*/
|
|
61
|
-
function getClientIp(request: NextRequest): string {
|
|
62
|
-
const forwardedFor = request.headers.get('x-forwarded-for');
|
|
63
|
-
if (forwardedFor) {
|
|
64
|
-
const ips = forwardedFor.split(',').map(ip => ip.trim());
|
|
65
|
-
if (ips[0]) return ips[0];
|
|
66
|
-
}
|
|
67
|
-
const realIp = request.headers.get('x-real-ip');
|
|
68
|
-
if (realIp) return realIp;
|
|
69
|
-
const cfIp = request.headers.get('cf-connecting-ip');
|
|
70
|
-
if (cfIp) return cfIp;
|
|
71
|
-
return 'unknown';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
35
|
export async function POST(request: NextRequest) {
|
|
75
36
|
const requestId = randomUUID().slice(0, 8);
|
|
76
|
-
let rateLimitHeaders: Record<string, string> = {};
|
|
77
|
-
|
|
78
|
-
// Rate limiting: 100 requests per minute per IP (api tier)
|
|
79
|
-
// Skip if rate limiting is not available
|
|
80
|
-
if (checkDistributedRateLimit && createRateLimitErrorResponse) {
|
|
81
|
-
try {
|
|
82
|
-
const clientIp = getClientIp(request);
|
|
83
|
-
const rateLimitResult = await checkDistributedRateLimit(`csp-report:ip:${clientIp}`, 'api');
|
|
84
|
-
|
|
85
|
-
if (!rateLimitResult.allowed) {
|
|
86
|
-
console.warn(`[CSP Report ${requestId}] Rate limit exceeded for IP: ${clientIp}`);
|
|
87
|
-
return createRateLimitErrorResponse(rateLimitResult);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Store rate limit headers to include in response
|
|
91
|
-
rateLimitHeaders = {
|
|
92
|
-
'X-RateLimit-Limit': rateLimitResult.limit.toString(),
|
|
93
|
-
'X-RateLimit-Remaining': rateLimitResult.remaining.toString(),
|
|
94
|
-
'X-RateLimit-Reset': rateLimitResult.resetTime.toString(),
|
|
95
|
-
};
|
|
96
|
-
} catch (rateLimitError) {
|
|
97
|
-
// Log but continue without rate limiting
|
|
98
|
-
console.warn(`[CSP Report ${requestId}] Rate limit check failed, continuing without:`, {
|
|
99
|
-
error: rateLimitError instanceof Error ? rateLimitError.message : 'Unknown error',
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
37
|
|
|
104
38
|
try {
|
|
105
39
|
const contentType = request.headers.get('content-type') || '';
|
|
@@ -107,7 +41,7 @@ export async function POST(request: NextRequest) {
|
|
|
107
41
|
// CSP reports are sent as application/csp-report or application/json
|
|
108
42
|
if (!contentType.includes('application/csp-report') && !contentType.includes('application/json')) {
|
|
109
43
|
console.warn(`[CSP Report ${requestId}] Invalid content-type: ${contentType}`);
|
|
110
|
-
return new
|
|
44
|
+
return new NextResponse('Invalid content type', { status: 400 });
|
|
111
45
|
}
|
|
112
46
|
|
|
113
47
|
let rawBody: string | undefined;
|
|
@@ -120,7 +54,7 @@ export async function POST(request: NextRequest) {
|
|
|
120
54
|
console.warn(`[CSP Report ${requestId}] Invalid report format - missing csp-report key`, {
|
|
121
55
|
bodyPreview: rawBody.slice(0, 200),
|
|
122
56
|
});
|
|
123
|
-
return new
|
|
57
|
+
return new NextResponse('Invalid report format', { status: 400 });
|
|
124
58
|
}
|
|
125
59
|
|
|
126
60
|
// Log the violation for monitoring
|
|
@@ -139,17 +73,14 @@ export async function POST(request: NextRequest) {
|
|
|
139
73
|
});
|
|
140
74
|
|
|
141
75
|
// Return 204 No Content - browsers don't expect a response body
|
|
142
|
-
return new
|
|
143
|
-
status: 204,
|
|
144
|
-
headers: rateLimitHeaders,
|
|
145
|
-
});
|
|
76
|
+
return new NextResponse(null, { status: 204 });
|
|
146
77
|
} catch (parseError) {
|
|
147
78
|
console.error(`[CSP Report ${requestId}] JSON parse error:`, {
|
|
148
79
|
error: parseError instanceof Error ? parseError.message : 'Unknown error',
|
|
149
80
|
bodyPreview: rawBody?.slice(0, 200),
|
|
150
81
|
});
|
|
151
82
|
// Still return 204 to not break browser behavior
|
|
152
|
-
return new
|
|
83
|
+
return new NextResponse(null, { status: 204 });
|
|
153
84
|
}
|
|
154
85
|
} catch (error) {
|
|
155
86
|
console.error(`[CSP Report ${requestId}] Unexpected error:`, {
|
|
@@ -157,14 +88,14 @@ export async function POST(request: NextRequest) {
|
|
|
157
88
|
stack: error instanceof Error ? error.stack : undefined,
|
|
158
89
|
});
|
|
159
90
|
// Still return 204 to not break browser behavior
|
|
160
|
-
return new
|
|
91
|
+
return new NextResponse(null, { status: 204 });
|
|
161
92
|
}
|
|
162
93
|
}
|
|
163
94
|
|
|
164
95
|
// Handle preflight requests for CORS
|
|
165
96
|
// Restrict to same origin instead of allowing all origins
|
|
166
97
|
export async function OPTIONS() {
|
|
167
|
-
return new
|
|
98
|
+
return new NextResponse(null, {
|
|
168
99
|
status: 204,
|
|
169
100
|
headers: {
|
|
170
101
|
'Access-Control-Allow-Origin': getAllowedOrigin(),
|
|
@@ -25,7 +25,7 @@ interface RouteParams {
|
|
|
25
25
|
export const PUT = withRateLimitTier(async (
|
|
26
26
|
request: NextRequest,
|
|
27
27
|
{ params }: { params: Promise<RouteParams> }
|
|
28
|
-
) {
|
|
28
|
+
) => {
|
|
29
29
|
const { entity, id, childType, childId } = await params
|
|
30
30
|
|
|
31
31
|
try {
|
|
@@ -150,7 +150,7 @@ export const PUT = withRateLimitTier(async (
|
|
|
150
150
|
export const DELETE = withRateLimitTier(async (
|
|
151
151
|
request: NextRequest,
|
|
152
152
|
{ params }: { params: Promise<RouteParams> }
|
|
153
|
-
) {
|
|
153
|
+
) => {
|
|
154
154
|
try {
|
|
155
155
|
const { entity, id, childType, childId } = await params
|
|
156
156
|
|
|
@@ -25,7 +25,7 @@ interface RouteParams {
|
|
|
25
25
|
export const GET = withRateLimitTier(async (
|
|
26
26
|
request: NextRequest,
|
|
27
27
|
{ params }: { params: Promise<RouteParams> }
|
|
28
|
-
) {
|
|
28
|
+
) => {
|
|
29
29
|
const { entity, id, childType } = await params
|
|
30
30
|
|
|
31
31
|
try {
|
|
@@ -90,7 +90,7 @@ export const GET = withRateLimitTier(async (
|
|
|
90
90
|
export const POST = withRateLimitTier(async (
|
|
91
91
|
request: NextRequest,
|
|
92
92
|
{ params }: { params: Promise<RouteParams> }
|
|
93
|
-
) {
|
|
93
|
+
) => {
|
|
94
94
|
const { entity, id, childType } = await params
|
|
95
95
|
|
|
96
96
|
try {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { NextRequest, NextResponse } from 'next/server'
|
|
11
11
|
import { z } from 'zod'
|
|
12
|
-
import { authenticateRequest
|
|
12
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
13
13
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
14
14
|
import {
|
|
15
15
|
cancelSubscriptionAtPeriodEnd,
|
|
@@ -37,7 +37,10 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
37
37
|
const authResult = await authenticateRequest(request)
|
|
38
38
|
|
|
39
39
|
if (!authResult.success || !authResult.user) {
|
|
40
|
-
return
|
|
40
|
+
return NextResponse.json(
|
|
41
|
+
{ success: false, error: 'Unauthorized' },
|
|
42
|
+
{ status: 401 }
|
|
43
|
+
)
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
// 2. Get team context
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { NextRequest, NextResponse } from 'next/server'
|
|
11
11
|
import { z } from 'zod'
|
|
12
|
-
import { authenticateRequest
|
|
12
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
13
13
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
14
14
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
15
15
|
|
|
@@ -31,7 +31,10 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
31
31
|
const authResult = await authenticateRequest(request)
|
|
32
32
|
|
|
33
33
|
if (!authResult.success || !authResult.user) {
|
|
34
|
-
return
|
|
34
|
+
return NextResponse.json(
|
|
35
|
+
{ success: false, error: 'Unauthorized' },
|
|
36
|
+
{ status: 401 }
|
|
37
|
+
)
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
// 2. Get team context
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { NextRequest, NextResponse } from 'next/server'
|
|
13
|
-
import { authenticateRequest
|
|
13
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
14
14
|
import { MembershipService } from '@nextsparkjs/core/lib/services'
|
|
15
15
|
import { z } from 'zod'
|
|
16
16
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
@@ -25,7 +25,10 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
25
25
|
const authResult = await authenticateRequest(request)
|
|
26
26
|
|
|
27
27
|
if (!authResult.success || !authResult.user) {
|
|
28
|
-
return
|
|
28
|
+
return NextResponse.json(
|
|
29
|
+
{ success: false, error: 'Unauthorized' },
|
|
30
|
+
{ status: 401 }
|
|
31
|
+
)
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
// 2. Parse and validate request body
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { NextRequest, NextResponse } from 'next/server'
|
|
13
|
-
import { authenticateRequest
|
|
13
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
14
14
|
import { createCheckoutSession } from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
15
15
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
16
16
|
import { z } from 'zod'
|
|
@@ -26,7 +26,10 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
26
26
|
const authResult = await authenticateRequest(request)
|
|
27
27
|
|
|
28
28
|
if (!authResult.success || !authResult.user) {
|
|
29
|
-
return
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{ success: false, error: 'Unauthorized' },
|
|
31
|
+
{ status: 401 }
|
|
32
|
+
)
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
// 2. Parse and validate request body
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { NextRequest, NextResponse } from 'next/server'
|
|
11
|
-
import { authenticateRequest
|
|
11
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
12
12
|
import { createPortalSession } from '@nextsparkjs/core/lib/billing/gateways/stripe'
|
|
13
13
|
import { SubscriptionService, MembershipService } from '@nextsparkjs/core/lib/services'
|
|
14
14
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
@@ -18,7 +18,10 @@ export const POST = withRateLimitTier(async (request: NextRequest) => {
|
|
|
18
18
|
const authResult = await authenticateRequest(request)
|
|
19
19
|
|
|
20
20
|
if (!authResult.success || !authResult.user) {
|
|
21
|
-
return
|
|
21
|
+
return NextResponse.json(
|
|
22
|
+
{ success: false, error: 'Unauthorized' },
|
|
23
|
+
{ status: 401 }
|
|
24
|
+
)
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
// 2. Get team context
|
|
@@ -19,7 +19,7 @@ export async function OPTIONS() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// GET /api/v1/teams/:teamId/invitations - List pending invitations for a team
|
|
22
|
-
export const GET = withRateLimitTier(
|
|
22
|
+
export const GET = withRateLimitTier(withApiLogging(
|
|
23
23
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
24
24
|
try {
|
|
25
25
|
// Authenticate using dual auth
|
|
@@ -99,10 +99,10 @@ export const GET = withRateLimitTier('read', withApiLogging(
|
|
|
99
99
|
return addCorsHeaders(response)
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
))
|
|
102
|
+
), 'read')
|
|
103
103
|
|
|
104
104
|
// DELETE /api/v1/teams/:teamId/invitations/:invitationId - Cancel/revoke an invitation
|
|
105
|
-
export const DELETE = withRateLimitTier(
|
|
105
|
+
export const DELETE = withRateLimitTier(withApiLogging(
|
|
106
106
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
107
107
|
try {
|
|
108
108
|
// Authenticate using dual auth
|
|
@@ -169,4 +169,4 @@ export const DELETE = withRateLimitTier('write', withApiLogging(
|
|
|
169
169
|
return addCorsHeaders(response)
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
-
))
|
|
172
|
+
), 'write')
|
|
@@ -18,7 +18,7 @@ export async function OPTIONS() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// GET /api/v1/teams/:teamId/invoices/:invoiceNumber - Get single invoice (owner only)
|
|
21
|
-
export const GET = withRateLimitTier(
|
|
21
|
+
export const GET = withRateLimitTier(withApiLogging(
|
|
22
22
|
async (
|
|
23
23
|
req: NextRequest,
|
|
24
24
|
{ params }: { params: Promise<{ teamId: string; invoiceNumber: string }> }
|
|
@@ -103,4 +103,4 @@ export const GET = withRateLimitTier('read', withApiLogging(
|
|
|
103
103
|
return addCorsHeaders(response)
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
))
|
|
106
|
+
), 'read')
|
|
@@ -20,7 +20,7 @@ export async function OPTIONS() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// GET /api/v1/teams/:teamId/invoices - List team invoices (owner only)
|
|
23
|
-
export const GET = withRateLimitTier(
|
|
23
|
+
export const GET = withRateLimitTier(withApiLogging(
|
|
24
24
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
25
25
|
try {
|
|
26
26
|
// Authenticate using dual auth (API key OR session)
|
|
@@ -123,4 +123,4 @@ export const GET = withRateLimitTier('read', withApiLogging(
|
|
|
123
123
|
return addCorsHeaders(response)
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
))
|
|
126
|
+
), 'read')
|
|
@@ -20,7 +20,7 @@ export async function OPTIONS() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// PATCH /api/v1/teams/:teamId/members/:memberId - Update member role
|
|
23
|
-
export const PATCH = withRateLimitTier(
|
|
23
|
+
export const PATCH = withRateLimitTier(withApiLogging(
|
|
24
24
|
async (
|
|
25
25
|
req: NextRequest,
|
|
26
26
|
{ params }: { params: Promise<{ teamId: string; memberId: string }> }
|
|
@@ -156,10 +156,10 @@ export const PATCH = withRateLimitTier('write', withApiLogging(
|
|
|
156
156
|
return addCorsHeaders(response)
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
-
))
|
|
159
|
+
), 'write')
|
|
160
160
|
|
|
161
161
|
// DELETE /api/v1/teams/:teamId/members/:memberId - Remove member from team
|
|
162
|
-
export const DELETE = withRateLimitTier(
|
|
162
|
+
export const DELETE = withRateLimitTier(withApiLogging(
|
|
163
163
|
async (
|
|
164
164
|
req: NextRequest,
|
|
165
165
|
{ params }: { params: Promise<{ teamId: string; memberId: string }> }
|
|
@@ -261,4 +261,4 @@ export const DELETE = withRateLimitTier('write', withApiLogging(
|
|
|
261
261
|
return addCorsHeaders(response)
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
|
-
))
|
|
264
|
+
), 'write')
|
|
@@ -37,7 +37,7 @@ export async function OPTIONS() {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// GET /api/v1/teams/:teamId/members - List team members
|
|
40
|
-
export const GET = withRateLimitTier(
|
|
40
|
+
export const GET = withRateLimitTier(withApiLogging(
|
|
41
41
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
42
42
|
try {
|
|
43
43
|
// Authenticate using dual auth
|
|
@@ -159,10 +159,10 @@ export const GET = withRateLimitTier('read', withApiLogging(
|
|
|
159
159
|
return addCorsHeaders(response)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
-
))
|
|
162
|
+
), 'read')
|
|
163
163
|
|
|
164
164
|
// POST /api/v1/teams/:teamId/members - Invite new member (creates invitation)
|
|
165
|
-
export const POST = withRateLimitTier(
|
|
165
|
+
export const POST = withRateLimitTier(withApiLogging(
|
|
166
166
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
167
167
|
try {
|
|
168
168
|
// Authenticate using dual auth
|
|
@@ -355,4 +355,4 @@ export const POST = withRateLimitTier('write', withApiLogging(
|
|
|
355
355
|
return addCorsHeaders(response)
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
|
-
))
|
|
358
|
+
), 'write')
|
|
@@ -19,7 +19,7 @@ export async function OPTIONS() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// GET /api/v1/teams/:teamId - Get team details
|
|
22
|
-
export const GET = withRateLimitTier(
|
|
22
|
+
export const GET = withRateLimitTier(withApiLogging(
|
|
23
23
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
24
24
|
try {
|
|
25
25
|
// Authenticate using dual auth
|
|
@@ -88,10 +88,10 @@ export const GET = withRateLimitTier('read', withApiLogging(
|
|
|
88
88
|
return addCorsHeaders(response)
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
))
|
|
91
|
+
), 'read')
|
|
92
92
|
|
|
93
93
|
// PATCH /api/v1/teams/:teamId - Update team (owners/admins only)
|
|
94
|
-
export const PATCH = withRateLimitTier(
|
|
94
|
+
export const PATCH = withRateLimitTier(withApiLogging(
|
|
95
95
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
96
96
|
try {
|
|
97
97
|
// Authenticate using dual auth
|
|
@@ -229,10 +229,10 @@ export const PATCH = withRateLimitTier('write', withApiLogging(
|
|
|
229
229
|
return addCorsHeaders(response)
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
|
-
))
|
|
232
|
+
), 'write')
|
|
233
233
|
|
|
234
234
|
// DELETE /api/v1/teams/:teamId - Delete team (owners only, NOT personal teams)
|
|
235
|
-
export const DELETE = withRateLimitTier(
|
|
235
|
+
export const DELETE = withRateLimitTier(withApiLogging(
|
|
236
236
|
async (req: NextRequest, { params }: { params: Promise<{ teamId: string }> }): Promise<NextResponse> => {
|
|
237
237
|
try {
|
|
238
238
|
// Authenticate using dual auth
|
|
@@ -292,4 +292,4 @@ export const DELETE = withRateLimitTier('write', withApiLogging(
|
|
|
292
292
|
return addCorsHeaders(response)
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
|
-
))
|
|
295
|
+
), 'write')
|
|
@@ -13,7 +13,7 @@ interface RouteParams {
|
|
|
13
13
|
params: Promise<{ teamId: string }>
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export const GET = withRateLimitTier(
|
|
16
|
+
export const GET = withRateLimitTier(async function GET(request: NextRequest, props: RouteParams) {
|
|
17
17
|
// Authenticate request
|
|
18
18
|
const { auth, rateLimitResponse } = await validateAndAuthenticateRequest(request)
|
|
19
19
|
if (rateLimitResponse) return rateLimitResponse
|
|
@@ -48,4 +48,4 @@ export const GET = withRateLimitTier('read', async function GET(request: NextReq
|
|
|
48
48
|
console.error('[Billing API] Error fetching subscription:', error)
|
|
49
49
|
return createApiError('Failed to fetch subscription', 500)
|
|
50
50
|
}
|
|
51
|
-
})
|
|
51
|
+
}, 'read')
|
|
@@ -15,7 +15,7 @@ interface RouteParams {
|
|
|
15
15
|
params: Promise<{ teamId: string; limitSlug: string }>
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export const GET = withRateLimitTier(
|
|
18
|
+
export const GET = withRateLimitTier(async function GET(request: NextRequest, props: RouteParams) {
|
|
19
19
|
// Authenticate request
|
|
20
20
|
const { auth, rateLimitResponse } = await validateAndAuthenticateRequest(request)
|
|
21
21
|
if (rateLimitResponse) return rateLimitResponse
|
|
@@ -45,9 +45,9 @@ export const GET = withRateLimitTier('read', async function GET(request: NextReq
|
|
|
45
45
|
console.error('[Billing API] Error checking quota:', error)
|
|
46
46
|
return createApiError('Failed to check quota', 500)
|
|
47
47
|
}
|
|
48
|
-
})
|
|
48
|
+
}, 'read')
|
|
49
49
|
|
|
50
|
-
export const POST = withRateLimitTier(
|
|
50
|
+
export const POST = withRateLimitTier(async function POST(request: NextRequest, props: RouteParams) {
|
|
51
51
|
// Authenticate request
|
|
52
52
|
const { auth, rateLimitResponse } = await validateAndAuthenticateRequest(request)
|
|
53
53
|
if (rateLimitResponse) return rateLimitResponse
|
|
@@ -89,4 +89,4 @@ export const POST = withRateLimitTier('write', async function POST(request: Next
|
|
|
89
89
|
const errorMessage = error instanceof Error ? error.message : 'Failed to track usage'
|
|
90
90
|
return createApiError(errorMessage, 500)
|
|
91
91
|
}
|
|
92
|
-
})
|
|
92
|
+
}, 'write')
|
|
@@ -22,7 +22,7 @@ export async function OPTIONS() {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// GET /api/v1/teams - List user's teams
|
|
25
|
-
export const GET = withRateLimitTier(
|
|
25
|
+
export const GET = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
26
26
|
try {
|
|
27
27
|
// Authenticate using dual auth
|
|
28
28
|
const authResult = await authenticateRequest(req)
|
|
@@ -161,10 +161,10 @@ export const GET = withRateLimitTier('read', withApiLogging(async (req: NextRequ
|
|
|
161
161
|
const response = createApiError('Internal server error', 500)
|
|
162
162
|
return addCorsHeaders(response)
|
|
163
163
|
}
|
|
164
|
-
}))
|
|
164
|
+
}), 'read')
|
|
165
165
|
|
|
166
166
|
// POST /api/v1/teams - Create new team
|
|
167
|
-
export const POST = withRateLimitTier(
|
|
167
|
+
export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
168
168
|
try {
|
|
169
169
|
// Authenticate using dual auth
|
|
170
170
|
const authResult = await authenticateRequest(req)
|
|
@@ -291,4 +291,4 @@ export const POST = withRateLimitTier('write', withApiLogging(async (req: NextRe
|
|
|
291
291
|
const response = createApiError('Internal server error', 500)
|
|
292
292
|
return addCorsHeaders(response)
|
|
293
293
|
}
|
|
294
|
-
}))
|
|
294
|
+
}), 'write')
|
|
@@ -21,7 +21,7 @@ export async function OPTIONS() {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// POST /api/v1/teams/switch - Switch active team context
|
|
24
|
-
export const POST = withRateLimitTier(
|
|
24
|
+
export const POST = withRateLimitTier(withApiLogging(async (req: NextRequest): Promise<NextResponse> => {
|
|
25
25
|
try {
|
|
26
26
|
// Authenticate using dual auth
|
|
27
27
|
const authResult = await authenticateRequest(req)
|
|
@@ -86,4 +86,4 @@ export const POST = withRateLimitTier('write', withApiLogging(async (req: NextRe
|
|
|
86
86
|
const response = createApiError('Internal server error', 500)
|
|
87
87
|
return addCorsHeaders(response)
|
|
88
88
|
}
|
|
89
|
-
}))
|
|
89
|
+
}), 'write')
|
|
@@ -42,7 +42,7 @@ export default function PatternsListPage() {
|
|
|
42
42
|
const router = useRouter()
|
|
43
43
|
|
|
44
44
|
// Use the new centralized hook for entity configuration
|
|
45
|
-
const { config: entityConfig, isLoading, error: configError
|
|
45
|
+
const { config: entityConfig, isLoading, error: configError } = useEntityConfig(entityType)
|
|
46
46
|
|
|
47
47
|
// Get current team for relation resolution
|
|
48
48
|
const { teamId } = useTeam()
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/* =============================================
|
|
2
2
|
GLOBAL STYLES - Import from Active Theme
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
This file imports styles from the active theme.
|
|
5
|
+
DO NOT edit directly - all styles are in:
|
|
6
6
|
contents/themes/{theme}/styles/globals.css
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
This import is AUTO-SYNCED by the registry build process
|
|
9
|
+
to match NEXT_PUBLIC_ACTIVE_THEME from your .env file.
|
|
10
|
+
|
|
11
|
+
To customize colors, typography and tokens, edit
|
|
12
|
+
the globals.css file in your active theme.
|
|
10
13
|
============================================= */
|
|
11
14
|
|
|
12
|
-
@import "../contents/themes/
|
|
15
|
+
@import "../contents/themes/starter/styles/globals.css";
|