190 lines
5.6 KiB
TypeScript
Executable File
190 lines
5.6 KiB
TypeScript
Executable File
#!/usr/bin/env deno -- --allow-all
|
|
|
|
// !IMPORTANT! not ready
|
|
|
|
import {
|
|
execCommand,
|
|
execCommandShell,
|
|
fetchWithTimeout,
|
|
log,
|
|
ProcessBar,
|
|
readFileToString,
|
|
sleep,
|
|
} from "https://global.hatter.ink/script/get/@24/deno-commons-mod.ts";
|
|
import {parseProcessLine, Process,} from "https://global.hatter.ink/script/get/@0/deno-process-mod.ts";
|
|
import {fromFileUrl} from "https://deno.land/std/path/mod.ts";
|
|
|
|
interface HealthCheck {
|
|
url?: string;
|
|
count?: number; // default 30
|
|
timeoutMillis?: number; // default 1000
|
|
intervalMillis?: number; // default 1000
|
|
}
|
|
|
|
interface ServerControlConfig {
|
|
appId?: string;
|
|
startup?: string;
|
|
healthCheck?: HealthCheck;
|
|
}
|
|
|
|
async function listJavaProcesses(
|
|
serverControlConfig: ServerControlConfig,
|
|
): Promise<Process[]> {
|
|
const appId = serverControlConfig.appId;
|
|
const processOutput = await execCommand("ps", ["aux"]);
|
|
processOutput.assertSuccess();
|
|
const stdout = processOutput.getStdoutAsStringThenTrim();
|
|
const lines = stdout.split("\n");
|
|
return lines.filter((line) => {
|
|
return line.includes("java") && line.includes("hatterserver") &&
|
|
line.includes(`-DAPPID=${appId}`);
|
|
}).map((line) => {
|
|
return parseProcessLine(line);
|
|
}).filter((line) => line !== null);
|
|
}
|
|
|
|
async function checkServerStarted(
|
|
serverControlConfig: ServerControlConfig,
|
|
): Promise<boolean> {
|
|
const healthCheck = serverControlConfig.healthCheck;
|
|
const healthCheckUrl = healthCheck?.url;
|
|
if (!healthCheck || !healthCheckUrl) {
|
|
log.error("Health check URL is not configured!");
|
|
return false;
|
|
}
|
|
const count = healthCheck.count || 30;
|
|
const intervalMillis = healthCheck.intervalMillis || 1000;
|
|
const timeoutMillis = healthCheck.timeoutMillis || 1000;
|
|
await new ProcessBar("Starting server").call(async () => {
|
|
for (let i = 0; i < count; i++) {
|
|
try {
|
|
const response = await fetchWithTimeout(
|
|
healthCheckUrl,
|
|
timeoutMillis,
|
|
);
|
|
if (response.status === 200) {
|
|
log.success("Server started!");
|
|
return true;
|
|
}
|
|
} catch (e) {
|
|
// IGNORE
|
|
}
|
|
await sleep(intervalMillis);
|
|
}
|
|
});
|
|
log.warn("Server failed!");
|
|
return false;
|
|
}
|
|
|
|
async function loadServerControlConfig(
|
|
serverControlConfigFile?: string,
|
|
): Promise<ServerControlConfig> {
|
|
const fullServerControlConfigFile = serverControlConfigFile ||
|
|
(fromFileUrl(import.meta.url).replace(".ts", ".json"));
|
|
const serverControlConfigJson = await readFileToString(
|
|
fullServerControlConfigFile,
|
|
);
|
|
if (serverControlConfigJson === null) {
|
|
throw new Error(`Read file ${fullServerControlConfigFile} failed.`);
|
|
}
|
|
return JSON.parse(
|
|
serverControlConfigJson,
|
|
) as ServerControlConfig;
|
|
}
|
|
|
|
async function handleStatus(
|
|
serverControlConfig: ServerControlConfig,
|
|
): Promise<void> {
|
|
const processes = await listJavaProcesses(serverControlConfig);
|
|
if (processes.length === 0) {
|
|
log.info("No process are running!");
|
|
return;
|
|
}
|
|
log.info(
|
|
`Find ${processes.length} process(es), pid(s): ${
|
|
processes.map((p) => p.pid).join(", ")
|
|
}`,
|
|
);
|
|
}
|
|
|
|
async function handleStop(serverControlConfig: ServerControlConfig) {
|
|
const processes = await listJavaProcesses(serverControlConfig);
|
|
if (processes.length === 0) {
|
|
log.info("No process are running!");
|
|
return;
|
|
}
|
|
if (processes.length > 1) {
|
|
log.warn(
|
|
`Too many processes are running, pid(s): ${
|
|
processes.map((p) => p.pid).join(", ")
|
|
}`,
|
|
);
|
|
return;
|
|
}
|
|
const pid = processes[0].pid;
|
|
log.warn(`Kill pid: ${pid}`);
|
|
await execCommandShell("kill", [`${pid}`]);
|
|
await sleep(500);
|
|
}
|
|
|
|
async function handleRestart(serverControlConfig: ServerControlConfig) {
|
|
let processes = await listJavaProcesses(serverControlConfig);
|
|
if (processes.length > 1) {
|
|
log.warn(
|
|
`Too many processes are running, pid(s): ${
|
|
processes.map((p) => p.pid).join(", ")
|
|
}`,
|
|
);
|
|
return;
|
|
}
|
|
if (!serverControlConfig.startup) {
|
|
log.error("Startup command is not configured!");
|
|
return;
|
|
}
|
|
while (processes.length > 0) {
|
|
await execCommandShell("kill", [`${processes[0].pid}`]);
|
|
await sleep(500);
|
|
processes = await listJavaProcesses(serverControlConfig);
|
|
}
|
|
log.info(`Run command: ${serverControlConfig.startup} &`);
|
|
await execCommandShell("sh", ["-c", `${serverControlConfig.startup} &`]);
|
|
log.success("Start server, checking ...");
|
|
await checkServerStarted(serverControlConfig);
|
|
}
|
|
|
|
async function main() {
|
|
const args = Deno.args;
|
|
if (args.length === 0) {
|
|
log.error(`No args.
|
|
|
|
server-control.js status
|
|
server-control.js kill|stop
|
|
server-control.js re|restart`);
|
|
return;
|
|
}
|
|
const serverControlConfig = await loadServerControlConfig();
|
|
if (!serverControlConfig.appId) {
|
|
log.error("Config appId not found!");
|
|
return;
|
|
}
|
|
|
|
switch (args[0]) {
|
|
case "status":
|
|
await handleStatus(serverControlConfig);
|
|
return;
|
|
case "kill":
|
|
case "stop":
|
|
await handleStop(serverControlConfig);
|
|
return;
|
|
case "re":
|
|
case "restart":
|
|
await handleRestart(serverControlConfig);
|
|
return;
|
|
default:
|
|
log.warn("Argument error!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
main().catch((e) => log.error(e));
|