-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmiddleware.ts
More file actions
135 lines (122 loc) · 3.84 KB
/
middleware.ts
File metadata and controls
135 lines (122 loc) · 3.84 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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { NextResponse, type NextRequest } from "next/server";
import { jwtVerify } from "jose";
const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET);
const COOKIE_NAME = "livecaps_token";
// --- CORS config (preserved from original) ---
const corsOptions = {
allowedMethods: (process.env?.ALLOWED_METHODS || "").split(","),
allowedOrigins: (process.env?.ALLOWED_ORIGIN || "").split(","),
allowedHeaders: (process.env?.ALLOWED_HEADERS || "").split(","),
exposedHeaders: (process.env?.EXPOSED_HEADERS || "").split(","),
maxAge:
(process.env?.PREFLIGHT_MAX_AGE &&
parseInt(process.env?.PREFLIGHT_MAX_AGE)) ||
undefined,
credentials: process.env?.CREDENTIALS == "true",
};
function applyCors(request: NextRequest, response: NextResponse) {
const origin = request.headers.get("origin") ?? "";
if (
corsOptions.allowedOrigins.includes("*") ||
corsOptions.allowedOrigins.includes(origin)
) {
response.headers.set("Access-Control-Allow-Origin", origin);
}
response.headers.set(
"Access-Control-Allow-Credentials",
corsOptions.credentials.toString()
);
response.headers.set(
"Access-Control-Allow-Methods",
corsOptions.allowedMethods.join(",")
);
response.headers.set(
"Access-Control-Allow-Headers",
corsOptions.allowedHeaders.join(",")
);
response.headers.set(
"Access-Control-Expose-Headers",
corsOptions.exposedHeaders.join(",")
);
response.headers.set(
"Access-Control-Max-Age",
corsOptions.maxAge?.toString() ?? ""
);
}
async function isAuthenticated(request: NextRequest): Promise<boolean> {
const token = request.cookies.get(COOKIE_NAME)?.value;
if (!token) return false;
try {
await jwtVerify(token, JWT_SECRET);
return true;
} catch {
return false;
}
}
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// --- CORS for /api/authenticate (preserve existing behavior) ---
if (pathname === "/api/authenticate") {
const response = NextResponse.next();
applyCors(request, response);
return response;
}
// --- Public auth endpoints: just pass through ---
if (
pathname === "/api/auth/login" ||
pathname === "/api/auth/signup" ||
pathname === "/api/auth/logout"
) {
return NextResponse.next();
}
// --- Stripe webhook: public (verified by Stripe signature) ---
if (pathname === "/api/stripe/webhook") {
return NextResponse.next();
}
// --- Protected API routes: 401 if not authenticated ---
if (
pathname === "/api/auth/me" ||
pathname.startsWith("/api/usage/") ||
pathname.startsWith("/api/sessions") ||
pathname === "/api/auth/profile" ||
pathname === "/api/auth/upgrade" ||
pathname === "/api/stripe/checkout" ||
pathname === "/api/stripe/portal"
) {
const authed = await isAuthenticated(request);
if (!authed) {
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
}
return NextResponse.next();
}
// --- /login, /signup: redirect to /app if already authenticated ---
if (pathname === "/login" || pathname === "/signup") {
const authed = await isAuthenticated(request);
if (authed) {
return NextResponse.redirect(new URL("/app", request.url));
}
return NextResponse.next();
}
// --- /profile, /app/*: redirect to /login if not authenticated ---
if (pathname.startsWith("/app") || pathname.startsWith("/profile")) {
const authed = await isAuthenticated(request);
if (!authed) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
return NextResponse.next();
}
export const config = {
matcher: [
"/app/:path*",
"/profile/:path*",
"/login",
"/signup",
"/api/authenticate",
"/api/auth/:path*",
"/api/usage/:path*",
"/api/sessions/:path*",
"/api/stripe/:path*",
],
};