Yogesh
initial deploy
cd8bd0a
Raw
History Blame Contribute Delete
5.72 kB
// Node.js-only route: uses child_process, fs, path via mitm/manager
// Dynamic imports prevent Turbopack from statically resolving native modules
export const runtime = "nodejs";
import { NextResponse } from "next/server";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
import { requireCliToolsAuth } from "@/lib/api/requireCliToolsAuth";
import { cliMitmStartSchema, cliMitmStopSchema } from "@/shared/validation/schemas";
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
import { resolveApiKey } from "@/shared/services/apiKeyResolver";
import { isRoot } from "@/mitm/systemCommands";
import { isSudoPasswordRequired } from "@/mitm/dns/dnsConfig";
// GET - Check MITM status
export async function GET(request) {
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
try {
const { getMitmStatus, getCachedPassword } = await import("@/mitm/manager.runtime");
const status = await getMitmStatus();
const isWin = process.platform === "win32";
const hasCachedPassword = !!getCachedPassword();
// Probe sudo availability so the UI can hide the password modal on hosts
// where it's unnecessary (Windows, root user, NOPASSWD sudoers, minimal
// containers without sudo). MITM elevation is decided by the server OS,
// not by the browser's user agent — see PR title.
const needsSudoPassword = !isWin && !hasCachedPassword && isSudoPasswordRequired();
return NextResponse.json({
running: status.running,
pid: status.pid || null,
dnsConfigured: status.dnsConfigured || false,
certExists: status.certExists || false,
hasCachedPassword,
isWin,
needsSudoPassword,
});
} catch (error) {
console.log("Error getting MITM status:", sanitizeErrorMessage(error));
return NextResponse.json({ error: "Failed to get MITM status" }, { status: 500 });
}
}
// POST - Start MITM proxy
export async function POST(request) {
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
let rawBody;
try {
rawBody = await request.json();
} catch {
return NextResponse.json(
{
error: {
message: "Invalid request",
details: [{ field: "body", message: "Invalid JSON body" }],
},
},
{ status: 400 }
);
}
try {
const validation = validateBody(cliMitmStartSchema, rawBody);
if (isValidationFailure(validation)) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}
const { apiKey: rawApiKey, keyId: rawKeyId, sudoPassword } = validation.data;
const apiKeyId = rawKeyId ?? null;
const apiKey = await resolveApiKey(apiKeyId, rawApiKey);
if (!apiKey || apiKey === "sk_omniroute") {
return NextResponse.json(
{ error: "Missing apiKey: provide a valid apiKey or a resolvable keyId" },
{ status: 400 }
);
}
const { startMitm, getCachedPassword, setCachedPassword } =
await import("@/mitm/manager.runtime");
const isWin = process.platform === "win32";
const isRootUser = !isWin && isRoot();
const pwd = sudoPassword || getCachedPassword() || "";
// Require a sudo password only when the OS actually needs one. Skips the
// prompt on Windows (UAC), root, NOPASSWD sudoers, and minimal containers
// without sudo on PATH (#822).
if (!isWin && !pwd && !isRootUser && isSudoPasswordRequired()) {
return NextResponse.json({ error: "Missing sudoPassword" }, { status: 400 });
}
const result = await startMitm(apiKey, pwd);
if (!isWin && pwd) setCachedPassword(pwd);
return NextResponse.json({
success: true,
running: result.running,
pid: result.pid,
});
} catch (error) {
console.log("Error starting MITM:", sanitizeErrorMessage(error));
return NextResponse.json(
{ error: sanitizeErrorMessage(error) || "Failed to start MITM proxy" },
{ status: 500 }
);
}
}
// DELETE - Stop MITM proxy
export async function DELETE(request) {
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
let rawBody;
try {
rawBody = await request.json();
} catch {
return NextResponse.json(
{
error: {
message: "Invalid request",
details: [{ field: "body", message: "Invalid JSON body" }],
},
},
{ status: 400 }
);
}
try {
const validation = validateBody(cliMitmStopSchema, rawBody);
if (isValidationFailure(validation)) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}
const { sudoPassword } = validation.data;
const { stopMitm, getCachedPassword, setCachedPassword } =
await import("@/mitm/manager.runtime");
const isWin = process.platform === "win32";
const isRootUser = !isWin && isRoot();
const pwd = sudoPassword || getCachedPassword() || "";
// Require a sudo password only when the OS actually needs one. Skips the
// prompt on Windows (UAC), root, NOPASSWD sudoers, and minimal containers
// without sudo on PATH (#822).
if (!isWin && !pwd && !isRootUser && isSudoPasswordRequired()) {
return NextResponse.json({ error: "Missing sudoPassword" }, { status: 400 });
}
await stopMitm(pwd);
if (!isWin && sudoPassword) setCachedPassword(sudoPassword);
return NextResponse.json({ success: true, running: false });
} catch (error) {
console.log("Error stopping MITM:", sanitizeErrorMessage(error));
return NextResponse.json(
{ error: sanitizeErrorMessage(error) || "Failed to stop MITM proxy" },
{ status: 500 }
);
}
}