import type { ReconfigureContext, ReconfigureOptions } from "../types/context"; import { readText, writeText } from "../lib/fs"; import { info, step } from "../lib/log"; import { parseRuntimeEnv, renderRuntimeEnv } from "../config/env"; import { preflight } from "../steps/preflight"; import { generateConfig } from "../steps/config"; import { deploySystemd } from "../steps/systemd"; import { applyFirewall } from "../steps/firewall"; import { writePostInstallEnv } from "../steps/env"; import { smoke } from "../steps/smoke"; import { runVisible } from "../lib/process"; async function backupCurrentState(): Promise { await runVisible`mkdir -p /etc/hy2xs/backups`; await runVisible`cp -a /etc/hysteria/config.yaml /etc/hy2xs/backups/config.yaml.bak 2>/dev/null || true`; await runVisible`cp -a /etc/systemd/system/hy2xs-admin.service /etc/hy2xs/backups/hy2xs-admin.service.bak 2>/dev/null || true`; await runVisible`cp -a /etc/systemd/system/hysteria-server.service /etc/hy2xs/backups/hysteria-server.service.bak 2>/dev/null || true`; await runVisible`cp -a /etc/nftables.d/hy2xs.nft /etc/hy2xs/backups/hy2xs.nft.bak 2>/dev/null || true`; } async function rollbackCurrentState(): Promise { await runVisible`cp -a /etc/hy2xs/backups/config.yaml.bak /etc/hysteria/config.yaml 2>/dev/null || true`; await runVisible`cp -a /etc/hy2xs/backups/hy2xs-admin.service.bak /etc/systemd/system/hy2xs-admin.service 2>/dev/null || true`; await runVisible`cp -a /etc/hy2xs/backups/hysteria-server.service.bak /etc/systemd/system/hysteria-server.service 2>/dev/null || true`; await runVisible`cp -a /etc/hy2xs/backups/hy2xs.nft.bak /etc/nftables.d/hy2xs.nft 2>/dev/null || true`; await runVisible`systemctl daemon-reload`; await runVisible`systemctl restart hysteria-server hy2xs-admin || true`; } export async function reconfigure(options: ReconfigureOptions): Promise { const configRaw = await readText(options.sourceConfigPath); const config = parseRuntimeEnv(configRaw); const context: ReconfigureContext & { packageVersion: string; packageBuildId: string; installDate: string; hysteriaAuthPassword: string; hysteriaVersion: string } = { options, config, packageVersion: "reconfigure", packageBuildId: "reconfigure", installDate: new Date().toISOString(), hysteriaAuthPassword: "managed-by-ui-auth", hysteriaVersion: "unknown" }; step("preflight"); await preflight(context); if (options.dryRun) { info("reconfigure dry-run: validated config and execution graph"); info(`config source: ${options.sourceConfigPath}`); info(`runtime file: ${options.runtimeConfigPath}`); info(`ui bind: ${config.uiBindHost}:${config.uiPort}`); info(`hysteria bind: ${config.hysteriaBindHost}:${config.hysteriaPort}`); info(`public endpoint: ${config.publicHost}:${config.publicPort}`); return; } step("backup"); await backupCurrentState(); try { step("config generation"); await generateConfig(context); step("systemd units"); await deploySystemd(context); step("firewall"); await applyFirewall(context); step("write env artifacts"); await writeText(options.runtimeConfigPath, renderRuntimeEnv(config), 0o600); await writePostInstallEnv(context); step("smoke checks"); await smoke(context); } catch (error) { info("reconfigure failed, rollback in progress"); await rollbackCurrentState(); throw error; } }