Skip to content

Commit 96b86c3

Browse files
committed
core&machine-setup: fix & bump version
1 parent c28d7b5 commit 96b86c3

File tree

9 files changed

+73
-89
lines changed

9 files changed

+73
-89
lines changed

packages/machine-setup/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@hydrooj/xcpc-tools-setup-app-frontend",
33
"private": true,
4-
"version": "0.0.1",
4+
"version": "0.1.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

packages/machine-setup/frontend/src/components/BasicInfo.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
</template>
3838

3939
<script setup lang="ts">
40-
import { filesystem, os } from '@neutralinojs/lib';
40+
import { app, filesystem, os } from '@neutralinojs/lib';
4141
import { NCard, NGrid, NGi, NButton, NInput, NPopconfirm } from 'naive-ui';
4242
import { onMounted, ref } from 'vue';
4343
@@ -87,9 +87,9 @@ const checkAll = async (force = false) => {
8787
}
8888
window.$notification.success({ title: '设备检查完成', content: `seat: ${nowSeat.value}\nip: ${window.ip}`, duration: 3000 });
8989
}
90-
await os.execCommand(`systemctl enable heartbeat.timer`);
91-
await os.execCommand(`zenity --info --text "<span font='256'>${nowSeat.value}</span><br><span font='128'>${window.ip}</span>"`);
92-
console.log('check all');
90+
await os.execCommand(`systemctl enable heartbeat.timer --now`);
91+
await os.execCommand(`zenity --info --text "<span font='256'>${nowSeat.value}\n</span><span font='128'>${window.ip}</span>"`);
92+
app.exit();
9393
};
9494
9595
const getIp = () => window.ip;

