-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproxy.ts
More file actions
72 lines (57 loc) · 3.49 KB
/
proxy.ts
File metadata and controls
72 lines (57 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import { NextRequest, NextResponse } from 'next/server';
import { createMiddlewareSupabaseClient } from '@/utils/supabase/middleware';
const AUTH_ROUTES = {
RECOVERY_ROUTES: ['/reset-password', '/reset-password/complete'],
REQUIRE_AUTH: ['/collection/detail', '/bookmark/detail'],
BLOCK_IF_AUTH: ['/signin', '/signup', '/signup/confirm', '/reset-password', '/reset-password/complete'],
REQUIRE_PROFILE: ['/main', '/search', '/collection', '/bookmark', '/recommendation'],
};
export async function proxy(request: NextRequest) {
const { pathname, searchParams } = new URL(request.url);
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
const cspHeader = `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.kakao.com https://dapi.kakao.com https://t1.daumcdn.net https://*.vercel.live https://vercel.live https://*.vercel-scripts.com https://static.cloudflareinsights.com;
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data: https://*.kakao.com https://*.kakaocdn.net https://lh3.googleusercontent.com https://avatars.githubusercontent.com https://t1.daumcdn.net https://mts.daumcdn.net https://*.supabase.co;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://*.supabase.co https://*.kakao.com https://dapi.kakao.com https://*.vercel.live https://vercel.live https://cloudflareinsights.com https://*.cloudflareinsights.com;
object-src 'none';
base-uri 'self';
form-action 'self' https://*.kakao.com;
frame-ancestors 'none';
frame-src https://*.vercel.live https://vercel.live https://*.kakao.com;
upgrade-insecure-requests;`;
const contentSecurityPolicyHeaderValue = cspHeader.replace(/\s{2,}/g, ' ').trim();
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-nonce', nonce);
requestHeaders.set('Content-Security-Policy', contentSecurityPolicyHeaderValue);
const response = NextResponse.next({ request: { headers: requestHeaders } });
response.headers.set('Content-Security-Policy', contentSecurityPolicyHeaderValue);
const supabase = createMiddlewareSupabaseClient(request, response);
const { data: { user } } = await supabase.auth.getUser();
const isRecoveryFlow = AUTH_ROUTES.RECOVERY_ROUTES.some(route => pathname.startsWith(route)) || searchParams.get('type') === 'recovery';
if (isRecoveryFlow) return response;
const requiresAuth = AUTH_ROUTES.REQUIRE_AUTH.some(route => pathname.startsWith(route));
if (requiresAuth && !user) return NextResponse.redirect(new URL('/signin', request.url));
const blockIfAuth = AUTH_ROUTES.BLOCK_IF_AUTH.some(route => pathname.startsWith(route));
if (blockIfAuth && user) return NextResponse.redirect(new URL('/main', request.url));
const requiresProfile = AUTH_ROUTES.REQUIRE_PROFILE.some(route => pathname.startsWith(route));
const isOnboardingPage = pathname.startsWith('/onboarding');
if (requiresProfile && user && !isOnboardingPage) {
const { data: userData } = await supabase
.from('user')
.select('nickname, gender')
.eq('user_id', user.id)
.maybeSingle();
if (!userData?.nickname || !userData?.gender) {
return NextResponse.redirect(new URL('/onboarding/profile-setup', request.url));
}
}
return response;
}
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|woff|woff2|ttf|eot)$).*)',
],
};