mirror of
https://github.com/MCSManager/MCSManager.git
synced 2024-11-27 06:59:54 +08:00
Feat: Select template function
This commit is contained in:
parent
7c92b882b6
commit
cd8d3777d2
2
common/global.d.ts
vendored
2
common/global.d.ts
vendored
@ -206,7 +206,7 @@ export interface IQuickStartPackages {
|
||||
size: string;
|
||||
hardware: string;
|
||||
remark: string;
|
||||
targetLink: string;
|
||||
targetLink?: string;
|
||||
author: string;
|
||||
setupInfo?: IJsonData;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ export default class Instance extends EventEmitter {
|
||||
}
|
||||
|
||||
if (cfg?.processType && cfg?.processType !== this.config.processType) {
|
||||
if (this.status() != Instance.STATUS_STOP)
|
||||
if (this.status() !== Instance.STATUS_STOP && this.status() !== Instance.STATUS_BUSY)
|
||||
throw new Error($t("TXT_CODE_instanceConf.cantModifyProcessType"));
|
||||
configureEntityParams(this.config, cfg, "processType", String);
|
||||
this.forceExec(new FunctionDispatcher());
|
||||
|
@ -482,7 +482,7 @@ routerApp.on("instance/outputlog", async (ctx, data) => {
|
||||
return protocol.response(ctx, text);
|
||||
}
|
||||
protocol.responseError(ctx, new Error($t("TXT_CODE_Instance_router.terminalLogNotExist")), {
|
||||
notPrintErr: true
|
||||
disablePrint: true
|
||||
});
|
||||
} catch (err: any) {
|
||||
protocol.responseError(ctx, err);
|
||||
|
@ -33,7 +33,9 @@ routerApp.use(async (event, ctx, _, next) => {
|
||||
event: event
|
||||
})
|
||||
);
|
||||
return protocol.error(ctx, "error", IGNORE);
|
||||
return protocol.error(ctx, "error", IGNORE, {
|
||||
disablePrint: true
|
||||
});
|
||||
});
|
||||
|
||||
// authentication controller
|
||||
|
@ -24,7 +24,9 @@ routerApp.use(async (event, ctx, data, next) => {
|
||||
) {
|
||||
return await next();
|
||||
}
|
||||
return protocol.error(ctx, "error", IGNORE);
|
||||
return protocol.error(ctx, "error", IGNORE, {
|
||||
disablePrint: true
|
||||
});
|
||||
}
|
||||
return await next();
|
||||
});
|
||||
@ -48,7 +50,7 @@ routerApp.on("stream/auth", (ctx, data) => {
|
||||
protocol.response(ctx, true);
|
||||
} catch (error: any) {
|
||||
protocol.responseError(ctx, error, {
|
||||
notPrintErr: true
|
||||
disablePrint: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -27,20 +27,21 @@ export class QuickInstallTask extends AsyncTask {
|
||||
|
||||
constructor(
|
||||
public instanceName: string,
|
||||
public targetLink: string,
|
||||
public targetLink?: string,
|
||||
public buildParams?: Partial<InstanceConfig>,
|
||||
curInstance?: Instance
|
||||
) {
|
||||
super();
|
||||
const config = new InstanceConfig();
|
||||
config.nickname = instanceName;
|
||||
config.cwd = "";
|
||||
config.stopCommand = "^c";
|
||||
config.type = Instance.TYPE_MINECRAFT_JAVA;
|
||||
if (!curInstance) {
|
||||
config.cwd = "";
|
||||
this.instance = InstanceSubsystem.createInstance(config);
|
||||
} else {
|
||||
this.instance = curInstance;
|
||||
config.cwd = this.instance.config.cwd;
|
||||
this.instance.config = config;
|
||||
}
|
||||
this.taskId = `${QuickInstallTask.TYPE}-${this.instance.instanceUuid}-${v4()}`;
|
||||
this.type = QuickInstallTask.TYPE;
|
||||
@ -49,6 +50,7 @@ export class QuickInstallTask extends AsyncTask {
|
||||
private download(): Promise<boolean> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (!this.targetLink) reject(new Error("No targetLink!"));
|
||||
this.zipPath = path.normalize(path.join(this.instance.config.cwd, this.TMP_ZIP_NAME));
|
||||
const writeStream = fs.createWriteStream(this.zipPath);
|
||||
const response = await axios<Readable>({
|
||||
@ -75,13 +77,15 @@ export class QuickInstallTask extends AsyncTask {
|
||||
async onStarted() {
|
||||
const fileManager = getFileManager(this.instance.instanceUuid);
|
||||
try {
|
||||
let result = await this.download();
|
||||
result = await fileManager.unzip(this.TMP_ZIP_NAME, ".", "UTF-8");
|
||||
if (!result) throw new Error($t("TXT_CODE_quick_install.unzipError"));
|
||||
if (this.targetLink) {
|
||||
let result = await this.download();
|
||||
result = await fileManager.unzip(this.TMP_ZIP_NAME, ".", "UTF-8");
|
||||
if (!result) throw new Error($t("TXT_CODE_quick_install.unzipError"));
|
||||
}
|
||||
|
||||
let config: Partial<InstanceConfig>;
|
||||
if (this.buildParams?.startCommand) {
|
||||
config = this.buildParams;
|
||||
if (this.buildParams?.startCommand || !fs.existsSync(this.ZIP_CONFIG_JSON)) {
|
||||
config = this.buildParams || {};
|
||||
} else {
|
||||
config = JSON.parse(await fileManager.readFile(this.ZIP_CONFIG_JSON));
|
||||
}
|
||||
@ -135,11 +139,11 @@ export class QuickInstallTask extends AsyncTask {
|
||||
}
|
||||
|
||||
export function createQuickInstallTask(
|
||||
targetLink: string,
|
||||
instanceName: string,
|
||||
targetLink?: string,
|
||||
instanceName?: string,
|
||||
buildParams?: any
|
||||
) {
|
||||
if (!targetLink || !instanceName) throw new Error("targetLink or instanceName is null!");
|
||||
if (!instanceName) throw new Error("Instance name is empty!");
|
||||
const task = new QuickInstallTask(instanceName, targetLink, buildParams);
|
||||
TaskCenter.addTask(task);
|
||||
return task;
|
||||
|
@ -18,7 +18,7 @@ export interface IPacket {
|
||||
}
|
||||
|
||||
export interface IResponseErrorConfig {
|
||||
notPrintErr: boolean;
|
||||
disablePrint: boolean;
|
||||
}
|
||||
|
||||
// global socket storage
|
||||
@ -50,9 +50,8 @@ export function responseError(
|
||||
else errinfo = err;
|
||||
const packet = new Packet(ctx.uuid, STATUS_ERR, ctx.event, errinfo);
|
||||
// Ignore
|
||||
if (err.toString().includes(IGNORE) && ctx.event) return ctx.socket.emit(ctx.event, packet);
|
||||
|
||||
if (!config?.notPrintErr)
|
||||
if (String(err).includes(IGNORE) && ctx.event) return ctx.socket.emit(ctx.event, packet);
|
||||
if (!config?.disablePrint)
|
||||
logger.warn(
|
||||
$t("TXT_CODE_protocol.socketErr", {
|
||||
id: ctx.socket.id,
|
||||
@ -69,19 +68,20 @@ export function msg(ctx: RouterContext, event: string, data: any) {
|
||||
ctx.socket.emit(event, packet);
|
||||
}
|
||||
|
||||
export function error(ctx: RouterContext, event: string, err: any) {
|
||||
export function error(ctx: RouterContext, event: string, err: any, config?: IResponseErrorConfig) {
|
||||
const packet = new Packet(ctx.uuid, STATUS_ERR, event, err);
|
||||
// Ignore
|
||||
if (err.toString().includes(IGNORE) && ctx.event) return ctx.socket.emit(ctx.event, packet);
|
||||
if (String(err).includes(IGNORE) && ctx.event) return ctx.socket.emit(ctx.event, packet);
|
||||
if (!config?.disablePrint)
|
||||
logger.warn(
|
||||
$t("TXT_CODE_protocol.socketErr", {
|
||||
id: ctx.socket.id,
|
||||
address: ctx.socket.handshake.address,
|
||||
event: ctx.event
|
||||
}),
|
||||
err
|
||||
);
|
||||
|
||||
logger.warn(
|
||||
$t("TXT_CODE_protocol.socketErr", {
|
||||
id: ctx.socket.id,
|
||||
address: ctx.socket.handshake.address,
|
||||
event: ctx.event
|
||||
}),
|
||||
err
|
||||
);
|
||||
ctx.socket.emit(event, packet);
|
||||
}
|
||||
|
||||
|
@ -245,7 +245,7 @@ export const createAsyncTask = useDefineApi<
|
||||
data: {
|
||||
time: number;
|
||||
newInstanceName: string;
|
||||
targetLink: string;
|
||||
targetLink?: string;
|
||||
setupInfo?: JsonData;
|
||||
};
|
||||
},
|
||||
@ -441,7 +441,9 @@ export const reinstallInstance = useDefineApi<
|
||||
uuid: string;
|
||||
};
|
||||
data: {
|
||||
targetUrl: string;
|
||||
targetUrl?: string;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
},
|
||||
boolean
|
||||
|
@ -51,17 +51,10 @@ const updateCmd = computed(() => (instanceInfo.value?.config.updateCommand ? tru
|
||||
const instanceStatusText = computed(() => INSTANCE_STATUS[instanceInfo.value?.status ?? -1]);
|
||||
const quickOperations = computed(() =>
|
||||
arrayFilter([
|
||||
{
|
||||
title: t("TXT_CODE_b19ed1dd"),
|
||||
icon: InteractionOutlined,
|
||||
type: "danger",
|
||||
click: () => reinstallDialog.value?.openDialog(),
|
||||
props: {},
|
||||
condition: () => isStopped.value
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_57245e94"),
|
||||
icon: PlayCircleOutlined,
|
||||
noConfirm: false,
|
||||
type: "default",
|
||||
click: async () => {
|
||||
try {
|
||||
@ -108,6 +101,7 @@ const instanceOperations = computed(() =>
|
||||
title: t("TXT_CODE_47dcfa5"),
|
||||
icon: RedoOutlined,
|
||||
type: "default",
|
||||
noConfirm: false,
|
||||
click: async () => {
|
||||
try {
|
||||
await restartInstance().execute({
|
||||
@ -161,6 +155,14 @@ const instanceOperations = computed(() =>
|
||||
}
|
||||
},
|
||||
condition: () => isStopped.value && updateCmd.value
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_b19ed1dd"),
|
||||
icon: InteractionOutlined,
|
||||
noConfirm: true,
|
||||
click: () => reinstallDialog.value?.openDialog(),
|
||||
props: {},
|
||||
condition: () => isStopped.value
|
||||
}
|
||||
])
|
||||
);
|
||||
@ -228,17 +230,28 @@ onMounted(async () => {
|
||||
</template>
|
||||
<template #right>
|
||||
<div v-if="!isPhone">
|
||||
<a-popconfirm
|
||||
v-for="item in [...quickOperations, ...instanceOperations]"
|
||||
:key="item.title"
|
||||
:title="t('TXT_CODE_276756b2')"
|
||||
@confirm="item.click"
|
||||
>
|
||||
<a-button class="ml-8" :danger="item.type === 'danger'">
|
||||
<template v-for="item in [...quickOperations, ...instanceOperations]" :key="item.title">
|
||||
<a-button
|
||||
v-if="item.noConfirm"
|
||||
class="ml-8"
|
||||
:danger="item.type === 'danger'"
|
||||
@click="item.click"
|
||||
>
|
||||
<component :is="item.icon" />
|
||||
{{ item.title }}
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm
|
||||
v-else
|
||||
:key="item.title"
|
||||
:title="t('TXT_CODE_276756b2')"
|
||||
@confirm="item.click"
|
||||
>
|
||||
<a-button class="ml-8" :danger="item.type === 'danger'">
|
||||
<component :is="item.icon" />
|
||||
{{ item.title }}
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<a-dropdown v-else>
|
||||
|
@ -40,7 +40,9 @@ const handleSelectTemplate = (item: QuickStartPackages) => {
|
||||
uuid: props.instanceId
|
||||
},
|
||||
data: {
|
||||
targetUrl: item.targetLink
|
||||
targetUrl: item.targetLink,
|
||||
title: item.title,
|
||||
description: item.description
|
||||
}
|
||||
});
|
||||
cancel();
|
||||
@ -65,7 +67,7 @@ defineExpose({
|
||||
<a-modal
|
||||
v-model:open="open"
|
||||
centered
|
||||
width="100%"
|
||||
width="95%"
|
||||
:cancel-text="t('TXT_CODE_3b1cc020')"
|
||||
:mask-closable="false"
|
||||
:confirm-loading="false"
|
||||
@ -73,6 +75,8 @@ defineExpose({
|
||||
@cancel="cancel"
|
||||
>
|
||||
<template #title> {{ t("TXT_CODE_5e10537a") }} </template>
|
||||
<AppPackages ref="appPackages" @handle-select-template="handleSelectTemplate" />
|
||||
<div class="mt-20">
|
||||
<AppPackages ref="appPackages" @handle-select-template="handleSelectTemplate" />
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
@ -127,7 +127,7 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div style="min-height: 220px; position: relative">
|
||||
<div style="min-height: 120px; position: relative">
|
||||
<a-typography-paragraph
|
||||
:ellipsis="{ rows: 3, expandable: true }"
|
||||
:content="item.description"
|
||||
@ -135,9 +135,11 @@ onMounted(() => {
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text class="color-info">
|
||||
<div>{{ t("TXT_CODE_18b94497") }}: {{ item.runtime }}</div>
|
||||
<div>{{ t("TXT_CODE_683e3033") }}: {{ item.hardware }}</div>
|
||||
<div>{{ t("TXT_CODE_94bb113a") }}: {{ item.size }}</div>
|
||||
<div v-if="item.runtime">{{ t("TXT_CODE_18b94497") }}: {{ item.runtime }}</div>
|
||||
<div v-if="item.hardware">
|
||||
{{ t("TXT_CODE_683e3033") }}: {{ item.hardware }}
|
||||
</div>
|
||||
<div v-if="item.size">{{ t("TXT_CODE_94bb113a") }}: {{ item.size }}</div>
|
||||
</a-typography-text>
|
||||
<br />
|
||||
<a-typography-text class="color-info"> </a-typography-text>
|
||||
@ -160,7 +162,7 @@ onMounted(() => {
|
||||
block
|
||||
type="primary"
|
||||
ghost
|
||||
style="max-width: 180px"
|
||||
style="max-width: 120px"
|
||||
@click="emit('handleSelectTemplate', item)"
|
||||
>
|
||||
<template #icon>
|
||||
|
@ -42,7 +42,7 @@ const handleSelectTemplate = async (item: QuickStartPackages) => {
|
||||
data: {
|
||||
time: Date.now(),
|
||||
newInstanceName: instanceName,
|
||||
targetLink: item.targetLink,
|
||||
targetLink: item.targetLink || "",
|
||||
setupInfo: item.setupInfo
|
||||
}
|
||||
});
|
||||
|
@ -403,7 +403,7 @@
|
||||
"TXT_CODE_4ea93630": "Please enter content",
|
||||
"TXT_CODE_b5b33dd4": "Custom Text on the Login Page",
|
||||
"TXT_CODE_c26e5fb7": "Support Markdown format",
|
||||
"TXT_CODE_b2767aa2": "Preset Packages",
|
||||
"TXT_CODE_b2767aa2": "Template Packages Config",
|
||||
"TXT_CODE_b1f833f3": "Source for the quick install function. This can be modified to support custom predefined server packages. ",
|
||||
"TXT_CODE_514e064a": "Panel Binding IP",
|
||||
"TXT_CODE_328191e": "Maybe useful in a multi-IP environment. If there's only one IP available on the host, please leave it empty.",
|
||||
@ -1877,7 +1877,17 @@
|
||||
"TXT_CODE_1c2efd38": "The network connection cannot be established with the remote node. Please check whether the remote node is running normally? \nIs the network configured correctly?",
|
||||
"TXT_CODE_6915f2a": "Dependency files are missing and cannot be started. Please reinstall the program:",
|
||||
"TXT_CODE_9337bed1": "It doesn't look like there are any remote nodes here. Maybe there was a problem during the installation process. Restarting your panel and daemon will solve it!",
|
||||
"TXT_CODE_8a30e150": "All Packages",
|
||||
"TXT_CODE_8a30e150": "All Languages",
|
||||
"TXT_CODE_ac225d07": "Build instance parameters:",
|
||||
"TXT_CODE_e5ba712d": "Building the server as a preset package:"
|
||||
"TXT_CODE_e5ba712d": "Building the server as a preset package:",
|
||||
"TXT_CODE_2375f010": "Reinstall",
|
||||
"TXT_CODE_5e10537a": "Change Instance Template",
|
||||
"TXT_CODE_617ce69c": "Warn",
|
||||
"TXT_CODE_906c5d6a": "Deletion Completed!",
|
||||
"TXT_CODE_b19ed1dd": "Change Template",
|
||||
"TXT_CODE_b9ca022b": "Installing instance files, please wait patiently...",
|
||||
"TXT_CODE_cbc235ad": "Clearing the existing files of the instance, please wait patiently...",
|
||||
"TXT_CODE_e4878221": "Missing remote node ID",
|
||||
"TXT_CODE_ed3fc23": "Continue",
|
||||
"TXT_CODE_f220ed78": "Installation is complete!"
|
||||
}
|
||||
|
@ -403,7 +403,7 @@
|
||||
"TXT_CODE_4ea93630": "请输入内容",
|
||||
"TXT_CODE_b5b33dd4": "登录页文字展示",
|
||||
"TXT_CODE_c26e5fb7": "支持 Markdown 格式",
|
||||
"TXT_CODE_b2767aa2": "预设资源下载点",
|
||||
"TXT_CODE_b2767aa2": "实例模板配置",
|
||||
"TXT_CODE_b1f833f3": "快速安装服务器时的下载源列表,您可以通过更改此地址实现自定义服务端预设下载站。",
|
||||
"TXT_CODE_514e064a": "面板绑定IP",
|
||||
"TXT_CODE_328191e": "适用于主机上拥有多张网卡多个IP地址的情况,如果您只有一个公网IP,那么请不要配置此项。",
|
||||
@ -1879,7 +1879,7 @@
|
||||
"TXT_CODE_1c2efd38": "无法与远程节点建立网络连接,请检查远程节点是否运行正常?网络是否正确配置?",
|
||||
"TXT_CODE_e5ba712d": "正在以预设包的方式构建服务器:",
|
||||
"TXT_CODE_ac225d07": "构建包实例参数:",
|
||||
"TXT_CODE_8a30e150": "所有包",
|
||||
"TXT_CODE_8a30e150": "所有语言",
|
||||
"TXT_CODE_cbc235ad": "正在清空实例现有文件,请耐心等待...",
|
||||
"TXT_CODE_906c5d6a": "删除完成!",
|
||||
"TXT_CODE_b9ca022b": "正在安装实例文件,请耐心等待...",
|
||||
|
@ -403,7 +403,7 @@
|
||||
"TXT_CODE_4ea93630": "請輸入內容",
|
||||
"TXT_CODE_b5b33dd4": "登入頁文字展示",
|
||||
"TXT_CODE_c26e5fb7": "支援 Markdown 格式",
|
||||
"TXT_CODE_b2767aa2": "預設資源下載點",
|
||||
"TXT_CODE_b2767aa2": "預設模板配置",
|
||||
"TXT_CODE_b1f833f3": "快速安裝伺服器時的下載來源列表,您可以透過變更此位址來實現自訂伺服端預設下載站。",
|
||||
"TXT_CODE_514e064a": "面板綁定IP",
|
||||
"TXT_CODE_328191e": "適用於主機上擁有多張網路卡多個IP位址的情況,如果您只有一個公網IP,那麼請不要設定此項目。",
|
||||
|
@ -472,12 +472,16 @@ router.get(
|
||||
router.post(
|
||||
"/install_instance",
|
||||
permission({ level: ROLE.USER, speedLimit: true }),
|
||||
validator({ query: { daemonId: String, uuid: String }, body: { targetUrl: String } }),
|
||||
validator({
|
||||
query: { daemonId: String, uuid: String },
|
||||
body: { description: String, title: String }
|
||||
}),
|
||||
async (ctx) => {
|
||||
try {
|
||||
const daemonId = String(ctx.query.daemonId);
|
||||
const instanceUuid = String(ctx.query.uuid);
|
||||
const targetUrl = String(ctx.request.body.targetUrl);
|
||||
const description = String(ctx.request.body.description);
|
||||
const title = String(ctx.request.body.title);
|
||||
|
||||
const presetUrl = systemConfig?.quickInstallAddr;
|
||||
if (!presetUrl) throw new Error("Preset Addr is empty!");
|
||||
@ -490,7 +494,9 @@ router.post(
|
||||
const packages = presetConfig.packages;
|
||||
|
||||
if (!(packages instanceof Array)) throw new Error("Preset Config is not array!");
|
||||
const targetPresetConfig = packages.find((v) => v.targetLink === targetUrl);
|
||||
const targetPresetConfig = packages.find(
|
||||
(v) => v.title === title && v.description === description
|
||||
);
|
||||
if (!targetPresetConfig) throw new Error("Preset Config is not found!");
|
||||
|
||||
const remoteService = RemoteServiceSubsystem.getInstance(daemonId);
|
||||
|
Loading…
Reference in New Issue
Block a user