fix middleware

This commit is contained in:
2025-02-05 15:07:18 +08:00
parent 331b5c9a84
commit 6dc4e7afc3
10 changed files with 713 additions and 203 deletions

View File

@@ -43,8 +43,6 @@ const middlewareConfig: MiddlewareConfig = {
// "/api/admin/voting/dashboard/*",
// "/api/admin/job/dashboard/*",
// "/api/admin/forum/dashboard/*",
// Akses awal
"/api/get-cookie",
@@ -78,135 +76,143 @@ export const middleware = async (req: NextRequest) => {
apiPath,
encodedKey,
loginPath,
// validasiPath,
// registarasiPath,
publicRoutes,
sessionKey,
validationApiRoute,
userPath,
} = middlewareConfig;
const { pathname } = req.nextUrl;
// CORS handling
// Handle CORS
const corsResponse = handleCors(req);
if (corsResponse) {
return setCorsHeaders(corsResponse);
return corsResponse;
}
// Skip authentication for public routes
const isPublicRoute = [
...publicRoutes,
loginPath,
// validasiPath,
// registarasiPath,
].some((route) => {
const pattern = route.replace(/\*/g, ".*");
return new RegExp(`^${pattern}$`).test(pathname);
});
// Always protect validation endpoint
if (pathname === validationApiRoute) {
const reqToken = req.headers.get("Authorization")?.split(" ")[1];
if (!reqToken) {
return setCorsHeaders(unauthorizedResponse());
}
}
if (
isPublicRoute &&
pathname !== loginPath
// &&
// pathname !== validasiPath &&
// pathname !== registarasiPath
) {
// Check if route is public
const isPublicRoute = isRoutePublic(pathname, publicRoutes, loginPath);
if (isPublicRoute && pathname !== loginPath) {
return setCorsHeaders(NextResponse.next());
}
const token =
req.cookies.get(sessionKey)?.value ||
req.headers.get("Authorization")?.split(" ")[1];
// Get token from cookies or Authorization header
const token = getToken(req, sessionKey);
// ==================== Authentication: Login, Validasi, Registrasi ==================== //
// Token verification
// Verify token and get user data
const user = await verifyToken({ token, encodedKey });
console.log("middlaware",user)
console.log("USER >>", user?.username);
// Handle login page access
if (pathname === loginPath) {
if (user) {
return setCorsHeaders(NextResponse.redirect(new URL(userPath, req.url)));
const response = NextResponse.redirect(new URL(userPath, req.url));
// Preserve token in cookie when redirecting
if (token) {
response.cookies.set(sessionKey, token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
});
}
return setCorsHeaders(response);
}
return setCorsHeaders(NextResponse.next());
}
// // Handle validation page access
// if (pathname === validasiPath) {
// if (user) {
// return setCorsHeaders(NextResponse.redirect(new URL(userPath, req.url)));
// }
// return setCorsHeaders(NextResponse.next());
// }
// // Handle register page access
// if (pathname === registarasiPath) {
// if (user) {
// return setCorsHeaders(NextResponse.redirect(new URL(userPath, req.url)));
// }
// return setCorsHeaders(NextResponse.next());
// }
// Handle protected routes
// Redirect to login if no user found
if (!user) {
return setCorsHeaders(NextResponse.redirect(new URL(loginPath, req.url)));
const response = NextResponse.redirect(new URL(loginPath, req.url));
// Clear invalid token
response.cookies.delete(sessionKey);
return setCorsHeaders(response);
}
// ==================== Authentication: Login, Validasi, Registrasi ==================== //
// Handle /dev routes that require active status
if (pathname.startsWith("/dev")) {
const userValidate = await fetch(new URL("/api/user-validate", req.url), {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
const userValidateJson = await userValidate.json();
if (!userValidateJson.data.active) {
return setCorsHeaders(
NextResponse.redirect(new URL("/waiting-room", req.url))
);
}
}
// Handle authenticated API requests
if (pathname.startsWith(apiPath)) {
const reqToken = req.headers.get("Authorization")?.split(" ")[1];
if (!reqToken) {
return setCorsHeaders(unauthorizedResponse());
}
// Validate user access with external API
const validationResponse = await fetch(
new URL(validationApiRoute, req.url),
{
try {
const userValidate = await fetch(new URL("/api/user-validate", req.url), {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${reqToken}`,
Authorization: `Bearer ${token}`,
},
}
);
});
if (!validationResponse.ok) {
if (!userValidate.ok) {
throw new Error("Failed to validate user");
}
const userValidateJson = await userValidate.json();
if (!userValidateJson.data.active) {
return setCorsHeaders(
NextResponse.redirect(new URL("/waiting-room", req.url))
);
}
} catch (error) {
console.error("Error validating user:", error);
return setCorsHeaders(unauthorizedResponse());
}
}
// Handle API requests
if (pathname.startsWith(apiPath)) {
if (!token) {
return setCorsHeaders(unauthorizedResponse());
}
const dataJson = await validationResponse.json();
try {
const validationResponse = await fetch(
new URL(validationApiRoute, req.url),
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}
);
if (!validationResponse.ok) {
throw new Error("Failed to validate API request");
}
} catch (error) {
console.error("Error validating API request:", error);
return setCorsHeaders(unauthorizedResponse());
}
}
// Proceed with the request
return setCorsHeaders(NextResponse.next());
const response = NextResponse.next();
// Ensure token is preserved in cookie
if (token) {
response.cookies.set(sessionKey, token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
});
}
return setCorsHeaders(response);
};
function isRoutePublic(
pathname: string,
publicRoutes: string[],
loginPath: string
): boolean {
return [...publicRoutes, loginPath].some((route) => {
const pattern = route.replace(/\*/g, ".*");
return new RegExp(`^${pattern}$`).test(pathname);
});
}
function getToken(req: NextRequest, sessionKey: string): string | undefined {
return (
req.cookies.get(sessionKey)?.value ||
req.headers.get("Authorization")?.split(" ")[1]
);
}
function unauthorizedResponse(): NextResponse {
return new NextResponse(JSON.stringify({ error: "Unauthorized" }), {
status: 401,
@@ -251,16 +257,6 @@ async function verifyToken({
}): Promise<Record<string, unknown> | null> {
if (!token) return null;
return await decrypt({ token, encodedKey });
}
async function decrypt({
token,
encodedKey,
}: {
token: string;
encodedKey: string;
}): Promise<Record<string, any> | null> {
try {
const enc = new TextEncoder().encode(encodedKey);
const { payload } = await jwtVerify(token, enc, {
@@ -268,7 +264,7 @@ async function decrypt({
});
return (payload.user as Record<string, any>) || null;
} catch (error) {
console.error("Gagal verifikasi session", error);
console.error("Token verification failed:", error);
return null;
}
}
@@ -276,5 +272,3 @@ async function decrypt({
export const config = {
matcher: ["/((?!_next|static|favicon.ico|manifest).*)"],
};
// wibu:0.2.82