@digilogiclabs/create-saas-app 2.7.1 → 2.7.2
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/.tsbuildinfo +1 -1
- package/dist/generators/template-generator.d.ts.map +1 -1
- package/dist/generators/template-generator.js +8 -4
- package/dist/generators/template-generator.js.map +1 -1
- package/dist/templates/shared/auth/firebase/web/src/lib/auth-session.ts +25 -0
- package/dist/templates/shared/auth/keycloak/web/src/lib/auth-session.ts +24 -0
- package/dist/templates/shared/auth/supabase/web/src/lib/auth-session.ts +37 -0
- package/dist/templates/shared/contact/web/src/app/api/contact/route.ts +4 -4
- package/{src/templates/shared/database/supabase/web → dist/templates/shared/database/supabase/web/src}/lib/supabase/server.ts +1 -1
- package/dist/templates/shared/payments/web/src/app/api/webhooks/stripe/route.ts +1 -1
- package/{src/templates/shared/security/web → dist/templates/shared/security/web/src}/lib/api-security.ts +7 -3
- package/dist/templates/web/ai-platform/template/middleware.ts +55 -55
- package/dist/templates/web/ai-platform/template/src/lib/supabase/server.ts +27 -27
- package/dist/templates/web/iot-dashboard/template/middleware.ts +56 -56
- package/dist/templates/web/iot-dashboard/template/src/lib/supabase/server.ts +27 -27
- package/dist/templates/web/marketplace/template/middleware.ts +56 -56
- package/dist/templates/web/marketplace/template/src/lib/supabase/server.ts +27 -27
- package/dist/templates/web/micro-saas/template/middleware.ts +1 -1
- package/dist/templates/web/micro-saas/template/src/lib/supabase/server.ts +29 -29
- package/dist/templates/web/ui-auth-ai/template/package.json +1 -3
- package/package.json +1 -1
- package/src/templates/shared/auth/firebase/web/src/lib/auth-session.ts +25 -0
- package/src/templates/shared/auth/keycloak/web/src/lib/auth-session.ts +24 -0
- package/src/templates/shared/auth/supabase/web/src/lib/auth-session.ts +37 -0
- package/src/templates/shared/contact/web/src/app/api/contact/route.ts +4 -4
- package/{dist/templates/shared/database/supabase/web → src/templates/shared/database/supabase/web/src}/lib/supabase/server.ts +1 -1
- package/src/templates/shared/payments/web/src/app/api/webhooks/stripe/route.ts +1 -1
- package/{dist/templates/shared/security/web → src/templates/shared/security/web/src}/lib/api-security.ts +7 -3
- package/src/templates/web/ai-platform/template/middleware.ts +55 -55
- package/src/templates/web/ai-platform/template/src/lib/supabase/server.ts +27 -27
- package/src/templates/web/iot-dashboard/template/middleware.ts +56 -56
- package/src/templates/web/iot-dashboard/template/src/lib/supabase/server.ts +27 -27
- package/src/templates/web/marketplace/template/middleware.ts +56 -56
- package/src/templates/web/marketplace/template/src/lib/supabase/server.ts +27 -27
- package/src/templates/web/micro-saas/template/middleware.ts +1 -1
- package/src/templates/web/micro-saas/template/src/lib/supabase/server.ts +29 -29
- package/src/templates/web/ui-auth-ai/template/package.json +1 -3
- /package/dist/templates/shared/audit/web/{lib → src/lib}/audit.ts +0 -0
- /package/dist/templates/shared/beta/web/{lib → src/lib}/beta/settings.ts +0 -0
- /package/dist/templates/shared/cache/web/{lib → src/lib}/cache.ts +0 -0
- /package/dist/templates/shared/config/web/{lib → src/lib}/config.ts +0 -0
- /package/dist/templates/shared/database/postgresql/web/{lib → src/lib}/db/drizzle.ts +0 -0
- /package/dist/templates/shared/database/postgresql/web/{lib → src/lib}/db/schema.ts +0 -0
- /package/dist/templates/shared/database/supabase/web/{lib → src/lib}/supabase/client.ts +0 -0
- /package/dist/templates/shared/database/supabase/web/{lib → src/lib}/supabase/service.ts +0 -0
- /package/dist/templates/shared/email/web/{lib → src/lib}/email/branding.ts +0 -0
- /package/dist/templates/shared/email/web/{lib → src/lib}/email/client.ts +0 -0
- /package/dist/templates/shared/legal/web/{lib → src/lib}/legal-config.ts +0 -0
- /package/dist/templates/shared/observability/web/{lib → src/lib}/observability.ts +0 -0
- /package/dist/templates/shared/platform/web/{lib → src/lib}/platform.ts +0 -0
- /package/dist/templates/shared/redis/web/{lib → src/lib}/rate-limit-store.ts +0 -0
- /package/dist/templates/shared/redis/web/{lib → src/lib}/redis.ts +0 -0
- /package/dist/templates/shared/utils/web/{lib → src/lib}/api-response.ts +0 -0
- /package/dist/templates/shared/utils/web/{lib → src/lib}/utils.ts +0 -0
- /package/src/templates/shared/audit/web/{lib → src/lib}/audit.ts +0 -0
- /package/src/templates/shared/beta/web/{lib → src/lib}/beta/settings.ts +0 -0
- /package/src/templates/shared/cache/web/{lib → src/lib}/cache.ts +0 -0
- /package/src/templates/shared/config/web/{lib → src/lib}/config.ts +0 -0
- /package/src/templates/shared/database/postgresql/web/{lib → src/lib}/db/drizzle.ts +0 -0
- /package/src/templates/shared/database/postgresql/web/{lib → src/lib}/db/schema.ts +0 -0
- /package/src/templates/shared/database/supabase/web/{lib → src/lib}/supabase/client.ts +0 -0
- /package/src/templates/shared/database/supabase/web/{lib → src/lib}/supabase/service.ts +0 -0
- /package/src/templates/shared/email/web/{lib → src/lib}/email/branding.ts +0 -0
- /package/src/templates/shared/email/web/{lib → src/lib}/email/client.ts +0 -0
- /package/src/templates/shared/legal/web/{lib → src/lib}/legal-config.ts +0 -0
- /package/src/templates/shared/observability/web/{lib → src/lib}/observability.ts +0 -0
- /package/src/templates/shared/platform/web/{lib → src/lib}/platform.ts +0 -0
- /package/src/templates/shared/redis/web/{lib → src/lib}/rate-limit-store.ts +0 -0
- /package/src/templates/shared/redis/web/{lib → src/lib}/redis.ts +0 -0
- /package/src/templates/shared/utils/web/{lib → src/lib}/api-response.ts +0 -0
- /package/src/templates/shared/utils/web/{lib → src/lib}/utils.ts +0 -0
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { createServerClient } from '@supabase/ssr'
|
|
2
|
-
import { cookies } from 'next/headers'
|
|
3
|
-
|
|
4
|
-
export async function createClient() {
|
|
5
|
-
const cookieStore = await cookies()
|
|
6
|
-
|
|
7
|
-
return createServerClient(
|
|
8
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
-
{
|
|
11
|
-
cookies: {
|
|
12
|
-
getAll() {
|
|
13
|
-
return cookieStore.getAll()
|
|
14
|
-
},
|
|
15
|
-
setAll(cookiesToSet) {
|
|
16
|
-
try {
|
|
17
|
-
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
-
cookieStore.set(name, value, options)
|
|
19
|
-
)
|
|
20
|
-
} catch {
|
|
21
|
-
// Server Component context
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
)
|
|
27
|
-
}
|
|
1
|
+
import { createServerClient } from '@supabase/ssr'
|
|
2
|
+
import { cookies } from 'next/headers'
|
|
3
|
+
|
|
4
|
+
export async function createClient() {
|
|
5
|
+
const cookieStore = await cookies()
|
|
6
|
+
|
|
7
|
+
return createServerClient(
|
|
8
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
+
{
|
|
11
|
+
cookies: {
|
|
12
|
+
getAll() {
|
|
13
|
+
return cookieStore.getAll()
|
|
14
|
+
},
|
|
15
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
16
|
+
try {
|
|
17
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
+
cookieStore.set(name, value, options)
|
|
19
|
+
)
|
|
20
|
+
} catch {
|
|
21
|
+
// Server Component context
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { createServerClient } from '@supabase/ssr'
|
|
2
|
-
import { NextResponse, type NextRequest } from 'next/server'
|
|
3
|
-
|
|
4
|
-
export async function middleware(request: NextRequest) {
|
|
5
|
-
let supabaseResponse = NextResponse.next({
|
|
6
|
-
request,
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
const supabase = createServerClient(
|
|
10
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
11
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
12
|
-
{
|
|
13
|
-
cookies: {
|
|
14
|
-
getAll() {
|
|
15
|
-
return request.cookies.getAll()
|
|
16
|
-
},
|
|
17
|
-
setAll(cookiesToSet) {
|
|
18
|
-
cookiesToSet.forEach(({ name, value }) =>
|
|
19
|
-
request.cookies.set(name, value)
|
|
20
|
-
)
|
|
21
|
-
supabaseResponse = NextResponse.next({
|
|
22
|
-
request,
|
|
23
|
-
})
|
|
24
|
-
cookiesToSet.forEach(({ name, value, options }) =>
|
|
25
|
-
supabaseResponse.cookies.set(name, value, options)
|
|
26
|
-
)
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
const {
|
|
33
|
-
data: { user },
|
|
34
|
-
} = await supabase.auth.getUser()
|
|
35
|
-
|
|
36
|
-
// Redirect to login if accessing protected route without auth
|
|
37
|
-
if (
|
|
38
|
-
!user &&
|
|
39
|
-
(request.nextUrl.pathname.startsWith('/dashboard') ||
|
|
40
|
-
request.nextUrl.pathname.startsWith('/products/new') ||
|
|
41
|
-
request.nextUrl.pathname.startsWith('/orders') ||
|
|
42
|
-
request.nextUrl.pathname.startsWith('/payouts'))
|
|
43
|
-
) {
|
|
44
|
-
const url = request.nextUrl.clone()
|
|
45
|
-
url.pathname = '/login'
|
|
46
|
-
return NextResponse.redirect(url)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return supabaseResponse
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const config = {
|
|
53
|
-
matcher: [
|
|
54
|
-
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
55
|
-
],
|
|
56
|
-
}
|
|
1
|
+
import { createServerClient } from '@supabase/ssr'
|
|
2
|
+
import { NextResponse, type NextRequest } from 'next/server'
|
|
3
|
+
|
|
4
|
+
export async function middleware(request: NextRequest) {
|
|
5
|
+
let supabaseResponse = NextResponse.next({
|
|
6
|
+
request,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const supabase = createServerClient(
|
|
10
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
11
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
12
|
+
{
|
|
13
|
+
cookies: {
|
|
14
|
+
getAll() {
|
|
15
|
+
return request.cookies.getAll()
|
|
16
|
+
},
|
|
17
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
18
|
+
cookiesToSet.forEach(({ name, value }) =>
|
|
19
|
+
request.cookies.set(name, value)
|
|
20
|
+
)
|
|
21
|
+
supabaseResponse = NextResponse.next({
|
|
22
|
+
request,
|
|
23
|
+
})
|
|
24
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
25
|
+
supabaseResponse.cookies.set(name, value, options)
|
|
26
|
+
)
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
data: { user },
|
|
34
|
+
} = await supabase.auth.getUser()
|
|
35
|
+
|
|
36
|
+
// Redirect to login if accessing protected route without auth
|
|
37
|
+
if (
|
|
38
|
+
!user &&
|
|
39
|
+
(request.nextUrl.pathname.startsWith('/dashboard') ||
|
|
40
|
+
request.nextUrl.pathname.startsWith('/products/new') ||
|
|
41
|
+
request.nextUrl.pathname.startsWith('/orders') ||
|
|
42
|
+
request.nextUrl.pathname.startsWith('/payouts'))
|
|
43
|
+
) {
|
|
44
|
+
const url = request.nextUrl.clone()
|
|
45
|
+
url.pathname = '/login'
|
|
46
|
+
return NextResponse.redirect(url)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return supabaseResponse
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const config = {
|
|
53
|
+
matcher: [
|
|
54
|
+
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
55
|
+
],
|
|
56
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { createServerClient } from '@supabase/ssr'
|
|
2
|
-
import { cookies } from 'next/headers'
|
|
3
|
-
|
|
4
|
-
export async function createClient() {
|
|
5
|
-
const cookieStore = await cookies()
|
|
6
|
-
|
|
7
|
-
return createServerClient(
|
|
8
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
-
{
|
|
11
|
-
cookies: {
|
|
12
|
-
getAll() {
|
|
13
|
-
return cookieStore.getAll()
|
|
14
|
-
},
|
|
15
|
-
setAll(cookiesToSet) {
|
|
16
|
-
try {
|
|
17
|
-
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
-
cookieStore.set(name, value, options)
|
|
19
|
-
)
|
|
20
|
-
} catch {
|
|
21
|
-
// Server Component context
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
)
|
|
27
|
-
}
|
|
1
|
+
import { createServerClient } from '@supabase/ssr'
|
|
2
|
+
import { cookies } from 'next/headers'
|
|
3
|
+
|
|
4
|
+
export async function createClient() {
|
|
5
|
+
const cookieStore = await cookies()
|
|
6
|
+
|
|
7
|
+
return createServerClient(
|
|
8
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
+
{
|
|
11
|
+
cookies: {
|
|
12
|
+
getAll() {
|
|
13
|
+
return cookieStore.getAll()
|
|
14
|
+
},
|
|
15
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
16
|
+
try {
|
|
17
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
+
cookieStore.set(name, value, options)
|
|
19
|
+
)
|
|
20
|
+
} catch {
|
|
21
|
+
// Server Component context
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -14,7 +14,7 @@ export async function middleware(request: NextRequest) {
|
|
|
14
14
|
getAll() {
|
|
15
15
|
return request.cookies.getAll()
|
|
16
16
|
},
|
|
17
|
-
setAll(cookiesToSet) {
|
|
17
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
18
18
|
cookiesToSet.forEach(({ name, value }) =>
|
|
19
19
|
request.cookies.set(name, value)
|
|
20
20
|
)
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { createServerClient } from '@supabase/ssr'
|
|
2
|
-
import { cookies } from 'next/headers'
|
|
3
|
-
|
|
4
|
-
export async function createClient() {
|
|
5
|
-
const cookieStore = await cookies()
|
|
6
|
-
|
|
7
|
-
return createServerClient(
|
|
8
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
-
{
|
|
11
|
-
cookies: {
|
|
12
|
-
getAll() {
|
|
13
|
-
return cookieStore.getAll()
|
|
14
|
-
},
|
|
15
|
-
setAll(cookiesToSet) {
|
|
16
|
-
try {
|
|
17
|
-
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
-
cookieStore.set(name, value, options)
|
|
19
|
-
)
|
|
20
|
-
} catch {
|
|
21
|
-
// The `setAll` method was called from a Server Component.
|
|
22
|
-
// This can be ignored if you have middleware refreshing
|
|
23
|
-
// user sessions.
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
)
|
|
29
|
-
}
|
|
1
|
+
import { createServerClient } from '@supabase/ssr'
|
|
2
|
+
import { cookies } from 'next/headers'
|
|
3
|
+
|
|
4
|
+
export async function createClient() {
|
|
5
|
+
const cookieStore = await cookies()
|
|
6
|
+
|
|
7
|
+
return createServerClient(
|
|
8
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
+
{
|
|
11
|
+
cookies: {
|
|
12
|
+
getAll() {
|
|
13
|
+
return cookieStore.getAll()
|
|
14
|
+
},
|
|
15
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
16
|
+
try {
|
|
17
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
+
cookieStore.set(name, value, options)
|
|
19
|
+
)
|
|
20
|
+
} catch {
|
|
21
|
+
// The `setAll` method was called from a Server Component.
|
|
22
|
+
// This can be ignored if you have middleware refreshing
|
|
23
|
+
// user sessions.
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
"clsx": "^2.0.0",
|
|
19
19
|
"tailwindcss": "^3.3.0",
|
|
20
20
|
"autoprefixer": "^10.4.16",
|
|
21
|
-
"postcss": "^8.4.31"
|
|
22
|
-
"@digilogiclabs/saas-factory-ai": "^4.0.2",
|
|
23
|
-
"@digilogiclabs/saas-factory-ai-types": "^4.0.2"{{/ai.enabled}}
|
|
21
|
+
"postcss": "^8.4.31"
|
|
24
22
|
},
|
|
25
23
|
"devDependencies": {
|
|
26
24
|
"@types/node": "^20.0.0",
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth session adapter for Firebase.
|
|
3
|
+
* Provides a consistent getSession() interface for api-security.ts.
|
|
4
|
+
*/
|
|
5
|
+
import 'server-only';
|
|
6
|
+
|
|
7
|
+
type Session = {
|
|
8
|
+
user?: {
|
|
9
|
+
id?: string;
|
|
10
|
+
email?: string | null;
|
|
11
|
+
name?: string | null;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export async function getSession(): Promise<Session | null> {
|
|
16
|
+
// Firebase server-side auth is typically handled via admin SDK
|
|
17
|
+
// or by verifying the ID token from cookies.
|
|
18
|
+
// Implement based on your Firebase setup.
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function getUser() {
|
|
23
|
+
const session = await getSession();
|
|
24
|
+
return session?.user ?? null;
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth session adapter for Keycloak (Auth.js).
|
|
3
|
+
* Provides a consistent getSession() interface for api-security.ts.
|
|
4
|
+
*/
|
|
5
|
+
import 'server-only';
|
|
6
|
+
|
|
7
|
+
type Session = {
|
|
8
|
+
user?: {
|
|
9
|
+
id?: string;
|
|
10
|
+
email?: string | null;
|
|
11
|
+
name?: string | null;
|
|
12
|
+
roles?: string[];
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function getSession(): Promise<Session | null> {
|
|
17
|
+
const { auth } = await import('@/auth');
|
|
18
|
+
return auth();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getUser() {
|
|
22
|
+
const session = await getSession();
|
|
23
|
+
return session?.user ?? null;
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth session adapter for Supabase.
|
|
3
|
+
* Provides a consistent getSession() interface for api-security.ts.
|
|
4
|
+
*/
|
|
5
|
+
import 'server-only';
|
|
6
|
+
import { createClient } from '@/lib/supabase/server';
|
|
7
|
+
|
|
8
|
+
type Session = {
|
|
9
|
+
user?: {
|
|
10
|
+
id?: string;
|
|
11
|
+
email?: string | null;
|
|
12
|
+
name?: string | null;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function getSession(): Promise<Session | null> {
|
|
17
|
+
const supabase = await createClient();
|
|
18
|
+
const {
|
|
19
|
+
data: { user },
|
|
20
|
+
error,
|
|
21
|
+
} = await supabase.auth.getUser();
|
|
22
|
+
|
|
23
|
+
if (error || !user) return null;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
user: {
|
|
27
|
+
id: user.id,
|
|
28
|
+
email: user.email ?? null,
|
|
29
|
+
name: user.user_metadata?.name ?? user.user_metadata?.full_name ?? null,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function getUser() {
|
|
35
|
+
const session = await getSession();
|
|
36
|
+
return session?.user ?? null;
|
|
37
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* POST — Submit a contact form message.
|
|
5
5
|
* Public endpoint with rate limiting. Sends email via Resend.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { NextResponse } from 'next/server';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { withPublicApi, escapeHtml } from '@/lib/api-security';
|
|
10
10
|
import { sendEmail } from '@/lib/email/client';
|
|
@@ -23,7 +23,7 @@ const ContactSchema = z.object({
|
|
|
23
23
|
name: z.string().min(2, 'Name must be at least 2 characters').max(100, 'Name is too long'),
|
|
24
24
|
email: z.string().email('Please enter a valid email address').max(255),
|
|
25
25
|
category: z.enum(['general', 'support', 'business', 'feedback', 'other'], {
|
|
26
|
-
|
|
26
|
+
error: 'Please select a category',
|
|
27
27
|
}),
|
|
28
28
|
message: z
|
|
29
29
|
.string()
|
|
@@ -39,9 +39,9 @@ const ContactSchema = z.object({
|
|
|
39
39
|
export const POST = withPublicApi(
|
|
40
40
|
{
|
|
41
41
|
operation: 'contact/post',
|
|
42
|
-
rateLimit:
|
|
42
|
+
rateLimit: 'publicRead',
|
|
43
43
|
},
|
|
44
|
-
async (request
|
|
44
|
+
async (request) => {
|
|
45
45
|
const body = await request.json();
|
|
46
46
|
const parsed = ContactSchema.safeParse(body);
|
|
47
47
|
|
|
@@ -16,7 +16,7 @@ export async function createClient() {
|
|
|
16
16
|
getAll() {
|
|
17
17
|
return cookieStore.getAll();
|
|
18
18
|
},
|
|
19
|
-
setAll(cookiesToSet) {
|
|
19
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
20
20
|
try {
|
|
21
21
|
cookiesToSet.forEach(({ name, value, options }) =>
|
|
22
22
|
cookieStore.set(name, value, options)
|
|
@@ -16,7 +16,7 @@ import { enforceRateLimit, AppRateLimits } from '@/lib/api-security';
|
|
|
16
16
|
export const dynamic = 'force-dynamic';
|
|
17
17
|
|
|
18
18
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
|
19
|
-
apiVersion: '
|
|
19
|
+
apiVersion: '2025-02-24.acacia',
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -114,10 +114,14 @@ export const {
|
|
|
114
114
|
AppRateLimitPreset
|
|
115
115
|
>({
|
|
116
116
|
getSession: async () => {
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
// Import the auth session adapter — see @/lib/auth-session.ts
|
|
118
|
+
const { getSession } = await import('@/lib/auth-session');
|
|
119
|
+
return getSession();
|
|
120
|
+
},
|
|
121
|
+
isAdmin: (session) => {
|
|
122
|
+
const roles = (session?.user as Record<string, unknown>)?.roles;
|
|
123
|
+
return Array.isArray(roles) && roles.includes('admin');
|
|
119
124
|
},
|
|
120
|
-
isAdmin: (session) => session?.user?.roles?.includes('admin') ?? false,
|
|
121
125
|
rateLimiter: {
|
|
122
126
|
enforce: enforcePreset,
|
|
123
127
|
publicDefault: 'publicRead',
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import { createServerClient } from '@supabase/ssr'
|
|
2
|
-
import { NextResponse, type NextRequest } from 'next/server'
|
|
3
|
-
|
|
4
|
-
export async function middleware(request: NextRequest) {
|
|
5
|
-
let supabaseResponse = NextResponse.next({
|
|
6
|
-
request,
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
const supabase = createServerClient(
|
|
10
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
11
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
12
|
-
{
|
|
13
|
-
cookies: {
|
|
14
|
-
getAll() {
|
|
15
|
-
return request.cookies.getAll()
|
|
16
|
-
},
|
|
17
|
-
setAll(cookiesToSet) {
|
|
18
|
-
cookiesToSet.forEach(({ name, value }) =>
|
|
19
|
-
request.cookies.set(name, value)
|
|
20
|
-
)
|
|
21
|
-
supabaseResponse = NextResponse.next({
|
|
22
|
-
request,
|
|
23
|
-
})
|
|
24
|
-
cookiesToSet.forEach(({ name, value, options }) =>
|
|
25
|
-
supabaseResponse.cookies.set(name, value, options)
|
|
26
|
-
)
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
const {
|
|
33
|
-
data: { user },
|
|
34
|
-
} = await supabase.auth.getUser()
|
|
35
|
-
|
|
36
|
-
// Redirect to login if accessing protected route without auth
|
|
37
|
-
if (
|
|
38
|
-
!user &&
|
|
39
|
-
(request.nextUrl.pathname.startsWith('/dashboard') ||
|
|
40
|
-
request.nextUrl.pathname.startsWith('/chat') ||
|
|
41
|
-
request.nextUrl.pathname.startsWith('/playground'))
|
|
42
|
-
) {
|
|
43
|
-
const url = request.nextUrl.clone()
|
|
44
|
-
url.pathname = '/login'
|
|
45
|
-
return NextResponse.redirect(url)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return supabaseResponse
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export const config = {
|
|
52
|
-
matcher: [
|
|
53
|
-
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
54
|
-
],
|
|
55
|
-
}
|
|
1
|
+
import { createServerClient } from '@supabase/ssr'
|
|
2
|
+
import { NextResponse, type NextRequest } from 'next/server'
|
|
3
|
+
|
|
4
|
+
export async function middleware(request: NextRequest) {
|
|
5
|
+
let supabaseResponse = NextResponse.next({
|
|
6
|
+
request,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const supabase = createServerClient(
|
|
10
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
11
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
12
|
+
{
|
|
13
|
+
cookies: {
|
|
14
|
+
getAll() {
|
|
15
|
+
return request.cookies.getAll()
|
|
16
|
+
},
|
|
17
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
18
|
+
cookiesToSet.forEach(({ name, value }) =>
|
|
19
|
+
request.cookies.set(name, value)
|
|
20
|
+
)
|
|
21
|
+
supabaseResponse = NextResponse.next({
|
|
22
|
+
request,
|
|
23
|
+
})
|
|
24
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
25
|
+
supabaseResponse.cookies.set(name, value, options)
|
|
26
|
+
)
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
data: { user },
|
|
34
|
+
} = await supabase.auth.getUser()
|
|
35
|
+
|
|
36
|
+
// Redirect to login if accessing protected route without auth
|
|
37
|
+
if (
|
|
38
|
+
!user &&
|
|
39
|
+
(request.nextUrl.pathname.startsWith('/dashboard') ||
|
|
40
|
+
request.nextUrl.pathname.startsWith('/chat') ||
|
|
41
|
+
request.nextUrl.pathname.startsWith('/playground'))
|
|
42
|
+
) {
|
|
43
|
+
const url = request.nextUrl.clone()
|
|
44
|
+
url.pathname = '/login'
|
|
45
|
+
return NextResponse.redirect(url)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return supabaseResponse
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const config = {
|
|
52
|
+
matcher: [
|
|
53
|
+
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
54
|
+
],
|
|
55
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { createServerClient } from '@supabase/ssr'
|
|
2
|
-
import { cookies } from 'next/headers'
|
|
3
|
-
|
|
4
|
-
export async function createClient() {
|
|
5
|
-
const cookieStore = await cookies()
|
|
6
|
-
|
|
7
|
-
return createServerClient(
|
|
8
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
-
{
|
|
11
|
-
cookies: {
|
|
12
|
-
getAll() {
|
|
13
|
-
return cookieStore.getAll()
|
|
14
|
-
},
|
|
15
|
-
setAll(cookiesToSet) {
|
|
16
|
-
try {
|
|
17
|
-
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
-
cookieStore.set(name, value, options)
|
|
19
|
-
)
|
|
20
|
-
} catch {
|
|
21
|
-
// Server Component context
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
)
|
|
27
|
-
}
|
|
1
|
+
import { createServerClient } from '@supabase/ssr'
|
|
2
|
+
import { cookies } from 'next/headers'
|
|
3
|
+
|
|
4
|
+
export async function createClient() {
|
|
5
|
+
const cookieStore = await cookies()
|
|
6
|
+
|
|
7
|
+
return createServerClient(
|
|
8
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
9
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
10
|
+
{
|
|
11
|
+
cookies: {
|
|
12
|
+
getAll() {
|
|
13
|
+
return cookieStore.getAll()
|
|
14
|
+
},
|
|
15
|
+
setAll(cookiesToSet: Array<{ name: string; value: string; options?: Record<string, unknown> }>) {
|
|
16
|
+
try {
|
|
17
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
18
|
+
cookieStore.set(name, value, options)
|
|
19
|
+
)
|
|
20
|
+
} catch {
|
|
21
|
+
// Server Component context
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
}
|