create middleware block and update proxy file also make token stored in cookes and up date user page to can show user details also fixed delete user error and delete car error and broken corse inginx image block now we can uplode image to server and but it in my app

This commit is contained in:
m7amedez5511
2026-06-17 17:01:53 +03:00
parent 6ca2cc08d1
commit a23d21f222
15 changed files with 1017 additions and 137 deletions

View File

@@ -0,0 +1,35 @@
// app/api/auth/set-cookie/route.ts
// Receives the JWT from the client immediately after login and stores it
// in an HttpOnly, Secure, SameSite=Strict cookie.
// The client never stores the raw token — it passes through this endpoint once.
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const body = await request.json().catch(() => null);
const token: unknown = body?.token;
if (typeof token !== "string" || !token) {
return NextResponse.json({ error: "Invalid token" }, { status: 400 });
}
const isProduction = process.env.NODE_ENV === "production";
// Build the Set-Cookie header manually so we have full control over flags.
// - HttpOnly: JavaScript cannot read this cookie.
// - Secure: Only sent over HTTPS (omitted in development for localhost).
// - SameSite=Strict: Not sent on cross-site requests, mitigating CSRF.
// - Path=/: Available for all routes (needed for the middleware to read it).
const cookieParts = [
`auth_token=${encodeURIComponent(token)}`,
"HttpOnly",
isProduction ? "Secure" : "", // Omit Secure on localhost (no HTTPS)
"SameSite=Strict",
"Path=/",
"Max-Age=604800", // 7 days in seconds
].filter(Boolean);
const response = NextResponse.json({ ok: true });
response.headers.set("Set-Cookie", cookieParts.join("; "));
return response;
}

View File

@@ -0,0 +1,17 @@
// app/api/auth/clear-cookie/route.ts
// Clears the HttpOnly auth cookie on logout.
// Only the server can delete an HttpOnly cookie.
import { NextResponse } from "next/server";
export async function POST() {
const response = NextResponse.json({ ok: true });
// Overwrite the cookie with an empty value and a past expiry date.
response.headers.set(
"Set-Cookie",
"auth_token=; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=0",
);
return response;
}

View File

@@ -1,65 +1,116 @@
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
const BACKEND_BASE_URL = "https://logiapi.slash.sa/api";
const BACKEND_BASE_URL = "https://logiapi.slash.sa/api/v1";
async function proxy(
request: NextRequest,
params: { path: string[] },
): Promise<NextResponse> {
const path = params.path?.join("/") ?? "";
const targetUrl = `${BACKEND_BASE_URL}/${path}${request.nextUrl.search}`;
// Read the HttpOnly cookie server-side — this is the ONLY place the raw
// token value is accessible after the Issue 3 refactor.
const cookieStore = await cookies();
const token = cookieStore.get("auth_token")?.value;
function buildHeaders(request: NextRequest) {
const headers = new Headers();
// Forward all safe request headers.
request.headers.forEach((value, key) => {
if (key === "host" || key === "content-length") return;
if (key === "host" || key === "content-length" || key === "cookie") return;
headers.set(key, value);
});
headers.set("x-forwarded-host", request.headers.get("host") || "localhost");
headers.delete("origin");
// Inject the Authorization header using the server-side cookie value.
// The client never had access to this token value.
if (token) {
headers.set("Authorization", `Bearer ${decodeURIComponent(token)}`);
}
return headers;
}
async function proxy(request: NextRequest, params: { path: string[] }) {
const path = params.path?.join("/") ?? "";
const targetUrl = `${BACKEND_BASE_URL}/${path}${request.nextUrl.search}`;
if (!headers.has("Content-Type")) {
headers.set("Content-Type", "application/json");
}
const isBodyless = request.method === "GET" || request.method === "HEAD";
const body = isBodyless ? undefined : await request.text();
const upstream = await fetch(targetUrl, {
method: request.method,
headers: buildHeaders(request),
headers,
body,
cache: "no-store",
});
const contentType = upstream.headers.get("content-type") || "application/json";
const contentType =
upstream.headers.get("content-type") || "application/json";
// Responses with these statuses MUST NOT carry a body (per the Fetch/HTTP
// spec). Constructing a NextResponse with a body alongside one of these
// statuses throws at runtime, which Next.js then surfaces as a 500 to the
// client — even though the upstream call itself succeeded. This is exactly
// what happens on DELETE endpoints that correctly return 204 No Content.
const isNoBodyStatus = [204, 205, 304].includes(upstream.status);
if (isNoBodyStatus) {
// Drain the upstream body (should be empty anyway) without parsing it.
await upstream.text().catch(() => null);
return new NextResponse(null, {
status: upstream.status,
headers: { "cache-control": "no-store" },
});
}
const responseBody = contentType.includes("application/json")
? await upstream.json().catch(() => null)
: await upstream.text();
return new NextResponse(typeof responseBody === "string" ? responseBody : JSON.stringify(responseBody), {
status: upstream.status,
headers: {
"content-type": contentType,
"cache-control": "no-store",
return new NextResponse(
typeof responseBody === "string"
? responseBody
: JSON.stringify(responseBody),
{
status: upstream.status,
headers: {
"content-type": contentType,
"cache-control": "no-store",
},
},
});
);
}
export async function GET(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
return proxy(request, await params);
}
export async function POST(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
return proxy(request, await params);
}
export async function PUT(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
return proxy(request, await params);
}
export async function PATCH(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
return proxy(request, await params);
}
export async function DELETE(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
return proxy(request, await params);
}
}