"use server"; import os from "os"; import { execSync } from "child_process"; import { installTailscale, loadState, generateShortId } from "@/lib/tunnel"; import { getCachedPassword, loadEncryptedPassword, initDbHooks } from "@/mitm/manager"; import { getSettings, updateSettings } from "@/lib/localDb"; initDbHooks(getSettings, updateSettings); const EXTENDED_PATH = `/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${process.env.PATH || ""}`; function hasBrew() { try { execSync("which brew", { stdio: "ignore", windowsHide: true, env: { ...process.env, PATH: EXTENDED_PATH } }); return true; } catch { return false; } } export async function POST(request) { const body = await request.json().catch(() => ({})); const platform = os.platform(); const isWindows = platform === "win32"; const isBrew = platform === "darwin" && hasBrew(); const needsPassword = !isWindows && !isBrew; const sudoPassword = body.sudoPassword || getCachedPassword() || await loadEncryptedPassword() || ""; if (needsPassword && !sudoPassword.trim()) { return new Response(JSON.stringify({ error: "Sudo password is required" }), { status: 400, headers: { "Content-Type": "application/json" }, }); } const shortId = loadState()?.shortId || generateShortId(); const encoder = new TextEncoder(); const stream = new ReadableStream({ async start(controller) { let closed = false; const send = (event, data) => { if (closed) return; try { controller.enqueue(encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`)); } catch { closed = true; } }; try { const result = await installTailscale(sudoPassword, shortId, (msg) => { send("progress", { message: msg }); }); send("done", { success: true, authUrl: result?.authUrl || null }); } catch (error) { console.error("Tailscale install error:", error); const msg = error.message?.includes("incorrect password") || error.message?.includes("Sorry") ? "Wrong sudo password" : error.message; send("error", { error: msg }); } finally { if (!closed) { try { controller.close(); } catch {} } } }, }); return new Response(stream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive", }, }); }