/*
 * ════════════════════════════════════════════════════════════════
 * 光湖 · 自部署密钥管理页 (Secrets Vault) · Express 后端入口
 * Sovereign: TCS-0002∞ · ICE-GL∞ · 国作登字-2026-A-00037559
 * 守护: 铸渊 · ICE-GL-ZY001
 * 模板版本: 0.1.0
 * ════════════════════════════════════════════════════════════════
 *
 * 服务器: GH-CVM-DOMAIN-PROD-01 / ZY-SVR-CN01 / 广州 2C2G
 * 角色:   guanghulab.com 域内 /admin/secrets 子站
 *
 * 设计理念:
 *   cc-003 · AutoDL host:port 不入 GitHub Secrets, 改入本地 vault
 *           1 步保存立即生效 (vs Github Secrets 5 步 round-trip).
 *
 *   cc-004 · 全中文 UI · 字段名走"用途"不走 secret 名
 *           Awen 在浏览器里能直接读懂, 不用记 ZY_AUTODL_HOST 是干啥的.
 *
 *   2C2G 现实:
 *           Express 单进程, pm2 max_memory_restart=128M
 *           内存预算: portal=512M + vault=128M + nginx=~64M + buffer = 不到 1G,
 *           留一半给系统. 跟 baton-005 A 节一致.
 *
 *   公网隔离:
 *           本服务只监听 127.0.0.1:VAULT_PORT (默认 8080)
 *           nginx /admin/ location 只允许 127.0.0.1 + basic-auth
 *           冰朔走 ssh -L 8080:127.0.0.1:8080 + 浏览器 localhost 访问
 *           **永不暴露公网**
 *
 *   主密钥管理:
 *           首次启动随机生成 → /data/guanghulab/secrets-vault/.master chmod 600
 *           vault.enc 也 chmod 600
 *           **不上 git, 不上 COS, 不传冰朔** (cc-004 系统自管, 不让冰朔记)
 *
 * 路由:
 *   GET    /admin/secrets                  按工作流分组列出 + 遮罩值
 *   GET    /admin/secrets/manifest         前端用的元数据 (用途/如何配置)
 *   POST   /admin/secrets/:key             写入 {value}
 *   DELETE /admin/secrets/:key             删除
 *   POST   /admin/secrets/autodl/save_and_refresh
 *                                          AutoDL 一键: vault 落 + 探活 + 写
 *                                          inference-endpoint.json + pm2 reload portal
 *
 * 内部 (本机) 路由 (神笔马良用):
 *   GET    /internal/fetch/:key            127.0.0.1 only · 拉明文
 *
 * 静态:
 *   /admin/static/*  → frontend/secrets-vault/assets
 *   /admin/          → frontend/secrets-vault/index.html
 *
 * 健康检查:
 *   GET    /admin/__healthz                {ok, vault_loaded, has_endpoint_path}
 * ════════════════════════════════════════════════════════════════
 */
"use strict";

const path = require("path");
const fs = require("fs");
const express = require("express");

const { Vault } = require("./lib/vault");
const { buildRouter } = require("./routes/secrets");
const { buildInternalRouter } = require("./routes/internal");
const { createRateLimit } = require("./lib/rate-limit");

// ─── 配置 ─────────────────────────────────────────────────────
const PORT = parseInt(process.env.PORT || "8080", 10);
const HOST = process.env.HOST || "127.0.0.1"; // 严格本机
const VAULT_DIR = process.env.VAULT_DIR || "/data/guanghulab/secrets-vault";
const PORTAL_DATA_DIR = process.env.PORTAL_DATA_DIR || "/data/guanghulab/portal/data";
const ENDPOINT_PATH =
  process.env.INFERENCE_ENDPOINT_PATH || path.join(PORTAL_DATA_DIR, "inference-endpoint.json");
const REPO_ROOT = path.resolve(__dirname, "..", "..");
const STATIC_DIR =
  process.env.VAULT_STATIC_DIR || path.join(REPO_ROOT, "frontend", "secrets-vault");
