Add Oudelaa dashboard API integration
فشلت بعض الفحوصات
Deploy To Ghaymah / deploy (push) Has been cancelled

هذا الالتزام موجود في:
boutmoun123
2026-05-25 20:36:52 +03:00
الأصل 367fce6557
التزام 8863f61d00
90 ملفات معدلة مع 16694 إضافات و1 حذوفات

عرض الملف

@@ -0,0 +1,248 @@
import { NextRequest, NextResponse } from "next/server";
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? process.env.API_BASE_URL ?? "";
const ACCESS_COOKIE = "oudelaa_sa_access";
const REFRESH_COOKIE = "oudelaa_sa_refresh";
const ACCESS_MAX_AGE_SECONDS = 15 * 60;
const REFRESH_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
function buildTargetUrl(pathSegments: string[], searchParams: URLSearchParams) {
const base = API_BASE_URL.replace(/\/$/, "");
const path = pathSegments.join("/");
const query = searchParams.toString();
return `${base}/${path}${query ? `?${query}` : ""}`;
}
function isSuperAdminLogin(pathSegments: string[]) {
return pathSegments.join("/") === "auth/superadmin/login";
}
function isSuperAdminRefresh(pathSegments: string[]) {
return pathSegments.join("/") === "auth/superadmin/refresh";
}
function isSuperAdminLogout(pathSegments: string[]) {
return pathSegments.join("/") === "auth/superadmin/logout";
}
function isJsonContent(contentType: string | null) {
return (contentType ?? "").toLowerCase().includes("application/json");
}
function parseJsonSafe<T>(value: string): T | null {
try {
return JSON.parse(value) as T;
} catch {
return null;
}
}
function buildUpstreamHeaders(params: {
req: NextRequest;
pathSegments: string[];
accessToken?: string;
contentType: string | null;
}) {
const headers = new Headers();
const { req, pathSegments, accessToken, contentType } = params;
const accept = req.headers.get("accept");
if (accept) {
headers.set("accept", accept);
}
if (contentType) {
headers.set("content-type", contentType);
}
const authorization = req.headers.get("authorization");
if (authorization) {
headers.set("authorization", authorization);
} else if (accessToken && !isSuperAdminLogin(pathSegments)) {
headers.set("authorization", `Bearer ${accessToken}`);
}
return headers;
}
function applyAuthCookies(response: NextResponse, payload: {
accessToken?: string;
refreshToken?: string;
}) {
const secure = process.env.NODE_ENV === "production";
if (payload.accessToken) {
response.cookies.set(ACCESS_COOKIE, payload.accessToken, {
httpOnly: true,
secure,
sameSite: "lax",
path: "/",
maxAge: ACCESS_MAX_AGE_SECONDS,
});
}
if (payload.refreshToken) {
response.cookies.set(REFRESH_COOKIE, payload.refreshToken, {
httpOnly: true,
secure,
sameSite: "lax",
path: "/",
maxAge: REFRESH_MAX_AGE_SECONDS,
});
}
}
function clearAuthCookies(response: NextResponse) {
response.cookies.set(ACCESS_COOKIE, "", {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: 0,
});
response.cookies.set(REFRESH_COOKIE, "", {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: 0,
});
}
async function proxyRequest(req: NextRequest, pathSegments: string[] = []) {
if (!API_BASE_URL) {
return new NextResponse("Missing API base URL", { status: 500 });
}
const url = new URL(req.url);
const targetUrl = buildTargetUrl(pathSegments, url.searchParams);
const isReadOnlyMethod = ["GET", "HEAD"].includes(req.method);
const contentType = req.headers.get("content-type");
const accessToken = req.cookies.get(ACCESS_COOKIE)?.value;
const refreshToken = req.cookies.get(REFRESH_COOKIE)?.value;
const headers = buildUpstreamHeaders({
req,
pathSegments,
accessToken,
contentType,
});
let body: BodyInit | undefined;
if (!isReadOnlyMethod) {
const bodyBuffer = await req.arrayBuffer();
const hasBody = bodyBuffer.byteLength > 0;
body = hasBody ? bodyBuffer : undefined;
const shouldInjectRefreshToken =
(isSuperAdminRefresh(pathSegments) || isSuperAdminLogout(pathSegments)) &&
isJsonContent(contentType);
if (shouldInjectRefreshToken) {
const rawText = hasBody ? Buffer.from(bodyBuffer).toString("utf8") : "";
const parsed = parseJsonSafe<Record<string, unknown>>(rawText) ?? {};
if (!parsed.refreshToken && refreshToken) {
parsed.refreshToken = refreshToken;
}
body = JSON.stringify(parsed);
headers.set("content-type", "application/json");
}
}
const init: RequestInit = {
method: req.method,
headers,
cache: "no-store",
} as RequestInit;
if (body !== undefined) {
init.body = body;
}
try {
const upstream = await fetch(targetUrl, init);
const responseHeaders = new Headers(upstream.headers);
responseHeaders.delete("content-encoding");
responseHeaders.delete("content-length");
const isAuthResponse =
isSuperAdminLogin(pathSegments) ||
isSuperAdminRefresh(pathSegments) ||
isSuperAdminLogout(pathSegments);
if (isAuthResponse && isJsonContent(upstream.headers.get("content-type"))) {
const payload = (await upstream.json()) as Record<string, unknown>;
const response = NextResponse.json(payload, {
status: upstream.status,
headers: responseHeaders,
});
if (upstream.ok && (isSuperAdminLogin(pathSegments) || isSuperAdminRefresh(pathSegments))) {
applyAuthCookies(response, {
accessToken: typeof payload.accessToken === "string" ? payload.accessToken : undefined,
refreshToken: typeof payload.refreshToken === "string" ? payload.refreshToken : undefined,
});
}
if (upstream.ok && isSuperAdminLogout(pathSegments)) {
clearAuthCookies(response);
}
return response;
}
const response = new NextResponse(upstream.body, {
status: upstream.status,
headers: responseHeaders,
});
if (upstream.ok && isSuperAdminLogout(pathSegments)) {
clearAuthCookies(response);
}
return response;
} catch (error) {
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
return new NextResponse(`Upstream fetch failed: ${message}`, { status: 502 });
}
}
export async function GET(
req: NextRequest,
context: { params: Promise<{ path: string[] }> },
) {
const { path } = await context.params;
return proxyRequest(req, path ?? []);
}
export async function POST(
req: NextRequest,
context: { params: Promise<{ path: string[] }> },
) {
const { path } = await context.params;
return proxyRequest(req, path ?? []);
}
export async function PATCH(
req: NextRequest,
context: { params: Promise<{ path: string[] }> },
) {
const { path } = await context.params;
return proxyRequest(req, path ?? []);
}
export async function DELETE(
req: NextRequest,
context: { params: Promise<{ path: string[] }> },
) {
const { path } = await context.params;
return proxyRequest(req, path ?? []);
}
export async function PUT(
req: NextRequest,
context: { params: Promise<{ path: string[] }> },
) {
const { path } = await context.params;
return proxyRequest(req, path ?? []);
}