-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmiddleware.ts
82 lines (69 loc) · 2.99 KB
/
middleware.ts
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
73
74
75
76
77
78
79
80
81
82
import { NextRequest } from 'next/server';
import createMiddleware from 'next-intl/middleware';
import { Role } from '@/apis/types/role';
import { isProd } from '@/constants/env';
import { routing } from '@/i18n/routing';
import { getUserState } from './actions/session';
import { BASE_URL, LOGIN_URL } from './constants/network';
const handleI18nRouting = createMiddleware(routing);
// TODO: 페이지별 권한관리가 개판이라서 정리 한 번 해줘야 함
const isAuthRequired = (pathname: string): Role | undefined => {
if (pathname.startsWith('/en')) pathname = pathname.slice(3);
if (pathname.startsWith('/admin') || pathname.endsWith('create')) return 'ROLE_STAFF';
if (pathname.endsWith('edit')) {
if (pathname.includes('council')) return 'ROLE_COUNCIL';
else return 'ROLE_STAFF';
}
};
const isCouncilRequired = (pathname: string) => {
if (pathname.startsWith('/en')) pathname = pathname.slice(3);
return (
pathname.includes('/council') && (pathname.endsWith('create') || pathname.endsWith('edit'))
);
};
export default async function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
const userState = await getUserState();
// 관리자 페이지는 스태프 계정으로 로그인되어있어야한다.
// 학생회 편집 페이지는 학생회 혹은 스태프 계정으로 로그인되어 있어야 한다.
const isValidState =
userState === 'ROLE_STAFF' ||
(isCouncilRequired(pathname) && userState === 'ROLE_COUNCIL') ||
!isAuthRequired(pathname);
if (!isValidState) {
if (isProd) {
return Response.redirect(new URL(LOGIN_URL));
} else {
return Response.redirect(new URL(BASE_URL));
}
}
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
const cspHeader = `
default-src 'self';
script-src 'self' https://t1.daumcdn.net https://dapi.kakao.com ${
isProd ? `'nonce-${nonce}' 'strict-dynamic'` : `'unsafe-inline' 'unsafe-eval'`
};
style-src 'self' 'nonce-${nonce}' https://cdn.jsdelivr.net https://fonts.googleapis.com;
img-src 'self' blob: data: https://t1.daumcdn.net https://map.daumcdn.net https://mts.daumcdn.net https://cse-dev-waffle.bacchus.io https://cse.snu.ac.kr;
font-src 'self' https://cdn.jsdelivr.net https://fonts.gstatic.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`
.replace(/\s{2,}/g, ' ')
.trim();
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-nonce', nonce);
requestHeaders.set('Content-Security-Policy', cspHeader);
const req = new NextRequest(request, { headers: requestHeaders });
const res = handleI18nRouting(req);
res.headers.set('Content-Security-Policy', cspHeader);
return res;
}
export const config = {
// Skip all paths that should not be internationalized. This example skips the
// folders "api", "_next" and all files with an extension (e.g. favicon.ico)
matcher: ['/((?!api|_next|.*\\..*).*)'],
};