import { randomBytes } from "node:crypto"; import type { InstallContext, InstallOptions } from "../types/context"; import { readText } from "../lib/fs"; import { step } from "../lib/log"; 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 context: InstallContext = { options, packageVersion: await readPackageValue(options.packageDir, "package.version", "unknown"), packageBuildId: await readPackageValue(options.packageDir, "package.build_id", "unknown"), installDate: new Date().toISOString(), hysteriaAuthPassword: secret(), hysteriaObfsPassword: secret(), hysteriaApiSecret: secret(), hysteriaApiPort: 36712, hysteriaVersion: "unknown" }; step("preflight"); await preflight(context); step("system dependencies"); await installDeps(context); step("filesystem"); await prepareFilesystem(context); 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); }