import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
import {
  Hono,
  LOG_DIR,
  WEBSOCKIFY_PORT,
  canAccessAdmin,
  clientIpMiddleware,
  describeRemoteSession,
  getRequestListener,
  newCorrId,
  parseCookieValue,
  requireAdminCookie,
  requirePortEnv,
  sanitizeClientCorrId,
  vncLog,
  websockifyLog
} from "./chunk-76HRO7NX.js";
import "./chunk-HYQNUVGO.js";

// server/edge.ts
import { createServer, request as httpRequest } from "http";
import { createConnection as createConnection2 } from "net";
import { readFileSync as readFileSync4, existsSync as existsSync4, watchFile } from "fs";
import { homedir } from "os";
import { join as join4 } from "path";

// server/ws-proxy.ts
import { createConnection } from "net";
var WS_PATH = "/websockify";
var UPSTREAM_TIMEOUT_MS = 5e3;
var HOP_BY_HOP = /* @__PURE__ */ new Set([
  "connection",
  "keep-alive",
  "proxy-authenticate",
  "proxy-authorization",
  "te",
  "trailer",
  "transfer-encoding",
  "upgrade"
]);
function attachVncWsProxy(server2, opts) {
  const upstreamHost = opts.upstreamHost ?? "127.0.0.1";
  const upstreamPort = opts.upstreamPort ?? WEBSOCKIFY_PORT;
  const brand = opts.brand ?? "unknown";
  server2.on("upgrade", (req, clientSocket, head) => {
    try {
      handleUpgrade(req, clientSocket, head, {
        isPublicHost: opts.isPublicHost,
        upstreamHost,
        upstreamPort,
        brand
      });
    } catch (err) {
      vncLog("ws-upgrade", {
        decision: "rejected",
        reason: "handler-exception",
        err: err.message
      });
      clientSocket.destroy();
    }
  });
}
function handleUpgrade(req, clientSocket, head, opts) {
  const url = req.url ?? "";
  const qsIndex = url.indexOf("?");
  const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
  if (pathname !== WS_PATH) {
    return;
  }
  const corrId = newCorrId();
  const query = qsIndex === -1 ? "" : url.slice(qsIndex + 1);
  const rawClientCorrId = parseQueryParam(query, "corrId");
  const clientCorrId = sanitizeClientCorrId(rawClientCorrId);
  const hostHeader = (req.headers.host ?? "").split(":")[0];
  const originHeader = headerString(req.headers.origin);
  const remote = req.socket.remoteAddress;
  const xff = headerString(req.headers["x-forwarded-for"]);
  const cookieHeader = headerString(req.headers.cookie);
  const decision = canAccessAdmin({
    host: hostHeader,
    remoteAddress: remote,
    xForwardedFor: xff,
    cookieHeader,
    isPublicHost: opts.isPublicHost
  });
  if (!decision.allow) {
    const status = decision.reason === "public-host" ? 404 : 401;
    const rawToken = parseCookieValue(cookieHeader, "__remote_session");
    const tokenInfo = describeRemoteSession(rawToken);
    vncLog("ws-upgrade", {
      corrId,
      clientCorrId: clientCorrId ?? null,
      decision: "rejected",
      reason: decision.reason,
      ip: remote,
      xff: xff ?? null,
      origin: originHeader ?? null,
      host: hostHeader,
      cookieHeaderPresent: cookieHeader != null && cookieHeader.length > 0,
      tokenPresent: tokenInfo.present,
      tokenExpired: tokenInfo.expired
    });
    writeStatusAndDestroy(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
    return;
  }
  const originHost = parseOriginHost(originHeader);
  if (!originHost) {
    vncLog("ws-upgrade", {
      corrId,
      clientCorrId: clientCorrId ?? null,
      decision: "rejected",
      reason: "origin-missing-or-invalid",
      origin: originHeader ?? null,
      host: hostHeader,
      ip: remote
    });
    writeStatusAndDestroy(clientSocket, 403, "Forbidden");
    return;
  }
  if (originHost !== hostHeader) {
    vncLog("ws-upgrade", {
      corrId,
      clientCorrId: clientCorrId ?? null,
      decision: "rejected",
      reason: "origin-mismatch",
      origin_host: originHost,
      host: hostHeader,
      ip: remote
    });
    writeStatusAndDestroy(clientSocket, 403, "Forbidden");
    return;
  }
  if (opts.isPublicHost(originHost)) {
    vncLog("ws-upgrade", {
      corrId,
      clientCorrId: clientCorrId ?? null,
      decision: "rejected",
      reason: "origin-public-host",
      origin_host: originHost,
      host: hostHeader,
      ip: remote
    });
    writeStatusAndDestroy(clientSocket, 403, "Forbidden");
    return;
  }
  vncLog("ws-upgrade", {
    corrId,
    clientCorrId: clientCorrId ?? null,
    decision: "accepted",
    ip: remote,
    xff: xff ?? null,
    origin: originHeader ?? null,
    host: hostHeader,
    sec_ws_version: headerString(req.headers["sec-websocket-version"]) ?? null,
    sec_ws_protocol: headerString(req.headers["sec-websocket-protocol"]) ?? null
  });
  websockifyLog("upgrade", { brand: opts.brand, remote: remote ?? "unknown", sid: corrId });
  const upgradeStart = Date.now();
  const upstream = createConnection({ host: opts.upstreamHost, port: opts.upstreamPort });
  upstream.setTimeout(UPSTREAM_TIMEOUT_MS);
  let bytesClientToUpstream = 0;
  let bytesUpstreamToClient = 0;
  let closedBy = null;
  let proxyOpened = false;
  const finish = (side, reason) => {
    if (closedBy) return;
    closedBy = side;
    if (proxyOpened) {
      vncLog("proxy-close", {
        corrId,
        closedBy: side,
        reason,
        clientBytes: bytesClientToUpstream,
        upstreamBytes: bytesUpstreamToClient
      });
      websockifyLog("close", {
        brand: opts.brand,
        code: reason === "error" ? 1006 : 1e3,
        reason,
        durationMs: Date.now() - upgradeStart,
        sid: corrId
      });
    }
    clientSocket.destroy();
    upstream.destroy();
  };
  upstream.once("connect", () => {
    upstream.setTimeout(0);
    proxyOpened = true;
    vncLog("proxy-open", {
      corrId,
      upstream: `${opts.upstreamHost}:${opts.upstreamPort}`
    });
    const lines = [];
    lines.push(`${req.method ?? "GET"} ${WS_PATH} HTTP/${req.httpVersion}`);
    lines.push(`host: ${opts.upstreamHost}:${opts.upstreamPort}`);
    for (const [name, value] of Object.entries(req.headers)) {
      if (name === "host") continue;
      if (HOP_BY_HOP.has(name)) continue;
      if (value == null) continue;
      if (Array.isArray(value)) {
        for (const v of value) lines.push(`${name}: ${v}`);
      } else {
        lines.push(`${name}: ${value}`);
      }
    }
    const upgradeHeader = headerString(req.headers.upgrade);
    const connectionHeader = headerString(req.headers.connection);
    if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
    if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
    upstream.write(lines.join("\r\n") + "\r\n\r\n");
    if (head && head.length > 0) upstream.write(head);
    clientSocket.on("data", (chunk) => {
      bytesClientToUpstream += chunk.length;
    });
    upstream.on("data", (chunk) => {
      bytesUpstreamToClient += chunk.length;
    });
    clientSocket.pipe(upstream);
    upstream.pipe(clientSocket);
    clientSocket.once("close", (hadError) => {
      finish("client", hadError ? "error" : "normal");
    });
    upstream.once("close", (hadError) => {
      finish("upstream", hadError ? "error" : "normal");
    });
    clientSocket.once("error", (err) => {
      vncLog("proxy-error", { corrId, side: "client", err: err.message });
      finish("client", "error");
    });
    upstream.once("error", (err) => {
      vncLog("proxy-error", { corrId, side: "upstream", err: err.message });
      finish("upstream", "error");
    });
  });
  upstream.once("timeout", () => {
    if (proxyOpened) return;
    vncLog("proxy-error", {
      corrId,
      side: "upstream-connect",
      err: "timeout",
      timeout_ms: UPSTREAM_TIMEOUT_MS
    });
    websockifyLog("close", {
      brand: opts.brand,
      code: 1006,
      reason: "upstream-connect-timeout",
      durationMs: Date.now() - upgradeStart,
      sid: corrId
    });
    writeStatusAndDestroy(clientSocket, 504, "Gateway Timeout");
    upstream.destroy();
  });
  upstream.once("error", (err) => {
    if (proxyOpened) return;
    vncLog("proxy-error", {
      corrId,
      side: "upstream-connect",
      err: err.message
    });
    websockifyLog("close", {
      brand: opts.brand,
      code: 1006,
      reason: "upstream-connect-error",
      durationMs: Date.now() - upgradeStart,
      sid: corrId,
      err: err.message
    });
    writeStatusAndDestroy(clientSocket, 502, "Bad Gateway");
    upstream.destroy();
  });
}
function parseQueryParam(query, key) {
  if (!query) return null;
  for (const pair of query.split("&")) {
    const eq = pair.indexOf("=");
    const k = eq === -1 ? pair : pair.slice(0, eq);
    if (k !== key) continue;
    const v = eq === -1 ? "" : pair.slice(eq + 1);
    try {
      return decodeURIComponent(v);
    } catch {
      return null;
    }
  }
  return null;
}
function headerString(value) {
  if (value == null) return void 0;
  return Array.isArray(value) ? value[0] : value;
}
function parseOriginHost(origin) {
  if (!origin) return null;
  try {
    return new URL(origin).hostname;
  } catch {
    return null;
  }
}
function writeStatusAndDestroy(socket, status, statusText) {
  try {
    socket.write(
      `HTTP/1.1 ${status} ${statusText}\r
Connection: close\r
Content-Length: 0\r
\r
`
    );
  } catch {
  }
  socket.destroy();
}

// server/routes/admin/version.ts
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
import { resolve, join as join2 } from "path";

// server/lib/client-error-count.ts
import { existsSync, readFileSync, statSync, openSync, readSync, closeSync } from "fs";
import { join } from "path";
var CLIENT_ERRORS_LOG = join(LOG_DIR, "client-errors.log");
var DAY_MS = 24 * 60 * 60 * 1e3;
var TAIL_BYTES = 256 * 1024;
function countClientErrors24h() {
  if (!existsSync(CLIENT_ERRORS_LOG)) return 0;
  let tail;
  try {
    const size = statSync(CLIENT_ERRORS_LOG).size;
    if (size <= TAIL_BYTES) {
      tail = readFileSync(CLIENT_ERRORS_LOG, "utf-8");
    } else {
      const fd = openSync(CLIENT_ERRORS_LOG, "r");
      try {
        const buf = Buffer.alloc(TAIL_BYTES);
        readSync(fd, buf, 0, TAIL_BYTES, size - TAIL_BYTES);
        const text = buf.toString("utf-8");
        const nl = text.indexOf("\n");
        tail = nl === -1 ? "" : text.slice(nl + 1);
      } finally {
        closeSync(fd);
      }
    }
  } catch (err) {
    console.error(`[client-error-count] read failed: ${err instanceof Error ? err.message : String(err)}`);
    return 0;
  }
  const cutoff = Date.now() - DAY_MS;
  let count = 0;
  for (const line of tail.split("\n")) {
    if (!line) continue;
    try {
      const entry = JSON.parse(line);
      if (typeof entry.ts !== "string") continue;
      const ms = Date.parse(entry.ts);
      if (Number.isFinite(ms) && ms >= cutoff) count++;
    } catch {
    }
  }
  return count;
}

// server/routes/admin/version.ts
var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve(process.cwd(), "..");
var DEFAULT_HOSTNAME = "maxy";
var DEFAULT_NPM_PACKAGE = "@rubytech/create-maxy";
var FETCH_TIMEOUT_MS = 5e3;
var fallbackLogged = /* @__PURE__ */ new Set();
function logFallbackOnce(reason, field, usingDefault) {
  const key = `${reason}:${field}`;
  if (fallbackLogged.has(key)) return;
  fallbackLogged.add(key);
  console.log(`[admin/version] brand-config-fallback reason=${reason} field=${field} using=${usingDefault}`);
}
function resolveBrandConfig() {
  const brandJsonPath = join2(PLATFORM_ROOT, "config", "brand.json");
  if (!existsSync2(brandJsonPath)) {
    logFallbackOnce("file-missing", "hostname", DEFAULT_HOSTNAME);
    logFallbackOnce("file-missing", "npm.packageName", DEFAULT_NPM_PACKAGE);
    return { hostname: DEFAULT_HOSTNAME, npmPackage: DEFAULT_NPM_PACKAGE };
  }
  let parsed;
  try {
    parsed = JSON.parse(readFileSync2(brandJsonPath, "utf-8"));
  } catch {
    logFallbackOnce("parse-failed", "hostname", DEFAULT_HOSTNAME);
    logFallbackOnce("parse-failed", "npm.packageName", DEFAULT_NPM_PACKAGE);
    return { hostname: DEFAULT_HOSTNAME, npmPackage: DEFAULT_NPM_PACKAGE };
  }
  let hostname = DEFAULT_HOSTNAME;
  let npmPackage = DEFAULT_NPM_PACKAGE;
  if (typeof parsed?.hostname === "string" && parsed.hostname) {
    hostname = parsed.hostname;
  } else {
    logFallbackOnce("field-missing", "hostname", DEFAULT_HOSTNAME);
  }
  if (typeof parsed?.npm?.packageName === "string" && parsed.npm.packageName) {
    npmPackage = parsed.npm.packageName;
  } else {
    logFallbackOnce("field-missing", "npm.packageName", DEFAULT_NPM_PACKAGE);
  }
  return { hostname, npmPackage };
}
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
function readInstalled(versionFile) {
  if (!existsSync2(versionFile)) return "unknown";
  const content = readFileSync2(versionFile, "utf-8").trim();
  return content || "unknown";
}
async function fetchLatest(npmPackage) {
  const registryUrl = `https://registry.npmjs.org/${npmPackage}/latest`;
  try {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
    const res = await fetch(registryUrl, { signal: controller.signal });
    clearTimeout(timer);
    if (!res.ok) {
      console.error(`[admin/version] npm registry returned ${res.status}`);
      return null;
    }
    const data = await res.json();
    const version = data?.version;
    if (typeof version !== "string") {
      console.error("[admin/version] npm registry response missing version field");
      return null;
    }
    return version;
  } catch (err) {
    console.error(`[admin/version] npm registry fetch failed: ${err}`);
    return null;
  }
}
function isNewer(latest, installed) {
  const parseSemver = (v) => v.replace(/^v/, "").split(".").map(Number);
  const l = parseSemver(latest);
  const i = parseSemver(installed);
  for (let idx = 0; idx < 3; idx++) {
    const lp = l[idx] ?? 0;
    const ip = i[idx] ?? 0;
    if (Number.isNaN(lp) || Number.isNaN(ip)) return false;
    if (lp > ip) return true;
    if (lp < ip) return false;
  }
  return false;
}
var app = new Hono();
app.get("/", async (c) => {
  const { hostname, npmPackage } = resolveBrandConfig();
  const versionFile = resolve(PLATFORM_ROOT, `config/.${hostname}-version`);
  const installed = readInstalled(versionFile);
  const latest = await fetchLatest(npmPackage);
  const updateAvailable = installed !== "unknown" && latest !== null && isNewer(latest, installed);
  const outcome = installed === "unknown" ? "stale-version-file" : latest === null ? "fetch-failed" : "ok";
  const clientErrors24h = countClientErrors24h();
  console.log(
    `[admin/version] outcome=${outcome} installed=${installed} latest=${latest ?? "null"} updateAvailable=${updateAvailable} clientErrors24h=${clientErrors24h} processStartedAt=${PROCESS_STARTED_AT} versionFile=${versionFile} npmPackage=${npmPackage}`
  );
  return c.json({ installed, latest, updateAvailable, clientErrors24h, processStartedAt: PROCESS_STARTED_AT });
});
app.post("/alert-surfaced", async (c) => {
  let installed = "unknown";
  let latest = null;
  try {
    const body = await c.req.json();
    if (typeof body?.installed === "string") installed = body.installed;
    if (typeof body?.latest === "string") latest = body.latest;
  } catch (err) {
    console.error(`[admin/version] alert-surfaced body parse failed: ${err}`);
  }
  console.log(`[admin/version] alert-surfaced installed=${installed} latest=${latest ?? "null"}`);
  return c.json({ ok: true });
});
var version_default = app;

// server/edge-admin.ts
function createEdgeAdminApp(opts) {
  const app2 = new Hono();
  app2.use("*", clientIpMiddleware);
  app2.use("*", async (c, next) => {
    const start = Date.now();
    console.log(
      `[edge-admin] inbound method=${c.req.method} path=${c.req.path} allowlist-branch=edge-admin`
    );
    await next();
    console.log(
      `[edge-admin] outcome method=${c.req.method} path=${c.req.path} status=${c.res.status} duration_ms=${Date.now() - start}`
    );
  });
  app2.use("*", requireAdminCookie(opts.isPublicHost));
  app2.route("/api/admin/version", version_default);
  return app2;
}

// server/edge-fallback.ts
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
import { extname, join as join3 } from "path";
var BRAND_DEFAULTS = {
  configDir: ".maxy",
  productName: "Maxy",
  // Editorial system (paper-200 / ink-900, Newsreader + Inter). brand.json
  // overrides these per install; these defaults apply only when it is absent.
  background: "#F6F5F2",
  textColor: "#2A2D38",
  displayFont: "'Newsreader', Georgia, serif",
  bodyFont: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif",
  logoContainsName: false,
  logoDataUri: null
};
var MIME_BY_EXT = {
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".svg": "image/svg+xml",
  ".webp": "image/webp",
  ".gif": "image/gif"
};
function inlineAsset(filePath) {
  if (!existsSync3(filePath)) return null;
  const mime = MIME_BY_EXT[extname(filePath).toLowerCase()];
  if (!mime) return null;
  const bytes = readFileSync3(filePath);
  return `data:${mime};base64,${bytes.toString("base64")}`;
}
function readString(value, fallback) {
  return typeof value === "string" && value.length > 0 ? value : fallback;
}
function loadBrand(brandJsonPath, assetsDir = "") {
  if (!brandJsonPath || !existsSync3(brandJsonPath)) return { ...BRAND_DEFAULTS };
  try {
    const parsed = JSON.parse(readFileSync3(brandJsonPath, "utf-8"));
    const logoFile = typeof parsed.assets?.logo === "string" ? parsed.assets.logo : null;
    const logoDataUri = logoFile && assetsDir ? inlineAsset(join3(assetsDir, logoFile)) : null;
    return {
      configDir: readString(parsed.configDir, BRAND_DEFAULTS.configDir),
      productName: readString(parsed.productName, BRAND_DEFAULTS.productName),
      background: readString(parsed.defaultColors?.background, BRAND_DEFAULTS.background),
      textColor: BRAND_DEFAULTS.textColor,
      displayFont: readString(parsed.defaultFonts?.display, BRAND_DEFAULTS.displayFont),
      bodyFont: readString(parsed.defaultFonts?.body, BRAND_DEFAULTS.bodyFont),
      logoContainsName: parsed.logoContainsName === true,
      logoDataUri
    };
  } catch (err) {
    console.error(`[edge] brand.json parse error: ${err.message}`);
    return { ...BRAND_DEFAULTS };
  }
}
function classifyUpstreamError(err) {
  return err && err.code === "ECONNREFUSED" ? "econnrefused-coldstart" : "other";
}
var HTML_ESCAPES = { "<": "&lt;", ">": "&gt;", "&": "&amp;", '"': "&quot;", "'": "&#39;" };
function escapeHtml(s) {
  return String(s).replace(/[<>&"']/g, (c) => HTML_ESCAPES[c] ?? c);
}
function firstFontFamily(stack) {
  const match = stack.match(/^\s*['"]([^'"]+)['"]/);
  return match ? match[1] : null;
}
function googleFontsHref(displayFont, bodyFont) {
  const families = [firstFontFamily(displayFont), firstFontFamily(bodyFont)].filter((f) => f !== null);
  if (families.length === 0) return null;
  const params = families.map((name) => `family=${encodeURIComponent(name)}:wght@400;500;600`).join("&");
  return `https://fonts.googleapis.com/css2?${params}&display=swap`;
}
function buildHoldingPage(brand) {
  const safeName = escapeHtml(brand.productName);
  const fontsHref = googleFontsHref(brand.displayFont, brand.bodyFont);
  const fontsLink = fontsHref ? `<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link rel="stylesheet" href="${escapeHtml(fontsHref)}">` : "";
  const logoBlock = brand.logoDataUri ? `<img class="logo" src="${brand.logoDataUri}" alt="${safeName}">` : "";
  const headline = brand.logoContainsName && brand.logoDataUri ? '<p class="sub">Starting</p>' : `<p class="name">${safeName} is starting</p>`;
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>${safeName}</title>
  ${fontsLink}
  <style>
    html, body { margin: 0; padding: 0; height: 100%; }
    body {
      font-family: ${brand.bodyFont};
      background: ${brand.background};
      color: ${brand.textColor};
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;
      gap: 1.25rem;
    }
    .logo { display: block; max-width: 240px; max-height: 120px; width: auto; height: auto; }
    .name { font-family: ${brand.displayFont}; font-size: 2rem; font-weight: 500; margin: 0; letter-spacing: -0.01em; }
    .sub { font-family: ${brand.bodyFont}; font-size: 0.95rem; color: ${brand.textColor}; opacity: 0.55; margin: 0; letter-spacing: 0.04em; text-transform: uppercase; }
    .dots { display: flex; gap: 6px; align-items: center; }
    .dot {
      display: inline-block; width: 6px; height: 6px; border-radius: 50%;
      background: currentColor; opacity: 0.3;
      animation: pulse 1.4s infinite ease-in-out;
    }
    .dot:nth-child(2) { animation-delay: 0.2s; }
    .dot:nth-child(3) { animation-delay: 0.4s; }
    @keyframes pulse { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 0.85; } }
  </style>
</head>
<body>
  ${logoBlock}
  ${headline}
  <p class="dots"><span class="dot"></span><span class="dot"></span><span class="dot"></span></p>
  <script>
    (function () {
      var attempts = 0;
      var MAX = 120;
      function poll() {
        if (attempts++ >= MAX) return;
        fetch('/api/health', { cache: 'no-store' })
          .then(function (r) { if (r.ok) location.reload(); else setTimeout(poll, 1500); })
          .catch(function () { setTimeout(poll, 1500); });
      }
      setTimeout(poll, 1500);
    })();
  </script>
</body>
</html>
`;
}

// server/edge.ts
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT || "";
var BRAND_JSON_PATH = PLATFORM_ROOT2 ? join4(PLATFORM_ROOT2, "config", "brand.json") : "";
var BRAND_ASSETS_DIR = PLATFORM_ROOT2 ? join4(PLATFORM_ROOT2, "..", "server", "public", "brand") : "";
var BRAND = loadBrand(BRAND_JSON_PATH, BRAND_ASSETS_DIR);
var ALIAS_DOMAINS_PATH = join4(homedir(), BRAND.configDir, "alias-domains.json");
function loadAliasDomains() {
  try {
    if (!existsSync4(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
    const parsed = JSON.parse(readFileSync4(ALIAS_DOMAINS_PATH, "utf-8"));
    if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
    return new Set(parsed.filter((h) => typeof h === "string"));
  } catch {
    return /* @__PURE__ */ new Set();
  }
}
var aliasDomains = loadAliasDomains();
watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
  aliasDomains = loadAliasDomains();
  console.log(`[edge] alias-domains reloaded (${aliasDomains.size})`);
});
function isPublicHost(host) {
  return host.startsWith("public.") || aliasDomains.has(host);
}
var EDGE_PORT = requirePortEnv("EDGE_PORT", { tag: "edge" });
var EDGE_HOSTNAME = process.env.EDGE_HOSTNAME ?? "0.0.0.0";
var UPSTREAM_HOST = process.env.MAXY_UI_HOST ?? "127.0.0.1";
var UPSTREAM_PORT = requirePortEnv("MAXY_UI_PORT", { tag: "edge" });
var WEBSOCKIFY_HOST = process.env.WEBSOCKIFY_HOST ?? "127.0.0.1";
var WEBSOCKIFY_PORT2 = parseInt(process.env.WEBSOCKIFY_PORT ?? String(WEBSOCKIFY_PORT), 10);
var HOP_BY_HOP2 = /* @__PURE__ */ new Set([
  "connection",
  "keep-alive",
  "proxy-authenticate",
  "proxy-authorization",
  "te",
  "trailer",
  "transfer-encoding",
  "upgrade"
]);
function isAdminProxyPath(url) {
  const q = url.indexOf("?");
  const pathname = q === -1 ? url : url.slice(0, q);
  return pathname.startsWith("/api/admin/");
}
function forwardHttp(clientReq, clientRes) {
  const headers = {};
  for (const [name, value] of Object.entries(clientReq.headers)) {
    if (value == null) continue;
    if (HOP_BY_HOP2.has(name)) continue;
    headers[name] = value;
  }
  const existingXff = headers["x-forwarded-for"];
  const remote = clientReq.socket.remoteAddress ?? "";
  headers["x-forwarded-for"] = existingXff ? Array.isArray(existingXff) ? [...existingXff, remote].join(", ") : `${existingXff}, ${remote}` : remote;
  const reqUrl = clientReq.url ?? "";
  const logIngress = isAdminProxyPath(reqUrl);
  const ingressStart = Date.now();
  if (logIngress) {
    const cookieHeader = String(clientReq.headers.cookie ?? "");
    const hasRemoteSession = /(?:^|;\s*)__remote_session=/.test(cookieHeader);
    console.log(
      `[edge-admin] inbound method=${clientReq.method} path=${reqUrl} token=${hasRemoteSession ? "present" : "absent"} allowlist-branch=passthrough`
    );
    clientRes.on("close", () => {
      const status = clientRes.headersSent ? clientRes.statusCode : "aborted";
      console.log(
        `[edge-admin] outcome method=${clientReq.method} path=${reqUrl} status=${status} duration_ms=${Date.now() - ingressStart}`
      );
    });
  }
  const upstream = httpRequest({
    host: UPSTREAM_HOST,
    port: UPSTREAM_PORT,
    method: clientReq.method,
    path: clientReq.url,
    headers
  });
  upstream.on("response", (upstreamRes) => {
    clientRes.writeHead(upstreamRes.statusCode ?? 502, upstreamRes.statusMessage, upstreamRes.headers);
    upstreamRes.pipe(clientRes);
  });
  upstream.on("error", (err) => {
    const errClass = classifyUpstreamError(err);
    console.error(
      `[edge] upstream http error path=${clientReq.url} err=${err.message} err-class=${errClass} upstream=${UPSTREAM_HOST}:${UPSTREAM_PORT}`
    );
    if (clientRes.headersSent) {
      clientRes.destroy();
      return;
    }
    const accept = String(clientReq.headers["accept"] ?? "");
    const isHtmlNavigation = errClass === "econnrefused-coldstart" && clientReq.method === "GET" && accept.includes("text/html");
    if (isHtmlNavigation) {
      clientRes.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
      clientRes.end(buildHoldingPage(BRAND));
    } else {
      clientRes.writeHead(502, { "content-type": "text/plain" });
      clientRes.end(`Bad Gateway (${BRAND.productName} unavailable)`);
    }
  });
  clientReq.on("aborted", () => upstream.destroy());
  clientReq.pipe(upstream);
}
function forwardUpgrade(req, clientSocket, head) {
  const upstream = createConnection2({ host: UPSTREAM_HOST, port: UPSTREAM_PORT });
  upstream.setTimeout(5e3);
  upstream.once("connect", () => {
    upstream.setTimeout(0);
    const lines = [];
    lines.push(`${req.method ?? "GET"} ${req.url} HTTP/${req.httpVersion}`);
    lines.push(`host: ${UPSTREAM_HOST}:${UPSTREAM_PORT}`);
    for (const [name, value] of Object.entries(req.headers)) {
      if (name === "host") continue;
      if (HOP_BY_HOP2.has(name)) continue;
      if (value == null) continue;
      if (Array.isArray(value)) {
        for (const v of value) lines.push(`${name}: ${v}`);
      } else {
        lines.push(`${name}: ${value}`);
      }
    }
    const upgradeHeader = req.headers.upgrade;
    const connectionHeader = req.headers.connection;
    if (typeof upgradeHeader === "string") lines.push(`upgrade: ${upgradeHeader}`);
    if (typeof connectionHeader === "string") lines.push(`connection: ${connectionHeader}`);
    upstream.write(lines.join("\r\n") + "\r\n\r\n");
    if (head && head.length > 0) upstream.write(head);
    clientSocket.pipe(upstream);
    upstream.pipe(clientSocket);
  });
  const teardown = (_reason) => {
    clientSocket.destroy();
    upstream.destroy();
  };
  clientSocket.once("close", () => teardown("client-close"));
  upstream.once("close", () => teardown("upstream-close"));
  clientSocket.once("error", () => teardown("client-error"));
  upstream.once("error", (err) => {
    console.error(`[edge] upstream upgrade error path=${req.url} err=${err.message}`);
    try {
      clientSocket.write("HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
    } catch {
    }
    teardown("upstream-error");
  });
  upstream.once("timeout", () => {
    console.error(`[edge] upstream upgrade timeout path=${req.url}`);
    try {
      clientSocket.write("HTTP/1.1 504 Gateway Timeout\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
    } catch {
    }
    teardown("upstream-timeout");
  });
}
var edgeAdminApp = createEdgeAdminApp({ isPublicHost });
var edgeAdminHandler = getRequestListener(edgeAdminApp.fetch);
function isEdgeAdminPath(url) {
  const q = url.indexOf("?");
  const pathname = q === -1 ? url : url.slice(0, q);
  return pathname.startsWith("/api/admin/actions") || pathname.startsWith("/api/admin/version");
}
var server = createServer((req, res) => {
  if (isEdgeAdminPath(req.url ?? "")) {
    edgeAdminHandler(req, res);
    return;
  }
  forwardHttp(req, res);
});
server.keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT ?? "61000", 10);
server.headersTimeout = server.keepAliveTimeout + 1e3;
attachVncWsProxy(server, {
  isPublicHost,
  upstreamHost: WEBSOCKIFY_HOST,
  upstreamPort: WEBSOCKIFY_PORT2,
  brand: BRAND.productName
});
server.on("upgrade", (req, socket, head) => {
  const url = req.url ?? "";
  const qsIndex = url.indexOf("?");
  const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
  if (pathname === "/websockify") return;
  forwardUpgrade(req, socket, head);
});
server.listen(EDGE_PORT, EDGE_HOSTNAME, () => {
  console.log(`[edge] listening on http://${EDGE_HOSTNAME}:${EDGE_PORT}`);
  console.log(`[edge] brand=${BRAND.productName} holding-logo=${BRAND.logoDataUri ? "inlined" : "missing"} assets-dir=${BRAND_ASSETS_DIR || "(none)"}`);
  console.log(`[edge] /websockify \u2192 ${WEBSOCKIFY_HOST}:${WEBSOCKIFY_PORT2}`);
  console.log(`[edge] everything else \u2192 ${UPSTREAM_HOST}:${UPSTREAM_PORT}`);
});
