import { NextRequest, NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; const MAIN_DOMAIN = process.env.MAIN_DOMAIN || "alfrednobel.edudeploy.com"; async function proxyRequest(req: NextRequest) { const host = req.headers.get("host") || ""; const cleanHost = host.split(":")[0]; if (!cleanHost.endsWith(`.${MAIN_DOMAIN}`)) { return NextResponse.json({ error: "Invalid host" }, { status: 404 }); } const subdomain = cleanHost.replace(`.${MAIN_DOMAIN}`, ""); const instance = await prisma.instance.findUnique({ where: { id: subdomain }, include: { node: true }, }); if (!instance || !instance.node?.tailscaleIp) { return NextResponse.json( { error: "Instance not found or not connected" }, { status: 404 } ); } const targetUrl = new URL(req.url); const upstream = `http://${instance.node.tailscaleIp}:${instance.port}${targetUrl.pathname}${targetUrl.search}`; const headers = new Headers(req.headers); headers.delete("host"); headers.set("host", cleanHost); const upstreamRes = await fetch(upstream, { method: req.method, headers, body: req.method !== "GET" && req.method !== "HEAD" ? (req.body as BodyInit) : undefined, redirect: "manual", }); const responseHeaders = new Headers(upstreamRes.headers); // Remove content-encoding because Next.js/fetch handles decompression automatically responseHeaders.delete("content-encoding"); responseHeaders.delete("content-length"); return new Response(upstreamRes.body, { status: upstreamRes.status, statusText: upstreamRes.statusText, headers: responseHeaders, }); } export const GET = proxyRequest; export const POST = proxyRequest; export const PUT = proxyRequest; export const PATCH = proxyRequest; export const DELETE = proxyRequest; export const HEAD = proxyRequest; export const OPTIONS = proxyRequest;