-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmiddleware.ts
More file actions
105 lines (90 loc) · 3.24 KB
/
middleware.ts
File metadata and controls
105 lines (90 loc) · 3.24 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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { createServerClient } from "@supabase/ssr";
import { Database } from "@/lib/types";
export async function middleware(request: NextRequest) {
let response = NextResponse.next({
request: {
headers: request.headers,
},
});
// Legal-Seiten vollständig statisch - Auth-Overhead überspringen
const path = request.nextUrl.pathname;
if (path.startsWith("/legal")) {
return response;
}
const supabase = createServerClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
db: {
schema: "public",
},
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
// Keep request cookies in sync so downstream middleware/handlers see latest auth.
cookiesToSet.forEach(({ name, value }) => {
request.cookies.set({ name, value });
});
// Re-create the response once, then apply all cookie mutations.
response = NextResponse.next({
request: {
headers: request.headers,
},
});
cookiesToSet.forEach(({ name, value, options }) => {
response.cookies.set({ name, value, ...options });
});
},
},
}
);
// IMPORTANT: Use getUser() instead of getSession() in middleware.
// getUser() contacts the Supabase Auth server, validates the JWT, and
// refreshes the session if the access token has expired.
// Wrap in try/catch: stale/invalid refresh tokens should not crash the middleware.
let user = null;
try {
const { data } = await supabase.auth.getUser();
user = data.user;
} catch {
// Invalid or missing refresh token — treat as unauthenticated.
// The browser will get fresh tokens on next login.
}
const isProtectedPath =
path.startsWith("/admin") ||
path.startsWith("/staff") ||
path.startsWith("/analytics") ||
path.startsWith("/moderation");
if (isProtectedPath) {
if (!user) {
return NextResponse.redirect(new URL("/", request.url));
}
// Fetch user system roles
const { data: rolesData } = await supabase
.from("user_system_roles")
.select("role:system_roles(name)")
.eq("user_id", user.id);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const userRoles = (rolesData || []).map((r: any) => r.role?.name).filter(Boolean);
let hasAccess = false;
const isStaff = userRoles.includes("admin") || userRoles.includes("moderator") || userRoles.includes("analyst");
if (path.startsWith("/admin") || path.startsWith("/staff")) {
hasAccess = isStaff;
} else if (path.startsWith("/analytics")) {
hasAccess = userRoles.includes("admin") || userRoles.includes("analyst");
} else if (path.startsWith("/moderation")) {
hasAccess = userRoles.includes("admin") || userRoles.includes("moderator");
}
if (!hasAccess) {
return NextResponse.redirect(new URL("/app-home", request.url));
}
}
return response;
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)).*)"],
};