Подготовить HY2XS к production-сборке
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { readText, renderTemplate, writeText } from "../lib/fs";
|
||||
import { runVisible } from "../lib/process";
|
||||
|
||||
export async function generateConfig(context: InstallContext): Promise<void> {
|
||||
const template = await readText(`${context.options.packageDir}/templates/hysteria/config.yaml.tpl`);
|
||||
const rendered = renderTemplate(template, {
|
||||
HYSTERIA_PORT: context.options.port,
|
||||
HYSTERIA_AUTH_PASSWORD: context.hysteriaAuthPassword,
|
||||
HYSTERIA_OBFS_PASSWORD: context.hysteriaObfsPassword,
|
||||
HYSTERIA_API_PORT: context.hysteriaApiPort,
|
||||
HYSTERIA_API_SECRET: context.hysteriaApiSecret,
|
||||
UI_PORT: context.options.uiPort,
|
||||
BANDWIDTH_UP: "50 mbps",
|
||||
BANDWIDTH_DOWN: "50 mbps"
|
||||
});
|
||||
|
||||
await writeText("/etc/hysteria/config.yaml", rendered, 0o600);
|
||||
await runVisible`openssl req -x509 -newkey rsa:2048 -nodes -days 3650 -subj /CN=${context.options.domain || "hy2xs.local"} -keyout /etc/hysteria/server.key -out /etc/hysteria/server.crt`;
|
||||
await runVisible`chown hysteria:hysteria /etc/hysteria/config.yaml /etc/hysteria/server.key /etc/hysteria/server.crt`;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { runVisible } from "../lib/process";
|
||||
|
||||
export async function installDeps(_context: InstallContext): Promise<void> {
|
||||
await runVisible`apt-get update`;
|
||||
await runVisible`apt-get install -y ca-certificates curl iproute2 tar openssl nftables systemd`;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { readText, renderTemplate, writeText } from "../lib/fs";
|
||||
|
||||
export async function writePostInstallEnv(context: InstallContext): Promise<void> {
|
||||
const rendered = renderTemplate(await readText(`${context.options.packageDir}/templates/env/post-install.env.tpl`), {
|
||||
PACKAGE_VERSION: context.packageVersion,
|
||||
PACKAGE_BUILD_ID: context.packageBuildId,
|
||||
INSTALL_DATE: context.installDate,
|
||||
DOMAIN: context.options.domain,
|
||||
SSH_PORT: context.options.sshPort,
|
||||
HYSTERIA_VERSION: context.hysteriaVersion,
|
||||
HYSTERIA_PORT: context.options.port,
|
||||
HYSTERIA_OBFS_PASSWORD: context.hysteriaObfsPassword,
|
||||
HYSTERIA_API_PORT: context.hysteriaApiPort,
|
||||
UI_BIND_HOST: context.options.uiBindHost,
|
||||
UI_PORT: context.options.uiPort
|
||||
});
|
||||
|
||||
await writeText("/etc/hysteria/post-install.env", rendered, 0o600);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { runVisible } from "../lib/process";
|
||||
|
||||
export async function prepareFilesystem(_context: InstallContext): Promise<void> {
|
||||
await runVisible`id -u hysteria >/dev/null 2>&1 || useradd --system --home /var/lib/hysteria --shell /usr/sbin/nologin hysteria`;
|
||||
await runVisible`mkdir -p /etc/hysteria /var/lib/hysteria /opt/hy2xs-admin /var/lib/hy2xs-admin /var/log/hy2xs /usr/local/lib/hy2xs`;
|
||||
await runVisible`chown -R hysteria:hysteria /etc/hysteria /var/lib/hysteria`;
|
||||
await runVisible`chown -R root:root /var/lib/hy2xs-admin`;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { readText, renderTemplate, writeText } from "../lib/fs";
|
||||
import { info } from "../lib/log";
|
||||
import { runVisible } from "../lib/process";
|
||||
|
||||
export async function applyFirewall(context: InstallContext): Promise<void> {
|
||||
if (context.options.skipFirewall) {
|
||||
info("firewall skipped by flag");
|
||||
return;
|
||||
}
|
||||
|
||||
const rendered = renderTemplate(await readText(`${context.options.packageDir}/templates/nftables/hy2xs.nft.tpl`), {
|
||||
SSH_PORT: context.options.sshPort,
|
||||
HYSTERIA_PORT: context.options.port,
|
||||
UI_PORT: context.options.uiPort
|
||||
});
|
||||
|
||||
await runVisible`cp -a /etc/nftables.conf /etc/nftables.conf.hy2xs.bak 2>/dev/null || true`;
|
||||
await writeText("/etc/nftables.conf", rendered, 0o644);
|
||||
await runVisible`nft -f /etc/nftables.conf`;
|
||||
await runVisible`systemctl enable --now nftables`;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { run, runVisible } from "../lib/process";
|
||||
|
||||
export async function installHysteria(context: InstallContext): Promise<void> {
|
||||
await runVisible`curl -fsSL https://get.hy2.sh/ -o /tmp/hy2xs-install-hysteria.sh`;
|
||||
await runVisible`sh /tmp/hy2xs-install-hysteria.sh`;
|
||||
context.hysteriaVersion = await run`/usr/local/bin/hysteria version`;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { exists, readText } from "../lib/fs";
|
||||
import { fail } from "../lib/log";
|
||||
import { run } from "../lib/process";
|
||||
|
||||
async function isPortBusy(port: number): Promise<boolean> {
|
||||
try {
|
||||
const output = await run`ss -H -lntu`;
|
||||
return output.split("\n").some((line) => line.includes(`:${port} `) || line.endsWith(`:${port}`));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function preflight(context: InstallContext): Promise<void> {
|
||||
if (process.getuid?.() !== 0) {
|
||||
fail("installer must run as root");
|
||||
}
|
||||
|
||||
const osRelease = await readText("/etc/os-release");
|
||||
if (!/^ID=debian$/m.test(osRelease) || !/^VERSION_ID="?12"?$/m.test(osRelease)) {
|
||||
fail("HY2XS baseline supports only clean Debian 12");
|
||||
}
|
||||
|
||||
if (!(await exists(`${context.options.packageDir}/ui/hy2xs-admin`))) {
|
||||
fail("bundled HY2XS admin is missing from install package");
|
||||
}
|
||||
|
||||
if (await exists("/etc/hysteria/post-install.env")) {
|
||||
fail("existing HY2XS post-install.env found; update/repair is out of scope");
|
||||
}
|
||||
|
||||
if (await exists("/opt/hy2xs-admin")) {
|
||||
fail("existing /opt/hy2xs-admin found; conflicting old state");
|
||||
}
|
||||
|
||||
const ports = new Set([context.options.port, context.options.uiPort]);
|
||||
if (ports.size !== 2) {
|
||||
fail("Hysteria port and UI port must be different");
|
||||
}
|
||||
|
||||
if (context.options.domain && !/^[a-zA-Z0-9.-]+$/.test(context.options.domain)) {
|
||||
fail("domain contains unsupported characters");
|
||||
}
|
||||
|
||||
if (await isPortBusy(context.options.port)) {
|
||||
fail(`Hysteria UDP/TCP port already appears to be in use: ${context.options.port}`);
|
||||
}
|
||||
|
||||
if (await isPortBusy(context.options.uiPort)) {
|
||||
fail(`HY2XS admin port already appears to be in use: ${context.options.uiPort}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { info } from "../lib/log";
|
||||
import { runVisible } from "../lib/process";
|
||||
|
||||
export async function smoke(context: InstallContext): Promise<void> {
|
||||
if (context.options.skipStart) {
|
||||
info("service start and smoke checks skipped by flag");
|
||||
return;
|
||||
}
|
||||
|
||||
await runVisible`systemctl start hysteria-server hy2xs-admin`;
|
||||
await runVisible`systemctl is-active --quiet hysteria-server`;
|
||||
await runVisible`systemctl is-active --quiet hy2xs-admin`;
|
||||
await runVisible`/usr/local/bin/hysteria version`;
|
||||
await runVisible`test -s /etc/hysteria/config.yaml`;
|
||||
await runVisible`test -s /etc/hysteria/post-install.env`;
|
||||
await runVisible`ss -H -lntu | grep -q ':${context.options.uiPort} '`;
|
||||
await runVisible`curl -fsS --max-time 5 http://127.0.0.1:${context.options.uiPort}/ >/dev/null`;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { readText, renderTemplate, writeText } from "../lib/fs";
|
||||
import { runVisible } from "../lib/process";
|
||||
|
||||
export async function deploySystemd(context: InstallContext): Promise<void> {
|
||||
const values = {
|
||||
UI_BIND_HOST: context.options.uiBindHost,
|
||||
UI_PORT: context.options.uiPort
|
||||
};
|
||||
|
||||
const hysteriaUnit = await readText(`${context.options.packageDir}/systemd/hysteria-server.service`);
|
||||
const adminUnit = renderTemplate(await readText(`${context.options.packageDir}/systemd/hy2xs-admin.service`), values);
|
||||
|
||||
await writeText("/etc/systemd/system/hysteria-server.service", hysteriaUnit, 0o644);
|
||||
await writeText("/etc/systemd/system/hy2xs-admin.service", adminUnit, 0o644);
|
||||
await runVisible`systemctl daemon-reload`;
|
||||
await runVisible`systemctl enable hysteria-server hy2xs-admin`;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { runVisible } from "../lib/process";
|
||||
|
||||
export async function deployUi(context: InstallContext): Promise<void> {
|
||||
await runVisible`cp -a ${context.options.packageDir}/ui/hy2xs-admin/. /opt/hy2xs-admin/`;
|
||||
await runVisible`chown -R root:root /opt/hy2xs-admin`;
|
||||
await runVisible`chmod -R go-w /opt/hy2xs-admin`;
|
||||
}
|
||||
Reference in New Issue
Block a user