const MANIFEST_PATH =
  process.env.VAULT_MANIFEST_PATH ||
  path.join(REPO_ROOT, "scripts", "preflight", "secrets-manifest.json");

// ─── Vault 启动 ────────────────────────────────────────────────
const vault = new Vault(VAULT_DIR);
const masterInfo = vault.loadOrCreateMaster();

// ─── Express ──────────────────────────────────────────────────
const app = express();
app.disable("x-powered-by");
// 不开 trust proxy: req.ip 直接拿 socket 地址, 给 internal 路由判本机用
// nginx 只是 hop, 我们这里只接受 127.0.0.1, 不需要 X-Forwarded-For

// ─── 健康检查 (basic-auth 之外, 给 nginx /__healthz 探活) ─────
app.get("/admin/__healthz", (_req, res) => {
  res.json({
    ok: true,
    server: "GH-CVM-DOMAIN-PROD-01",
    role: "secrets-vault",
    sovereign: "TCS-0002∞ · 国作登字-2026-A-00037559",
    vault_dir: VAULT_DIR,
    has_master: fs.existsSync(vault.masterPath),
    has_vault_enc: fs.existsSync(vault.encPath),
    endpoint_path: ENDPOINT_PATH,
    ts: new Date().toISOString()
  });
});

// ─── 限流 (纵深防御 · nginx 在前层也限) ────────────────────
// admin: 静态/路由用宽松限流; internal: 神笔马良调, 给紧一点
const adminLimit = createRateLimit({ windowMs: 60_000, max: 240 });
const internalLimit = createRateLimit({ windowMs: 60_000, max: 600 });

// ─── Admin 路由 (basic-auth 由 nginx 在前面拦, app 这层不再做) ─
app.use(
  "/admin/secrets",
  adminLimit,
  buildRouter({ vault, manifestPath: MANIFEST_PATH, endpointPath: ENDPOINT_PATH })
);

// ─── 静态 (前端) ───────────────────────────────────────────────
app.use(
  "/admin/static",
  adminLimit,
  express.static(path.join(STATIC_DIR, "assets"), { fallthrough: true })
);
app.get(["/admin", "/admin/"], adminLimit, (_req, res, next) => {
  const idx = path.join(STATIC_DIR, "index.html");
  fs.access(idx, fs.constants.R_OK, (err) => {
    if (err) return next();
    res.sendFile(idx);
  });
});

// ─── 内部 (本机) 路由 — 神笔马良 ────────────────────────────────
app.use("/internal", internalLimit, buildInternalRouter({ vault }));

// ─── 404 + 错误 ───────────────────────────────────────────────
app.use((req, res) => {
  if (req.path.startsWith("/admin/") || req.path.startsWith("/internal/")) {
    res
      .status(404)
      .json({ error: true, code: "not_found", message: "接口不存在: " + req.path });
  } else {
    res.status(404).type("text/plain").send("404");
  }
});

// eslint-disable-next-line no-unused-vars
app.use((err, req, res, _next) => {
  const msg = (err && err.message) || "未知错误";
  // eslint-disable-next-line no-console
  console.error("[vault] 内部错误:", err);
  if (res.headersSent) return;
  res
    .status(err && err.status ? err.status : 500)
    .json({ error: true, code: "internal_error", message: "内部错误: " + msg });
});

// ─── 启动 ─────────────────────────────────────────────────────
if (require.main === module) {
  app.listen(PORT, HOST, () => {
    // eslint-disable-next-line no-console
    console.log(
      `[vault] 已启动 · ${HOST}:${PORT} · vault_dir=${VAULT_DIR} · master_${masterInfo.created ? "created" : "loaded"}`
    );
    // eslint-disable-next-line no-console
    console.log(`[vault] 静态目录: ${STATIC_DIR}`);
    // eslint-disable-next-line no-console
    console.log(`[vault] 推理端点写入文件: ${ENDPOINT_PATH}`);
    // eslint-disable-next-line no-console
    console.log("[vault] 守护: 铸渊 · ICE-GL-ZY001 · TCS-0002∞ · 国作登字-2026-A-00037559");
  });
}

module.exports = { app, vault };
