import { randomBytes } from "node:crypto"; import type { InstallContext, InstallOptions } from "../types/context"; import { exists, readText, writeText } from "../lib/fs"; import { runVisible } from "../lib/process"; import { step } from "../lib/log"; import { parseRuntimeEnv, renderRuntimeEnv } from "../config/env"; import { preflight } from "../steps/preflight"; import { installDeps } from "../steps/deps"; import { prepareFilesystem } from "../steps/filesystem"; import { deployUi } from "../steps/ui"; import { installHysteria } from "../steps/hysteria"; 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"; async function readPackageValue(packageDir: string, file: string, fallback: string): Promise { try { return (await readText(`${packageDir}/metadata/${file}`)).trim(); } catch { return fallback; } } function secret(): string { return randomBytes(24).toString("base64url"); } export async function install(options: InstallOptions): Promise { const hasConfig = await exists(options.configPath); const sourceConfigPath = hasConfig ? options.configPath : `${options.packageDir}/config/hy2xs.env`; const sourceConfigRaw = await readText(sourceConfigPath); const config = parseRuntimeEnv(sourceConfigRaw); const context: InstallContext = { options, config, packageVersion: await readPackageValue(options.packageDir, "package.version", "unknown"), packageBuildId: await readPackageValue(options.packageDir, "package.build_id", "unknown"), installDate: new Date().toISOString(), hysteriaAuthPassword: secret(), hysteriaVersion: "unknown" }; step("preflight"); await preflight(context); step("system dependencies"); await installDeps(context); step("filesystem"); await prepareFilesystem(context); if (!hasConfig) { step("write runtime env"); await runVisible`mkdir -p /etc/hy2xs`; await writeText(options.configPath, renderRuntimeEnv(config), 0o600); } step("bundled UI"); await deployUi(context); step("Hysteria2 upstream install"); await installHysteria(context); step("config generation"); await generateConfig(context); step("systemd units"); await deploySystemd(context); step("firewall"); await applyFirewall(context); step("post-install env"); await writePostInstallEnv(context); step("smoke checks"); await smoke(context); }