Refactor: install flow and hiper flow

This commit is contained in:
unitwk 2022-10-25 19:39:35 +08:00
parent b572c5fe50
commit 13473fba9f
5 changed files with 104 additions and 65 deletions

View File

@ -243,6 +243,7 @@
},
"quick_install": {
"unzipError": "解压文件失败"
"unzipError": "解压文件失败",
"hiperError": "HiPer 进程已存在,不可重复启动!"
}
}

View File

@ -21,6 +21,8 @@ import { ProcessConfig } from "../entity/instance/process_config";
import RestartCommand from "../entity/commands/restart";
import { TaskCenter } from "../service/async_task_service";
import { createQuickInstallTask } from "../service/async_task_service/quick_install";
import { HiPerTask, openHiPerTask } from "../service/async_task_service/hiper_start";
import { QuickInstallTask } from "../service/async_task_service/quick_install";
// Some instances operate router authentication middleware
routerApp.use((event, ctx, data, next) => {
@ -296,6 +298,11 @@ routerApp.on("instance/asynchronous", (ctx, data) => {
const task = createQuickInstallTask(targetLink, newInstanceName);
return protocol.response(ctx, task.toObject());
}
// Start HiPer Network
if (taskName === "hiper") {
const indexCode = String(parameter.indexCode);
openHiPerTask(indexCode);
}
protocol.response(ctx, true);
});
@ -330,6 +337,7 @@ routerApp.on("instance/stop_asynchronous", (ctx, data) => {
// Query async task status
routerApp.on("instance/query_asynchronous", (ctx, data) => {
const taskId = data.parameter.taskId as string | undefined;
// const type = String(data.parameter.type) || QuickInstallTask.TYPE;
if (!taskId) {
const result = [];
for (const task of TaskCenter.tasks) {

View File

@ -1,4 +1,3 @@
import { ChildProcess } from "child_process";
// Copyright (C) 2022 MCSManager <mcsmanager-dev@outlook.com>
import { v4 } from "uuid";
@ -8,26 +7,35 @@ import InstanceSubsystem from "../system_instance";
import InstanceConfig from "../../entity/instance/Instance_config";
import { $t, i18next } from "../../i18n";
import path from "path";
import { spawn, ChildProcess } from "child_process";
import { getFileManager } from "../file_router_service";
import EventEmitter from "events";
import { IAsyncTask, IAsyncTaskJSON, TaskCenter } from "./index";
import { AsyncTask, IAsyncTask, IAsyncTaskJSON, TaskCenter } from "./index";
import logger from "../log";
import { downloadFileToLocalFile } from "../download";
import os from "os";
// singleton pattern
class HiPer {
public static hiperProcess: ChildProcess;
public static hiperFileName: string = os.platform() === "win32" ? "hiper.exe" : "hiper";
public static hiperFilePath: string;
export class HiPer {
public static subProcess: ChildProcess;
public static openHiPer(keyPath: string) {
if (HiPer.hiperProcess) {
throw new Error($t("quick_install.hiperError"));
}
HiPer.hiperFilePath = path.normalize(path.join(process.cwd(), "lib", HiPer.hiperFileName));
HiPer.hiperProcess = spawn("hiper", ["-v", keyPath]);
}
public static openHiPer(keyPath: string) {}
public static stopHiPer() {}
public static stopHiPer() {
HiPer.hiperProcess = null;
}
}
export class HiPerTask extends EventEmitter implements IAsyncTask {
public taskId: string;
public instance: Instance;
export class HiPerTask extends AsyncTask {
public static readonly TYPE = "HiPerTask";
public readonly KEY_YML = path.normalize(path.join(process.cwd(), "lib", "hiper", "key.yml"));
public readonly POINT_YML = path.normalize(path.join(process.cwd(), "lib", "hiper", "point.yml"));
@ -35,16 +43,14 @@ export class HiPerTask extends EventEmitter implements IAsyncTask {
private keyYmlPath: string;
private pointYmlPath: string;
private _status = 0; // 0=stop 1=running -1=error 2=downloading
constructor(public readonly instanceUuid: string, public readonly indexCode: string) {
constructor(public readonly indexCode: string) {
super();
this.taskId = `HiPerTask-${instanceUuid}-${v4()}`;
this.taskId = `${HiPerTask.TYPE}-${indexCode}-${v4()}`;
this.type = HiPerTask.TYPE;
}
async start() {
this._status = 1;
this.emit("started");
async onStarted(): Promise<boolean | void> {
try {
// Download hiper key.yml
await downloadFileToLocalFile(`${this.BASE_URL}/${this.indexCode}.yml`, this.KEY_YML);
@ -52,8 +58,7 @@ export class HiPerTask extends EventEmitter implements IAsyncTask {
// Download hiper point.yml
await downloadFileToLocalFile(`${this.BASE_URL}/point.yml`, this.KEY_YML);
// TODO: The node information in point.yml is overwritten to key.yml
// fs.writeFile()
// The node information in point.yml is overwritten to key.yml
let keyFile = fs.readFileSync(this.KEY_YML, "utf-8");
const pointFile = fs.readFileSync(this.POINT_YML, "utf-8");
const START_TEXT = ">>> AUTO SYNC AREA";
@ -73,36 +78,27 @@ export class HiPerTask extends EventEmitter implements IAsyncTask {
// Start Command: hiper.exe -config .\key.yml
HiPer.openHiPer(this.keyYmlPath);
} catch (error) {
logger.error("HiPer Task Error:", error);
this.emit("failure");
} finally {
this.stop();
this.error(error);
}
}
async stop() {
this._status = 0;
this.emit("stopped");
onStopped(): Promise<boolean | void> {
HiPer.stopHiPer();
return null;
}
failure() {
this._status = -1;
this.emit("failure");
}
status(): number {
return this._status;
}
onError(): void {}
toObject(): IAsyncTaskJSON {
return JSON.parse(
JSON.stringify({
taskId: this.taskId,
status: this.status(),
instanceUuid: this.instance.instanceUuid,
instanceStatus: this.instance.status(),
instanceConfig: this.instance.config
status: this.status()
})
);
}
}
export function openHiPerTask(indexCode: string) {
TaskCenter.addTask(new HiPerTask(indexCode));
}

View File

@ -2,7 +2,6 @@
import EventEmitter from "events";
import logger from "../log";
export interface IAsyncTaskJSON {
[key: string]: any;
}
@ -10,32 +9,78 @@ export interface IAsyncTaskJSON {
export interface IAsyncTask extends EventEmitter {
// The taskId must be complex enough to prevent other users from accessing the information
taskId: string;
type: string;
start(): Promise<boolean | void>;
stop(): Promise<boolean | void>;
status(): number;
toObject(): IAsyncTaskJSON;
}
export abstract class AsyncTask extends EventEmitter implements IAsyncTask {
constructor() {
super();
}
public taskId: string;
public type: string;
// 0=stop 1=running -1=error 2=downloading
protected _status = 0;
public start() {
return this.onStarted();
}
public stop() {
this.emit("stopped");
return this.onStopped();
}
public error(err: any) {
logger.error(`AsyncTask - ID: ${this.taskId} TYPE: ${this.type} Error:`, err);
this.emit("error", err);
this.stop();
}
status(): number {
return this._status;
}
abstract onStarted(): Promise<boolean | void>;
abstract onStopped(): Promise<boolean | void>;
abstract onError(): void;
abstract toObject(): IAsyncTaskJSON;
}
export class TaskCenter {
public static tasks = new Array<IAsyncTask>();
public static addTask(t: IAsyncTask) {
TaskCenter.tasks.push(t);
t.start();
t.on("stopped", () => TaskCenter.onTaskStopped(t));
t.on("failure", () => TaskCenter.onTaskFailure(t));
t.on("error", () => TaskCenter.onTaskError(t));
}
public static onTaskStopped(t: IAsyncTask) {
logger.info("Async Task:", t.taskId, "Stopped.");
}
public static onTaskFailure(t: IAsyncTask) {
public static onTaskError(t: IAsyncTask) {
logger.info("Async Task:", t.taskId, "Failed.");
}
public static getTask(taskId: string) {
public static getTask(taskId: string, type?: string) {
for (const iterator of TaskCenter.tasks) {
if (iterator.taskId === taskId) return iterator;
if (iterator.taskId === taskId && (type == null || iterator.type === type)) return iterator;
}
}
public static getTasks(taskId: string, type?: string) {
const result: IAsyncTask[] = [];
for (const iterator of TaskCenter.tasks) {
if (iterator.taskId === taskId && (type == null || iterator.type === type)) {
result.push(iterator);
}
}
return result;
}
}

View File

@ -11,17 +11,17 @@ import { $t, i18next } from "../../i18n";
import path from "path";
import { getFileManager } from "../file_router_service";
import EventEmitter from "events";
import { IAsyncTask, IAsyncTaskJSON, TaskCenter } from "./index";
import { IAsyncTask, IAsyncTaskJSON, TaskCenter, AsyncTask } from "./index";
import logger from "../log";
export class QuickInstallTask extends EventEmitter implements IAsyncTask {
public taskId: string;
export class QuickInstallTask extends AsyncTask {
public static TYPE = "QuickInstallTask";
public instance: Instance;
public readonly TMP_ZIP_NAME = "mcsm_install_package.zip";
public readonly ZIP_CONFIG_JSON = "mcsmanager-config.json";
public zipPath = "";
private _status = 0; // 0=stop 1=running -1=error 2=downloading
private downloadStream: fs.WriteStream = null;
constructor(public instanceName: string, public targetLink: string) {
@ -32,7 +32,8 @@ export class QuickInstallTask extends EventEmitter implements IAsyncTask {
config.stopCommand = "stop";
config.type = Instance.TYPE_MINECRAFT_JAVA;
this.instance = InstanceSubsystem.createInstance(config);
this.taskId = `QuickInstallTask-${this.instance.instanceUuid}-${v4()}`;
this.taskId = `${QuickInstallTask.TYPE}-${this.instance.instanceUuid}-${v4()}`;
this.type = QuickInstallTask.TYPE;
}
private download(): Promise<boolean> {
@ -53,9 +54,7 @@ export class QuickInstallTask extends EventEmitter implements IAsyncTask {
});
}
async start() {
this._status = 1;
this.emit("started");
async onStarted() {
const fileManager = getFileManager(this.instance.instanceUuid);
try {
let result = await this.download();
@ -65,29 +64,19 @@ export class QuickInstallTask extends EventEmitter implements IAsyncTask {
this.instance.parameters(config);
this.stop();
} catch (error) {
logger.error("QuickInstall Task Error:", error);
this.emit("failure");
this.error(error);
} finally {
fs.remove(fileManager.toAbsolutePath(this.TMP_ZIP_NAME), () => {});
}
}
async stop() {
async onStopped(): Promise<boolean | void> {
try {
if (this.downloadStream) this.downloadStream.destroy(new Error("STOP TASK"));
} catch (error) {}
this._status = 0;
this.emit("stopped");
}
failure() {
this._status = -1;
this.emit("failure");
}
status(): number {
return this._status;
}
onError(): void {}
toObject(): IAsyncTaskJSON {
return JSON.parse(