#!/usr/bin/env node
import fs from "node:fs/promises";
import http from "node:http";
import path from "node:path";
import {
beginAdminOAuth,
clearAdminSessionCookie,
finishAdminOAuth,
getAdminSession,
getLocalBypassAdminSession,
isAdminUser,
} from "./admin-auth.mjs";
import { EncryptedPoolStore } from "./encrypted-pool-store.mjs";
import { createApiPoolProxyService } from "../scripts/api-pool-proxy.mjs";
import { createProxyService } from "../scripts/codex-local-proxy.mjs";
import { redactSensitiveText, sanitizeForLogs } from "../shared/secret-sanitizer.mjs";
const REPO_ROOT = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..", "..");
const DEFAULT_HOST = "0.0.0.0";
const DEFAULT_PORT = Number(process.env.PORT || 7860);
const DEFAULT_STATIC_DIR = path.join(REPO_ROOT, "dist", "ui");
const MAX_LOG_ENTRIES = 500;
const REMOTE_TOOL_DEFINITIONS = [
{
id: "pool.manage",
tabTitle: "池管理",
description: "查看、导入并保存到加密池存储,支持 private hf dataset,修改后手动 reload 生效。",
argsSchema: [],
defaults: {},
virtual: true,
remoteManaged: true,
},
{
id: "proxy.start",
tabTitle: "Codex 账号池代理",
description: "Hugging Face 常驻托管的 Codex 账号池代理,仅支持查看状态与手动 reload。",
argsSchema: [],
defaults: {},
virtual: true,
remoteManaged: true,
},
{
id: "api-pool.start",
tabTitle: "API 池代理",
description: "Hugging Face 常驻托管的 API 池代理,仅支持查看状态与手动 reload。",
argsSchema: [],
defaults: {},
virtual: true,
remoteManaged: true,
},
];
function nowIso() {
return new Date().toISOString();
}
function parseArgs(argv) {
const args = {};
for (const part of argv) {
if (!part.startsWith("--")) continue;
const raw = part.slice(2);
const idx = raw.indexOf("=");
if (idx < 0) {
args[raw] = "true";
continue;
}
args[raw.slice(0, idx)] = raw.slice(idx + 1);
}
return args;
}
function json(res, statusCode, value) {
res.statusCode = statusCode;
res.setHeader("content-type", "application/json; charset=utf-8");
res.end(`${JSON.stringify(value, null, 2)}\n`);
}
function html(res, statusCode, body) {
res.statusCode = statusCode;
res.setHeader("content-type", "text/html; charset=utf-8");
res.end(body);
}
async function readJsonBody(req) {
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const text = Buffer.concat(chunks).toString("utf8").trim();
if (!text) return {};
return JSON.parse(text);
}
function contentTypeFor(filePath) {
if (filePath.endsWith(".html")) return "text/html; charset=utf-8";
if (filePath.endsWith(".js")) return "text/javascript; charset=utf-8";
if (filePath.endsWith(".css")) return "text/css; charset=utf-8";
if (filePath.endsWith(".json")) return "application/json; charset=utf-8";
if (filePath.endsWith(".svg")) return "image/svg+xml";
if (filePath.endsWith(".png")) return "image/png";
return "application/octet-stream";
}
async function fileExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
async function serveAdminStatic(req, res, staticDir) {
const url = new URL(req.url || "/", "http://localhost");
const pathname = url.pathname;
const adminPath = pathname === "/admin" ? "/index.html" : pathname;
const relativePath = adminPath.startsWith("/admin/") ? adminPath.slice("/admin".length) : adminPath;
const requested = path.join(staticDir, relativePath);
const resolved = path.resolve(requested);
const staticRoot = path.resolve(staticDir);
if (!resolved.startsWith(staticRoot)) {
json(res, 403, { error: "Forbidden" });
return true;
}
const filePath = (await fileExists(resolved)) ? resolved : path.join(staticDir, "index.html");
if (!(await fileExists(filePath))) {
json(res, 500, { error: "Admin UI is not built. Run npm run ui:build first." });
return true;
}
const body = await fs.readFile(filePath);
res.statusCode = 200;
res.setHeader("content-type", contentTypeFor(filePath));
res.end(body);
return true;
}
function formatLogLine(event, payload = {}) {
const sanitized = sanitizeForLogs(payload);
const details = Object.entries(sanitized)
.filter(([, value]) => value !== undefined && value !== null && value !== "")
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
.join(" ");
return `[startup] ${nowIso()} ${event}${details ? ` ${details}` : ""}`;
}
function createLogBuffer(label) {
const logs = [];
function append(stream, text) {
const normalized = String(text || "").replace(/\r\n/g, "\n");
for (const line of normalized.split("\n")) {
if (!line) continue;
logs.push({
timestamp: nowIso(),
stream,
text: redactSensitiveText(`${label}: ${line}`),
});
}
if (logs.length > MAX_LOG_ENTRIES) {
logs.splice(0, logs.length - MAX_LOG_ENTRIES);
}
}
return {
append,
logger(event, payload = {}) {
append("stdout", formatLogLine(event, payload));
},
list() {
return [...logs];
},
};
}
function unauthorizedJson(res) {
json(res, 401, { error: "Unauthorized" });
}
function requireAdmin(req, res, env) {
const session =
getLocalBypassAdminSession(env) || getAdminSession(req, env.ADMIN_SESSION_SECRET);
if (!session || !isAdminUser(session, env)) {
unauthorizedJson(res);
return null;
}
return session;
}
function requireProxyAuth(req, expectedKey) {
if (!expectedKey) return false;
return String(req.headers.authorization || "") === `Bearer ${expectedKey}`;
}
function unavailable(res, message) {
json(res, 503, {
error: {
message,
},
});
}
function renderLoginPage(env = process.env) {
const spaceHost = env.SPACE_HOST ? `https://${env.SPACE_HOST}` : "";
const loginHref = `${spaceHost}/oauth/start`;
return `
登录管理台
登录 Hugging Face 管理台
为了避免 Spaces 嵌入页里的 cookie / state 问题,登录流程会在新标签页中打开。
如果你是从 huggingface.co/spaces/... 页面进入,请点击下面的按钮,在新标签页完成登录。
`;
}
function normalizeApiPoolRuntimeConfig(value, defaults = {}) {
const enableScheduledSwitch =
value?.enableScheduledSwitch == null
? Boolean(defaults.enableScheduledSwitch)
: Boolean(value.enableScheduledSwitch);
const intervalSource =
value?.scheduledSwitchIntervalMs == null
? defaults.scheduledSwitchIntervalMs
: value.scheduledSwitchIntervalMs;
const scheduledSwitchIntervalMs = Math.max(1_000, Number(intervalSource || 900000));
return {
enableScheduledSwitch,
scheduledSwitchIntervalMs,
};
}
class ManagedRemoteService {
constructor({ id, endpoint, authEnvName, apiKey, logLabel, createService }) {
this.id = id;
this.endpoint = endpoint;
this.authEnvName = authEnvName;
this.apiKey = apiKey;
this.createService = createService;
this.logBuffer = createLogBuffer(logLabel);
this.service = null;
this.lastError = "";
}
async load({ initial = false } = {}) {
try {
const nextService = await this.createService(this.logBuffer.logger);
this.service?.close?.();
this.service = nextService;
this.lastError = "";
this.logBuffer.append("stdout", initial ? "service initialized" : "service reloaded");
} catch (error) {
this.lastError = error?.message || String(error);
this.logBuffer.append("stderr", this.lastError);
if (!initial) {
throw error;
}
}
}
async reload() {
await this.load({ initial: false });
return this.getStatus();
}
async handle(req, res, { requestUrl }) {
if (!requireProxyAuth(req, this.apiKey)) {
unauthorizedJson(res);
return;
}
if (!this.service) {
unavailable(res, this.lastError || "Service is not ready.");
return;
}
return this.service.handleRequest(req, res, {
requestUrl,
exposeHealthDetails: false,
exposeStatus: false,
});
}
getStatus() {
return {
running: Boolean(this.service),
endpoint: this.endpoint,
authEnvName: this.authEnvName,
lastError: this.lastError || "",
recentLogs: this.logBuffer.list(),
health: {
ok: true,
status: 200,
body: { ok: true },
},
proxyStatus: this.service
? {
ok: true,
status: 200,
body: this.service.getAdminStatus(),
}
: {
ok: false,
status: 503,
body: {
error: this.lastError || "Service is not ready.",
},
},
};
}
}
export async function createHfSpaceServer({
env = process.env,
staticDir = DEFAULT_STATIC_DIR,
accountFetchFn,
codexApiFetchFn,
claudeApiFetchFn,
} = {}) {
const dataDir = path.resolve(env.DATA_DIR || path.join(REPO_ROOT, ".hf-data"));
const poolStore = new EncryptedPoolStore({
dataDir,
cryptoKey: env.POOL_CRYPTO_KEY,
storageBackend: env.POOL_STORAGE_BACKEND || (env.HF_DATASET_REPO ? "hf-dataset" : "local-fs"),
hfDatasetRepo: env.HF_DATASET_REPO,
hfToken: env.HF_TOKEN,
hfDatasetBranch: env.HF_DATASET_BRANCH || "main",
});
await poolStore.init();
const proxyOptions = {
upstreamBase: env.CODEX_PROXY_UPSTREAM_BASE || "https://chatgpt.com/backend-api/codex",
refreshEndpoint: env.CODEX_PROXY_REFRESH_ENDPOINT || "https://auth.openai.com/oauth/token",
probeUrl:
env.CODEX_PROXY_PROBE_URL ||
"https://chatgpt.com/backend-api/codex/models?client_version=0.117.0",
maxSwitchAttempts: Number(env.CODEX_PROXY_MAX_SWITCH_ATTEMPTS || 5),
requestTimeoutMs: Number(env.CODEX_PROXY_REQUEST_TIMEOUT_MS || 60000),
proxyUrl: env.CODEX_PROXY_UPSTREAM_PROXY || env.HTTPS_PROXY || env.HTTP_PROXY || "",
clientVersion: env.CODEX_PROXY_CLIENT_VERSION || "0.117.0",
};
const apiProxyOptions = {
maxSwitchAttempts: Number(env.API_POOL_MAX_SWITCH_ATTEMPTS || 5),
requestTimeoutMs: Number(env.API_POOL_REQUEST_TIMEOUT_MS || 60000),
enableScheduledSwitch: env.API_POOL_SCHEDULED_SWITCH_ENABLED ?? "true",
scheduledSwitchIntervalMs: Number(
env.API_POOL_SCHEDULED_SWITCH_INTERVAL_MS || 900000,
),
proxyUrl: env.API_POOL_PROXY_URL || env.HTTPS_PROXY || env.HTTP_PROXY || "",
};
const apiPoolRuntimeConfigDefaults = normalizeApiPoolRuntimeConfig({
enableScheduledSwitch: apiProxyOptions.enableScheduledSwitch,
scheduledSwitchIntervalMs: apiProxyOptions.scheduledSwitchIntervalMs,
});
let apiPoolRuntimeConfig = await poolStore.loadRuntimeConfig(
"api-pool-runtime",
apiPoolRuntimeConfigDefaults,
);
apiPoolRuntimeConfig = normalizeApiPoolRuntimeConfig(
apiPoolRuntimeConfig,
apiPoolRuntimeConfigDefaults,
);
const services = {
"codex-account": new ManagedRemoteService({
id: "codex-account",
endpoint: "/proxy/codex-account",
authEnvName: "CODEX_ACCOUNT_PROXY_KEY",
apiKey: env.CODEX_ACCOUNT_PROXY_KEY || "",
logLabel: "codex-account",
createService: (logger) =>
createProxyService({
...proxyOptions,
localApiKey: env.CODEX_ACCOUNT_PROXY_KEY || "",
fetchFn: accountFetchFn,
logger,
sourcePath: "encrypted://codex-accounts",
loadSnapshot: async () => ({
entries: await poolStore.loadRawPoolItems("codex-accounts"),
sourcePath: "encrypted://codex-accounts",
}),
saveSnapshot: async (entries) => {
await poolStore.writeEncryptedPool("codex-accounts", entries);
},
}),
}),
"codex-api": new ManagedRemoteService({
id: "codex-api",
endpoint: "/proxy/codex-api",
authEnvName: "CODEX_API_PROXY_KEY",
apiKey: env.CODEX_API_PROXY_KEY || "",
logLabel: "codex-api",
createService: (logger) =>
createApiPoolProxyService({
...apiProxyOptions,
enableScheduledSwitch: apiPoolRuntimeConfig.enableScheduledSwitch,
scheduledSwitchIntervalMs: apiPoolRuntimeConfig.scheduledSwitchIntervalMs,
provider: "codex",
localApiKey: env.CODEX_API_PROXY_KEY || "",
fetchFn: codexApiFetchFn,
logger,
sourcePath: "encrypted://codex-api",
loadSnapshot: async () => ({
entries: await poolStore.loadRawPoolItems("codex-api"),
sourcePath: "encrypted://codex-api",
}),
}),
}),
"claude-api": new ManagedRemoteService({
id: "claude-api",
endpoint: "/proxy/claude-api",
authEnvName: "CLAUDE_API_PROXY_KEY",
apiKey: env.CLAUDE_API_PROXY_KEY || "",
logLabel: "claude-api",
createService: (logger) =>
createApiPoolProxyService({
...apiProxyOptions,
enableScheduledSwitch: apiPoolRuntimeConfig.enableScheduledSwitch,
scheduledSwitchIntervalMs: apiPoolRuntimeConfig.scheduledSwitchIntervalMs,
provider: "claude-code",
localApiKey: env.CLAUDE_API_PROXY_KEY || "",
fetchFn: claudeApiFetchFn,
logger,
sourcePath: "encrypted://claude-code-api",
loadSnapshot: async () => ({
entries: await poolStore.loadRawPoolItems("claude-code-api"),
sourcePath: "encrypted://claude-code-api",
}),
}),
}),
};
await Promise.all(Object.values(services).map((service) => service.load({ initial: true })));
const server = http.createServer(async (req, res) => {
try {
const url = new URL(req.url || "/", "http://localhost");
const pathname = url.pathname;
if (req.method === "GET" && pathname === "/healthz") {
json(res, 200, { ok: true });
return;
}
if (req.method === "GET" && pathname === "/") {
res.statusCode = 302;
res.setHeader("location", "/admin");
res.end();
return;
}
if (req.method === "GET" && pathname === "/login") {
html(res, 200, renderLoginPage(env));
return;
}
if (req.method === "GET" && pathname === "/oauth/start") {
await beginAdminOAuth(req, res, env);
return;
}
if (req.method === "GET" && pathname === "/login/callback") {
await finishAdminOAuth(req, res, env);
return;
}
if (req.method === "POST" && pathname === "/logout") {
res.statusCode = 204;
res.setHeader("set-cookie", clearAdminSessionCookie());
res.end();
return;
}
if (pathname.startsWith("/proxy/codex-account/")) {
const requestUrl = pathname.slice("/proxy/codex-account".length) + url.search;
await services["codex-account"].handle(req, res, { requestUrl });
return;
}
if (pathname.startsWith("/proxy/codex-api/")) {
const requestUrl = pathname.slice("/proxy/codex-api".length) + url.search;
await services["codex-api"].handle(req, res, { requestUrl });
return;
}
if (pathname.startsWith("/proxy/claude-api/")) {
const requestUrl = pathname.slice("/proxy/claude-api".length) + url.search;
await services["claude-api"].handle(req, res, { requestUrl });
return;
}
if (pathname.startsWith("/admin/api/")) {
const session = requireAdmin(req, res, env);
if (!session) return;
if (req.method === "GET" && pathname === "/admin/api/app-config") {
json(res, 200, {
mode: "remote",
apiBase: "/admin/api",
environment:
poolStore.storageBackend === "hf-dataset"
? "Hugging Face Space + private dataset"
: "Hugging Face Space + local storage",
user: session,
readOnly: poolStore.readOnly,
readOnlyReason: poolStore.readOnlyReason,
storageBackend: poolStore.storageBackend,
dataDir: poolStore.dataDir,
});
return;
}
if (req.method === "GET" && pathname === "/admin/api/tools") {
json(res, 200, { tools: REMOTE_TOOL_DEFINITIONS });
return;
}
if (req.method === "GET" && pathname === "/admin/api/history") {
json(res, 200, { items: [] });
return;
}
if (req.method === "GET" && pathname === "/admin/api/pools") {
json(res, 200, { items: poolStore.listPools() });
return;
}
if (req.method === "GET" && pathname.startsWith("/admin/api/pools/")) {
const poolId = pathname.split("/")[4];
json(res, 200, await poolStore.loadPool(poolId));
return;
}
if (
req.method === "POST" &&
pathname.startsWith("/admin/api/pools/") &&
pathname.endsWith("/validate")
) {
const poolId = pathname.split("/")[4];
const body = await readJsonBody(req);
const result = await poolStore.validatePoolItems(poolId, body.items || []);
json(res, result.ok ? 200 : 400, result);
return;
}
if (
req.method === "POST" &&
pathname.startsWith("/admin/api/pools/") &&
pathname.endsWith("/import")
) {
const poolId = pathname.split("/")[4];
const body = await readJsonBody(req);
json(res, 200, await poolStore.importPool(poolId, body.items || []));
return;
}
if (req.method === "PUT" && pathname.startsWith("/admin/api/pools/")) {
const poolId = pathname.split("/")[4];
const body = await readJsonBody(req);
json(res, 200, await poolStore.savePool(poolId, body.items || []));
return;
}
if (req.method === "POST" && pathname === "/admin/api/reload") {
const results = {};
for (const [serviceId, service] of Object.entries(services)) {
try {
results[serviceId] = await service.reload();
} catch (error) {
results[serviceId] = service.getStatus();
results[serviceId].lastError = error?.message || String(error);
}
}
json(res, 200, { ok: true, results });
return;
}
if (req.method === "GET" && pathname === "/admin/api/api-pool/config") {
json(res, 200, apiPoolRuntimeConfig);
return;
}
if (req.method === "PUT" && pathname === "/admin/api/api-pool/config") {
const body = await readJsonBody(req);
apiPoolRuntimeConfig = normalizeApiPoolRuntimeConfig(
body,
apiPoolRuntimeConfigDefaults,
);
await poolStore.saveRuntimeConfig("api-pool-runtime", apiPoolRuntimeConfig);
const serviceId = body?.provider === "claude-code" ? "claude-api" : "codex-api";
const status = await services[serviceId].reload();
json(res, 200, {
ok: true,
config: apiPoolRuntimeConfig,
status,
});
return;
}
if (req.method === "GET" && pathname === "/admin/api/proxy/status") {
json(res, 200, services["codex-account"].getStatus());
return;
}
if (req.method === "GET" && pathname === "/admin/api/api-pool/codex/status") {
json(res, 200, services["codex-api"].getStatus());
return;
}
if (req.method === "GET" && pathname === "/admin/api/api-pool/claude-code/status") {
json(res, 200, services["claude-api"].getStatus());
return;
}
if (req.method === "GET" && pathname.startsWith("/admin/api/status/")) {
const serviceId = pathname.split("/")[4];
const service = services[serviceId];
if (!service) {
json(res, 404, { error: "Unknown service" });
return;
}
json(res, 200, service.getStatus());
return;
}
json(res, 404, { error: "Not found" });
return;
}
if (pathname === "/admin" || pathname.startsWith("/admin/")) {
const session =
getLocalBypassAdminSession(env) || getAdminSession(req, env.ADMIN_SESSION_SECRET);
if (!session || !isAdminUser(session, env)) {
html(res, 401, renderLoginPage(env));
return;
}
await serveAdminStatic(req, res, staticDir);
return;
}
if (pathname.startsWith("/assets/")) {
await serveAdminStatic(req, res, staticDir);
return;
}
json(res, 404, { error: "Not found" });
} catch (error) {
json(res, error.statusCode || 500, {
error: error?.message || String(error),
});
}
});
return {
server,
services,
poolStore,
};
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const host = args.host || process.env.HF_SERVER_HOST || DEFAULT_HOST;
const port = Number(args.port || process.env.PORT || DEFAULT_PORT);
const staticDir = path.resolve(args["static-dir"] || process.env.HF_STATIC_DIR || DEFAULT_STATIC_DIR);
const { server } = await createHfSpaceServer({ staticDir });
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(port, host, () => {
server.off("error", reject);
console.log(`HF secure proxy server listening on http://${host}:${port}`);
console.log(`Admin path: /admin`);
resolve();
});
});
}
const directRun = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname);
if (directRun) {
main().catch((error) => {
console.error(error?.message || error);
process.exitCode = 1;
});
}