packages/machine-setup/frontend/src/components/HeartbeatInfo.vue

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
<n-card bordered shadow="always">
33
<n-grid x-gap="12" :cols="2">
44
<n-gi>
5-
<p>上报中心:<n-tag :type="!nowHeartbeat ? 'error' : 'success'">{{ nowHeartbeat || 'no center' }}</n-tag></p>
5+
<n-tag :type="!nowHeartbeat ? 'error' : 'success'">{{ nowHeartbeat || 'no center' }}</n-tag>
66
<n-space>
77
<n-tag :type="onHeartbeat ? 'success' : 'error'">{{ onHeartbeat ? '已开启上报' : '未开启上报' }}</n-tag>
8-
<n-button type="warning" size="small" @click="">获取中心状态</n-button>
8+
<n-button type="warning" size="small" @click="getHeartbeatVersion(nowHeartbeat)">中心状态</n-button>
9+
<n-button type="warning" size="small" @click="getHeartbeatTimer()">服务状态</n-button>
910
</n-space>
1011
</n-gi>
1112
<n-gi>
@@ -84,16 +85,27 @@ const saveHeartbeat = async (force = false) => {
8485
}
8586
console.log('save heartbeat', url);
8687
await filesystem.writeFile('/etc/default/icpc-heartbeat', `HEARTBEATURL=${url}`);
87-
const res2 = await os.execCommand('systemctl enable heartbeat.timer');
88+
const res2 = await os.execCommand('systemctl enable heartbeat.timer --now');
8889
console.log('run enable heartbeat on save', res2);
8990
nowHeartbeat.value = url;
91+
onHeartbeat.value = true;
92+
window.$notification.success({ title: '保存心跳上报URL成功', content: '请查看心跳上报状态', duration: 3000 });
9093
} catch (error) {
9194
console.error(`save heartbeat error: ${error}`);
9295
window.$notification.error({ title: '保存心跳上报URL失败', content: (error as any).message, duration: 3000 });
9396
}
9497
};
9598
96-
99+
const getHeartbeatTimer = async () => {
100+
try {
101+
const res = await os.execCommand('systemctl status heartbeat.timer');
102+
console.log('systemctl status heartbeat.timer status', res.stdOut);
103+
window.$notification.success({ title: '心跳上报服务状态', content: res.stdOut, duration: 10000 });
104+
} catch (error) {
105+
console.error(`get heartbeat timer error: ${error}`);
106+
window.$notification.error({ title: '获取心跳上报服务状态失败', content: (error as any).message, duration: 3000 });
107+
}
108+
};
97109
98110
onMounted(async () => {
99111
try {
@@ -105,8 +117,8 @@ onMounted(async () => {
105117
console.log('disable heartbeat.timer', res);
106118
onHeartbeat.value = false;
107119
} else {
108-
const res = await os.execCommand('systemctl status heartbeat | grep Active');
109-
console.log('systemctl status heartbeat status', res);
120+
const res = await os.execCommand('systemctl status heartbeat.timer');
121+
console.log('systemctl status heartbeat.timer status', res.stdOut);
110122
if (!res.stdOut.includes('dead')) onHeartbeat.value = true;
111123
}
112124
} catch (error) {

packages/machine-setup/frontend/src/components/VideoInfo.vue

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,35 @@
22
<n-card bordered shadow="always">
33
<n-grid x-gap="12" :cols="2">
44
<n-gi>
5-
<p>摄像头服务:<n-tag :type="runCamera ? 'success' : 'error'">{{ runCamera ? '运行中' : '未运行' }}</n-tag></p>
6-
{{ cameraInfo }}
5+
<n-space>
6+
<p>摄像头服务:</p>
7+
<n-tag :type="hasCamera ? 'success' : 'error'">{{ hasCamera ? '已连接' : '未连接' }}</n-tag>
8+
<n-tag :type="runCamera ? 'success' : 'error'">{{ runCamera ? '运行中' : '未运行' }}</n-tag>
9+
</n-space>
710
<n-space>
811
<n-button size="small" type="primary" @click="runService('vlc-webcam', 'restart')">启动</n-button>
912
<n-button size="small" type="error" @click="runService('vlc-webcam', 'stop')">停止</n-button>
1013
<n-button size="small" type="info" @click="runVLC('webcam')">测试</n-button>
1114
<n-button size="small" type="warning" @click="runService('vlc-webcam', 'enable')">激活</n-button>
15+
<n-button size="small" @click="statusService('vlc-webcam')">状态</n-button>
1216
</n-space>
1317
<p>屏幕捕获服务:<n-tag :type="runScreen ? 'success' : 'error'">{{ runScreen ? '运行中' : '未运行' }}</n-tag></p>
14-
{{ screenInfo }}
1518
<n-space>
1619
<n-button size="small" type="primary" @click="runService('vlc-screen', 'restart')">启动</n-button>
17-
<n-button size="small" type="primary" @click="runService('vlc-screen', 'stop')">停止</n-button>
18-
<n-button size="small" type="error" @click="runVLC('screen')">测试</n-button>
20+
<n-button size="small" type="error" @click="runService('vlc-screen', 'stop')">停止</n-button>
21+
<n-button size="small" type="info" @click="runVLC('screen')">测试</n-button>
1922
<n-button size="small" type="warning" @click="runService('vlc-screen', 'enable')">激活</n-button>
23+
<n-button size="small" @click="statusService('vlc-screen')">状态</n-button>
2024
</n-space>
2125
</n-gi>
2226
<n-gi>
2327
<n-tabs default-value="video" justify-content="space-evenly" type="line" animated>
2428
<n-tab-pane name="video" tab="视频配置">
25-
<n-input type="textarea" rows="4" placeholder="配置文件" v-model:value="cameraInfo"></n-input>
29+
<n-input type="textarea" :rows="6" placeholder="配置文件" v-model:value="cameraInfo"></n-input>
2630
<n-button size="small" block type="primary">保存</n-button>
2731
</n-tab-pane>
2832
<n-tab-pane name="desktop" tab="桌面配置">
29-
<n-input type="textarea" rows="4" placeholder="配置文件" v-model:value="screenInfo"></n-input>
33+
<n-input type="textarea" :rows="6" placeholder="配置文件" v-model:value="screenInfo"></n-input>
3034
<n-button size="small" block type="primary">保存</n-button>
3135
</n-tab-pane>
3236
</n-tabs>
@@ -49,15 +53,20 @@ const cameraInfo = ref('');
4953
const screenInfo = ref('');
5054
5155
const runService = async (service: string, action: string) => {
52-
56+
if (service === 'vlc-webcam' && !hasCamera.value) {
57+
window.$notification.error({ title: '摄像头未连接', content: '请检查摄像头连接后再操作', duration: 3000 });
58+
return;
59+
}
5360
try {
5461
const res = await os.execCommand(`systemctl ${action} ${service}`);
55-
console.log(`systemctl ${action} ${service} status`, res);
56-
if (res.stdErr || res.exitCode) throw new Error(res.stdErr);
62+
console.log(`systemctl ${action} ${service} status`, res.stdOut);
63+
if (res.stdErr) throw new Error(res.stdErr);
5764
if (action === 'restart') {
65+
const status = await os.execCommand(`systemctl status ${service}`);
66+
if (status.stdOut.includes('dead') && status.stdOut.includes('exited')) throw new Error('服务启动失败');
5867
if (service === 'vlc-screen') runScreen.value = true;
5968
if (service === 'vlc-webcam') runCamera.value = true;
60-
} else {
69+
} else if (action === 'stop') {
6170
if (service === 'vlc-screen') runScreen.value = false;
6271
if (service === 'vlc-webcam') runCamera.value = false;
6372
}
@@ -72,14 +81,25 @@ const runVLC = async (service: string) => {
7281
try {
7382
const res = await os.execCommand(`su icpc -c "vlc http://localhost:${port}/"`);
7483
console.log('run vlc on test', res);
75-
if (res.stdErr || res.exitCode) throw new Error(res.stdErr);
84+
if (res.exitCode) throw new Error(res.stdErr);
7685
window.$notification.success({ title: 'VLC启动成功', content: '请查看VLC播放器,确认视频正常后关闭', duration: 3000 });
7786
} catch (error) {
7887
console.error(error);
7988
window.$notification.error({ title: 'VLC启动失败', content: (error as any).message, duration: 3000 });
8089
}
8190
}
8291
92+
const statusService = async (service: string) => {
93+
try {
94+
const res = await os.execCommand(`systemctl status ${service}`);
95+
if (res.stdErr) throw new Error(res.stdErr);
96+
window.$notification.success({ title: '状态获取成功', content: res.stdOut, duration: 10000 });
97+
} catch (error) {
98+
console.error(error);
99+
window.$notification.error({ title: '状态获取失败', content: (error as any).message, duration: 3000 });
100+
}
101+
};
102+
83103
84104
onMounted(async () => {
85105
try {
@@ -96,11 +116,11 @@ onMounted(async () => {
96116
}
97117
try {
98118
const res = await os.execCommand('systemctl status vlc-screen');
99-
console.log('systemctl status vlc-screen status', res);
100-
if (!res.stdOut.includes('dead')) runScreen.value = true;
119+
console.log('systemctl status vlc-screen status', res.stdOut);
120+
if (!res.stdOut.includes('dead') && !res.stdOut.includes('exited')) runScreen.value = true;
101121
const res2 = await os.execCommand('systemctl status vlc-webcam');
102-
console.log('systemctl status vlc-webcam status', res2);
103-
if (!res2.stdOut.includes('dead')) runCamera.value = true;
122+
console.log('systemctl status vlc-webcam status', res2.stdOut);
123+
if (!res2.stdOut.includes('dead') && !res.stdOut.includes('exited')) runCamera.value = true;
104124
} catch (error) {
105125
console.error(error);
106126
}

packages/server/handler/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class CommandsHandler extends AuthHandler {
1818

1919
async executeForAll(command: string, t = 10000) {
2020
const allOnline = await this.ctx.db.monitor.find({});
21-
const result = await Promise.allSettled(allOnline.map((i) => executeOnHost(i.ip, command, t)));
21+
const result = await Promise.allSettled(allOnline.map((i) => executeOnHost(i.ip, command, t, config.customKeyfile)));
2222
return {
2323
success: result.filter((i) => i.status === 'fulfilled').length,
2424
fail: result.filter((i) => i.status === 'rejected').length,

packages/server/handler/misc.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ class VersionHandler extends Handler {
7676
program: '@hydro/xcpc-tools',
7777
version,
7878
};
79+
this.response.addHeader('Access-Control-Allow-Origin', '*');
80+
this.response.addHeader('Access-Control-Allow-Methods', 'GET');
81+
this.response.addHeader('Access-Control-Allow-Headers', 'Content-Type');
82+
this.response.addHeader('Cache-Control', 'no-store');
7983
}
8084
}
8185

packages/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hydrooj/xcpc-tools",
3-
"version": "1.0.4",
3+
"version": "1.1.0",
44
"description": "A tools for XCPC contests",
55
"main": "index.ts",
66
"repository": "https://github.com/Hydro-dev/xcpc-tools",

packages/server/utils/commandRunner.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import child from 'child_process';
33
import fs from 'fs';
44
import { homedir } from 'os';
5-
import { config } from '../config';
65
import { Logger } from './index';
76

87
const logger = new Logger('runner');
@@ -37,13 +36,12 @@ export async function asyncCommand(command: string | string[], timeout = 10000)
3736
});
3837
}
3938

40-
const keyfile = config.customKeyfile ? config.customKeyfile
41-
: (fs.existsSync(`${homedir()}.ssh/id_rsa`) ? '.ssh/id_rsa' : '.ssh/id_ed25519');
39+
const keyfile = fs.existsSync(`${homedir()}.ssh/id_rsa`) ? '~/.ssh/id_rsa' : '~/.ssh/id_ed25519';
4240

43-
export async function executeOnHost(host: string, command: string, timeout = 10000) {
41+
export async function executeOnHost(host: string, command: string, timeout = 10000, customKeyfile?: string) {
4442
logger.info('executing', command, 'on', host);
4543
return await asyncCommand([
46-
'ssh', '-o', 'StrictHostKeyChecking no', '-o', `IdentityFile ~/${keyfile}`,
44+
'ssh', '-o', 'StrictHostKeyChecking no', '-o', `IdentityFile ${customKeyfile || keyfile}`,
4745
`root@${host}`,
4846
'bash', '-c', `'echo $(echo ${Buffer.from(command).toString('base64')} | base64 -d | bash)'`,
4947
], timeout);

packages/server/utils/index.ts

Lines changed: 5 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,65 +19,15 @@ declare global {
1919

2020
const defaultDict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
2121

22-
String.random = function random(digit = 32, dict = defaultDict) {
22+
export function randomstring(digit = 32, dict = defaultDict) {
2323
let str = '';
2424
for (let i = 1; i <= digit; i++) str += dict[Math.floor(Math.random() * dict.length)];
2525
return str;
26-
};
27-
28-
export function Counter<T extends (string | number) = string>() {
29-
return new Proxy({}, {
30-
get: (target, prop) => {
31-
if (target[prop] === undefined) return 0;
32-
return target[prop];
33-
},
34-
}) as Record<T, number>;
35-
}
36-
export function errorMessage(err: Error | string) {
37-
const t = typeof err === 'string' ? err : err.stack;
38-
const lines = t.split('\n')
39-
.filter((i) => !i.includes(' (node:') && !i.includes('(internal'));
40-
let cursor = 1;
41-
let count = 0;
42-
while (cursor < lines.length) {
43-
if (lines[cursor] !== lines[cursor - 1]) {
44-
if (count) {
45-
lines[cursor - 1] += ` [+${count}]`;
46-
count = 0;
47-
}
48-
cursor++;
49-
} else {
50-
count++;
51-
lines.splice(cursor, 1);
52-
}
53-
}
54-
const parsed = lines.join('\n')
55-
.replace(/[A-Z]:\\.+\\@hydrooj\\/g, '@hydrooj\\')
56-
.replace(/\/.+\/@hydrooj\//g, '\\')
57-
.replace(/[A-Z]:\\.+\\hydrooj\\/g, 'hydrooj\\')
58-
.replace(/\/.+\/hydrooj\//g, 'hydrooj/')
59-
.replace(/[A-Z]:\\.+\\node_modules\\/g, '')
60-
.replace(/\/.+\/node_modules\//g, '')
61-
.replace(/\\/g, '/');
62-
if (typeof err === 'string') return parsed;
63-
err.stack = parsed;
64-
return err;
65-
}
66-
export function isClass(obj: any, strict = false): obj is new (...args: any) => any {
67-
if (typeof obj !== 'function') return false;
68-
if (obj.prototype === undefined) return false;
69-
if (obj.prototype.constructor !== obj) return false;
70-
if (Object.getOwnPropertyNames(obj.prototype).length >= 2) return true;
71-
const str = obj.toString();
72-
if (str.slice(0, 5) === 'class') return true;
73-
if (/^function\s+\(|^function\s+anonymous\(/.test(str)) return false;
74-
if (strict && /^function\s+[A-Z]/.test(str)) return true;
75-
if (/\b\(this\b|\bthis[.[]\b/.test(str)) {
76-
if (!strict || /classCallCheck\(this/.test(str)) return true;
77-
return /^function\sdefault_\d+\s*\(/.test(str);
78-
}
79-
return false;
8026
}
27+
try {
28+
String.random = randomstring;
29+
} catch (e) { } // Cannot add property random, object is not extensible
30+
8131
export function sleep(timeout: number) {
8232
return new Promise((resolve) => {
8333
setTimeout(() => resolve(true), timeout);

0 commit comments

Comments
 (0)