mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
install: support arm64 cpu
This commit is contained in:
1 parent
632ae27b88
commit
4d61549f06
5 files changed
+315
-1115
No files matched your search
@@ -1,337 +0,0 @@
|
||||
/* eslint-disable no-unreachable */
|
||||
/* eslint-disable consistent-return */
|
||||
/* eslint-disable no-undef */
|
||||
/// <reference types="./jssh" />
|
||||
|
||||
const locales = {
|
||||
zh: {
|
||||
'install.start': '开始运行 Hydro 安装工具',
|
||||
'info.mirror': '将首选 %s 镜像。可以使用 MIRROR=tsinghua|tencent|official 更改。',
|
||||
'warn.avx2': '检测到您的 CPU 不支持 avx2 指令集,将使用 mongodb@v4.4',
|
||||
'error.rootRequired': '请先使用 sudo su 切换到 root 用户后再运行该工具。',
|
||||
'error.unsupportedArch': '不支持的架构 %s ,请尝试手动安装。',
|
||||
'error.osreleaseNotFound': '无法获取系统版本信息(/etc/os-release 文件未找到),请尝试手动安装。',
|
||||
'error.unsupportedOS': '不支持的操作系统 %s ,请尝试手动安装,',
|
||||
'install.preparing': '正在初始化安装...',
|
||||
'install.mongodb': '正在安装 mongodb...',
|
||||
'install.nvm': '正在安装 NVM...',
|
||||
'error.nodeWithoutNVMDetected': '检测到您的系统中安装了 Node,但未使用 NVM,请尝试手动安装或卸载当前 Node 后再试。',
|
||||
'install.nodejs': '正在安装 NodeJS...',
|
||||
'error.nodeVersionPraseFail': '无法解析 Node 版本号,请尝试手动安装。',
|
||||
'install.pm2': '正在安装 PM2...',
|
||||
'install.createDatabaseUser': '正在创建数据库用户...',
|
||||
'install.compiler': '正在安装编译器...',
|
||||
'install.hydro': '正在安装 Hydro...',
|
||||
'install.done': 'Hydro 安装成功!',
|
||||
'extra.restartTerm': '请重启终端(或重新连接ssh)并切换到 root 用户执行剩下的操作。',
|
||||
'extra.dbUser': '数据库用户名: hydro',
|
||||
'extra.dbPassword': '数据库密码: %s',
|
||||
'info.skip': '步骤已跳过。',
|
||||
},
|
||||
en: {
|
||||
'install.start': 'Starting Hydro installation tool',
|
||||
'info.mirror': 'Using preferred %s mirror. You can use MIRROR=tsinghua|tencent|official to change.',
|
||||
'warn.avx2': 'Your CPU does not support avx2, will use mongodb@v4.4',
|
||||
'error.rootRequired': 'Please run this tool as root user.',
|
||||
'error.unsupportedArch': 'Unsupported architecture %s, please try to install manually.',
|
||||
'error.osreleaseNotFound': 'Unable to get system version information (/etc/os-release file not found), please try to install manually.',
|
||||
'error.unsupportedOS': 'Unsupported operating system %s, please try to install manually.',
|
||||
'install.preparing': 'Initializing installation...',
|
||||
'install.mongodb': 'Installing mongodb...',
|
||||
'install.nvm': 'Installing NVM...',
|
||||
'error.nodeWithoutNVMDetected': 'Detected Node installation without NVM, please try to install manually or uninstall current Node first.',
|
||||
'install.nodejs': 'Installing NodeJS...',
|
||||
'error.nodeVersionPraseFail': 'Unable to parse Node version, please try to install manually.',
|
||||
'install.pm2': 'Installing PM2...',
|
||||
'install.createDatabaseUser': 'Creating database user...',
|
||||
'install.compiler': 'Installing compiler...',
|
||||
'install.hydro': 'Installing Hydro...',
|
||||
'install.done': 'Hydro installation completed!',
|
||||
'extra.restartTerm': 'Please restart your terminal (or reconnect ssh) and switch to root user to execute the remaining operations.',
|
||||
'extra.dbUser': 'Database username: hydro',
|
||||
'extra.dbPassword': 'Database password: %s',
|
||||
'info.skip': 'Step skipped.',
|
||||
},
|
||||
};
|
||||
let locale = __env.LANG?.includes('zh') ? 'zh' : 'en';
|
||||
if (__env.TERM === 'linux') locale = 'en';
|
||||
log.info = ((orig) => (str, ...args) => orig(locales[locale][str] || str, ...args) && 0)(log.info);
|
||||
log.warn = ((orig) => (str, ...args) => orig(locales[locale][str] || str, ...args) && 0)(log.warn);
|
||||
log.fatal = ((orig) => (str, ...args) => orig(locales[locale][str] || str, ...args) && 0)(log.fatal);
|
||||
|
||||
if (__user !== 'root') log.fatal('error.rootRequired');
|
||||
if (__arch !== 'amd64') log.fatal('error.unsupportedArch', __arch);
|
||||
if (__os !== 'linux') log.fatal('error.unsupportedOS', __os);
|
||||
if (!__env.HOME) log.fatal('$HOME not found');
|
||||
if (!fs.exist('/etc/os-release')) log.fatal('error.osreleaseNotFound');
|
||||
const osinfoFile = fs.readfile('/etc/os-release');
|
||||
const lines = osinfoFile.split('\n');
|
||||
const values = {};
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
const d = line.split('=');
|
||||
if (d[1].startsWith('"')) values[d[0].toLowerCase()] = d[1].substr(1, d[1].length - 2);
|
||||
else values[d[0].toLowerCase()] = d[1];
|
||||
}
|
||||
let avx2 = true;
|
||||
const cpuInfoFile = fs.readfile('/proc/cpuinfo');
|
||||
if (!cpuInfoFile.includes('avx2')) {
|
||||
avx2 = false;
|
||||
log.warn('warn.avx2');
|
||||
}
|
||||
let migration;
|
||||
let retry = 0;
|
||||
log.info('install.start');
|
||||
let DATABASE_PASSWORD = randomstring(32);
|
||||
// TODO read from args
|
||||
const CN = true;
|
||||
|
||||
const nixBin = `${__env.HOME}/.nix-profile/bin`;
|
||||
const mount = `\
|
||||
mount:
|
||||
- type: bind
|
||||
source: ${nixBin}
|
||||
target: /bin
|
||||
readonly: true
|
||||
- type: bind
|
||||
source: ${nixBin}
|
||||
target: /usr/bin
|
||||
readonly: true
|
||||
- type: bind
|
||||
source: /nix
|
||||
target: /nix
|
||||
readonly: true
|
||||
- type: bind
|
||||
source: /dev/null
|
||||
target: /dev/null
|
||||
- type: bind
|
||||
source: /dev/urandom
|
||||
target: /dev/urandom
|
||||
- type: tmpfs
|
||||
target: /w
|
||||
data: size=512m,nr_inodes=8k
|
||||
- type: tmpfs
|
||||
target: /tmp
|
||||
data: size=512m,nr_inodes=8k
|
||||
proc: true
|
||||
workDir: /w
|
||||
hostName: executor_server
|
||||
domainName: executor_server
|
||||
uid: 1536
|
||||
gid: 1536
|
||||
`;
|
||||
|
||||
function removeOptionalEsbuildDeps() {
|
||||
const yarnGlobalPath = exec('yarn global dir').output?.trim() || '';
|
||||
if (!yarnGlobalPath) return false;
|
||||
const file = fs.readfile(`${yarnGlobalPath}/package.json`);
|
||||
const data = JSON.parse(file);
|
||||
data.resolutions = data.resolutions || {};
|
||||
Object.assign(data.resolutions, Object.fromEntries([
|
||||
'@esbuild/linux-loong64',
|
||||
...['android', 'windows', 'darwin', 'freebsd'].flatMap((i) => [`${i}-64`, `${i}-arm64`, `${i}-32`]),
|
||||
...['32', 'arm', 'arm64', 'mips64', 'ppc64', 'riscv64', 's390x'].map((i) => `esbuild-linux-${i}`),
|
||||
...['netbsd', 'openbsd', 'sunos'].map((i) => `esbuild-${i}-64`),
|
||||
].map((i) => [i, 'link:/dev/null'])));
|
||||
fs.writefile(`${yarnGlobalPath}/package.json`, JSON.stringify(data, null, 2));
|
||||
return true;
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
init: 'install.preparing',
|
||||
operations: [
|
||||
'mkdir -p /data/db /data/file ~/.hydro',
|
||||
() => {
|
||||
if (locale === 'zh') {
|
||||
log.info('扫码加入QQ群:');
|
||||
exec('echo https://qm.qq.com/cgi-bin/qm/qr\\?k\\=0aTZfDKURRhPBZVpTYBohYG6P6sxABTw | qrencode -o - -m 2 -t UTF8', {}, 0);
|
||||
}
|
||||
log.info(`Hydro includes anonymous system telemetry,
|
||||
which helps developers figure out the most commonly used operating system and platform.
|
||||
To disable this feature, checkout our sourcecode.`);
|
||||
},
|
||||
'bash -c "bash <(curl https://hydro.ac/nix.sh)"',
|
||||
() => {
|
||||
setenv('PATH', `${__env.HOME}/.nix-profile/bin:${__env.PATH}`);
|
||||
},
|
||||
'nix-env -iA nixpkgs.coreutils nixpkgs.bash nixpkgs.unzip nixpkgs.zip nixpkgs.diffutils nixpkgs.qrencode',
|
||||
() => {
|
||||
return; // Not implemented yet
|
||||
if (fs.exist('/home/judge/src')) {
|
||||
const res = cli.prompt('migrate.hustojFound');
|
||||
if (res.toLowerCase().trim() === 'y') migration = 'hustoj';
|
||||
}
|
||||
|
||||
const docker = !exec1('docker -v').code;
|
||||
if (!docker) return;
|
||||
// TODO check more places
|
||||
if (fs.exist('/root/OnlineJudgeDeploy/docker-compose.yml')) {
|
||||
const res = cli.prompt('migrate.qduojFound');
|
||||
if (res.toLowerCase().trim() === 'y') migration = 'qduoj';
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.mongodb',
|
||||
skip: () => !exec('mongod --version').code,
|
||||
operations: [
|
||||
`nix-env -iA hydro.mongodb${avx2 ? 5 : 4}${CN ? '-cn' : ''}`,
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.mongodb-tools',
|
||||
skip: () => !exec('mongoexport --version').code,
|
||||
operations: [
|
||||
'nix-env -iA nixpkgs.mongodb-tools',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.mongo',
|
||||
skip: () => !exec('mongo --version').code,
|
||||
operations: [
|
||||
`nix-env -iA hydro.mongosh${avx2 ? 5 : 4}${CN ? '-cn' : ''}`,
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.nodejs',
|
||||
skip: () => !exec('node -v').code && !exec('yarn -v').code,
|
||||
operations: [
|
||||
'nix-env -iA nixpkgs.nodejs nixpkgs.yarn nixpkgs.esbuild',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.pm2',
|
||||
skip: () => !exec('pm2 -v').code,
|
||||
operations: [
|
||||
['yarn global add pm2', { retry: true }],
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.compiler',
|
||||
operations: [
|
||||
'nix-env -iA nixpkgs.gcc nixpkgs.fpc nixpkgs.python3',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.sandbox',
|
||||
skip: () => !exec('hydro-sandbox --help').code,
|
||||
operations: [
|
||||
'nix-env -iA hydro.sandbox',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.hydro',
|
||||
operations: [
|
||||
() => removeOptionalEsbuildDeps(),
|
||||
['yarn global add hydrooj @hydrooj/ui-default @hydrooj/hydrojudge', { retry: true }],
|
||||
() => fs.writefile(`${__env.HOME}/.hydro/addon.json`, '["@hydrooj/ui-default","@hydrooj/hydrojudge"]'),
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.createDatabaseUser',
|
||||
skip: () => fs.exist(`${__env.HOME}/.hydro/config.json`),
|
||||
operations: [
|
||||
'pm2 start mongod',
|
||||
() => sleep(5000),
|
||||
() => fs.writefile('/tmp/createUser.js', `\
|
||||
db.createUser({
|
||||
user: 'hydro',
|
||||
pwd: '${DATABASE_PASSWORD}',
|
||||
roles: [{ role: 'readWrite', db: 'hydro' }]
|
||||
})`),
|
||||
'mongo 127.0.0.1:27017/hydro /tmp/createUser.js',
|
||||
() => fs.writefile(`${__env.HOME}/.hydro/config.json`, JSON.stringify({
|
||||
host: '127.0.0.1',
|
||||
port: 27017,
|
||||
name: 'hydro',
|
||||
username: 'hydro',
|
||||
password: DATABASE_PASSWORD,
|
||||
})),
|
||||
'pm2 stop mongod',
|
||||
'pm2 del mongod',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.starting',
|
||||
operations: [
|
||||
['pm2 stop all', { ignore: true }],
|
||||
() => fs.writefile(`${__env.HOME}/.hydro/mount.yaml`, mount),
|
||||
'pm2 start mongod --name mongodb -- --auth --bind_ip 0.0.0.0',
|
||||
() => sleep(1000),
|
||||
`pm2 start bash --name hydro-sandbox -- -c "ulimit -s unlimited && hydro-sandbox -mount-conf ${__env.HOME}/.hydro/mount.yaml"`,
|
||||
'pm2 start hydrooj',
|
||||
'pm2 startup',
|
||||
'pm2 save',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.migrateHustoj',
|
||||
skip: () => migration !== 'hustoj',
|
||||
silent: true,
|
||||
operations: [
|
||||
['yarn global add @hydrooj/migrate', { retry: true }],
|
||||
'hydrooj addon add @hydrooj/migrate',
|
||||
() => {
|
||||
const config = {
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
name: 'jol',
|
||||
dataDir: '/home/judge/data',
|
||||
// TODO: auto-read uname&passwd&contestType
|
||||
username: 'debian-sys-maint',
|
||||
password: '',
|
||||
contestType: 'acm',
|
||||
};
|
||||
exec2(`hydrooj cli script migrateHustoj ${JSON.stringify(config)}`);
|
||||
},
|
||||
'pm2 restart hydrooj',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.done',
|
||||
operations: [
|
||||
() => {
|
||||
DATABASE_PASSWORD = loadconfig(`${__env.HOME}/.hydro/config.json`).password;
|
||||
},
|
||||
() => log.info('extra.restartTerm'),
|
||||
() => log.info('extra.dbUser'),
|
||||
() => log.info('extra.dbPassword', DATABASE_PASSWORD),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
const step = steps[i];
|
||||
if (!step.silent) log.info(step.init);
|
||||
if (!(step.skip && step.skip())) {
|
||||
for (let op of step.operations) {
|
||||
if (!(op instanceof Array)) op = [op, {}];
|
||||
if (op[0].toString().startsWith('nix-env')) op[1].retry = true;
|
||||
if (typeof op[0] === 'string') {
|
||||
retry = 0;
|
||||
exec(op[0], op[1]);
|
||||
while (__code !== 0 && op[1].ignore !== true) {
|
||||
if (op[1].retry && retry < 30) {
|
||||
log.warn('Retry... (%s)', op[0]);
|
||||
exec(op[0], op[1]);
|
||||
retry++;
|
||||
} else log.fatal('Error when running %s', op[0]);
|
||||
}
|
||||
} else {
|
||||
retry = 0;
|
||||
let res = op[0](op[1]);
|
||||
while (res === 'retry') {
|
||||
if (retry < 30) {
|
||||
log.warn('Retry...');
|
||||
res = op[0](op[1]);
|
||||
retry++;
|
||||
} else log.fatal('Error installing');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!step.silent) log.info('info.skip');
|
||||
}
|
||||
|
||||
exit(0);
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
echo "Executing Hydro install script v3.0.0"
|
||||
echo "Hydro includes anonymous system telemetry,
|
||||
which helps developers figure out the most commonly used operating system and platform.
|
||||
To disable this feature, checkout our sourcecode."
|
||||
mkdir -p /data/db /data/file ~/.hydro
|
||||
bash <(curl https://hydro.ac/nix.sh)
|
||||
export PATH=$HOME/.nix-profile/bin:$PATH
|
||||
nix-env -iA nixpkgs.nodejs nixpkgs.pm2 nixpkgs.yarn nixpkgs.esbuild nixpkgs.coreutils nixpkgs.bash nixpkgs.unzip nixpkgs.zip nixpkgs.diffutils nixpkgs.qrencode
|
||||
echo "扫码加入QQ群:"
|
||||
echo https://qm.qq.com/cgi-bin/qm/qr\?k\=0aTZfDKURRhPBZVpTYBohYG6P6sxABTw | qrencode -o - -m 2 -t UTF8
|
||||
echo "// File created by Hydro install script\n" >/tmp/install.js
|
||||
cat >/tmp/install.b64 << EOF123
|
||||
%PLACEHOLDER%
|
||||
EOF123
|
||||
cat /tmp/install.b64 | base64 -d >>/tmp/install.js
|
||||
node /tmp/install.js
|
||||
set +e
|
||||
@@ -0,0 +1,295 @@
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable no-sequences */
|
||||
import { execSync, ExecSyncOptions } from 'child_process';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
const exec = (command: string, args?: ExecSyncOptions) => {
|
||||
try {
|
||||
return {
|
||||
output: execSync(command, args).toString(),
|
||||
code: 0,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
code: e.status,
|
||||
message: e.message,
|
||||
};
|
||||
}
|
||||
};
|
||||
const sleep = (t: number) => new Promise((r) => { setTimeout(r, t); });
|
||||
const locales = {
|
||||
zh: {
|
||||
'install.start': '开始运行 Hydro 安装工具',
|
||||
'warn.avx2': '检测到您的 CPU 不支持 avx2 指令集,将使用 mongodb@v4.4',
|
||||
'error.rootRequired': '请先使用 sudo su 切换到 root 用户后再运行该工具。',
|
||||
'error.unsupportedArch': '不支持的架构 %s ,请尝试手动安装。',
|
||||
'error.osreleaseNotFound': '无法获取系统版本信息(/etc/os-release 文件未找到),请尝试手动安装。',
|
||||
'error.unsupportedOS': '不支持的操作系统 %s ,请尝试手动安装,',
|
||||
'install.preparing': '正在初始化安装...',
|
||||
'install.mongodb': '正在安装 mongodb...',
|
||||
'install.createDatabaseUser': '正在创建数据库用户...',
|
||||
'install.compiler': '正在安装编译器...',
|
||||
'install.hydro': '正在安装 Hydro...',
|
||||
'install.done': 'Hydro 安装成功!',
|
||||
'extra.dbUser': '数据库用户名: hydro',
|
||||
'extra.dbPassword': '数据库密码: %s',
|
||||
'info.skip': '步骤已跳过。',
|
||||
},
|
||||
en: {
|
||||
'install.start': 'Starting Hydro installation tool',
|
||||
'warn.avx2': 'Your CPU does not support avx2, will use mongodb@v4.4',
|
||||
'error.rootRequired': 'Please run this tool as root user.',
|
||||
'error.unsupportedArch': 'Unsupported architecture %s, please try to install manually.',
|
||||
'error.osreleaseNotFound': 'Unable to get system version information (/etc/os-release file not found), please try to install manually.',
|
||||
'error.unsupportedOS': 'Unsupported operating system %s, please try to install manually.',
|
||||
'install.preparing': 'Initializing installation...',
|
||||
'install.mongodb': 'Installing mongodb...',
|
||||
'install.createDatabaseUser': 'Creating database user...',
|
||||
'install.compiler': 'Installing compiler...',
|
||||
'install.hydro': 'Installing Hydro...',
|
||||
'install.done': 'Hydro installation completed!',
|
||||
'extra.dbUser': 'Database username: hydro',
|
||||
'extra.dbPassword': 'Database password: %s',
|
||||
'info.skip': 'Step skipped.',
|
||||
},
|
||||
};
|
||||
let locale = process.env.LANG?.includes('zh') ? 'zh' : 'en';
|
||||
if (process.env.TERM === 'linux') locale = 'en';
|
||||
const processLog = (orig) => (str, ...args) => (orig(locales[locale][str] || str, ...args), 0);
|
||||
const log = {
|
||||
info: processLog(console.log),
|
||||
warn: processLog(console.warn),
|
||||
fatal: (str, ...args) => (processLog(console.error)(str, ...args), process.exit(1)),
|
||||
};
|
||||
|
||||
if (!process.getuid) log.fatal('error.unsupportedOs');
|
||||
else if (process.getuid() !== 0) log.fatal('error.rootRequired');
|
||||
if (!['x64', 'arm64'].includes(process.arch)) log.fatal('error.unsupportedArch', process.arch);
|
||||
if (!process.env.HOME) log.fatal('$HOME not found');
|
||||
if (!existsSync('/etc/os-release')) log.fatal('error.osreleaseNotFound');
|
||||
const osinfoFile = readFileSync('/etc/os-release', 'utf-8');
|
||||
const lines = osinfoFile.split('\n');
|
||||
const values = {};
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
const d = line.split('=');
|
||||
if (d[1].startsWith('"')) values[d[0].toLowerCase()] = d[1].substr(1, d[1].length - 2);
|
||||
else values[d[0].toLowerCase()] = d[1];
|
||||
}
|
||||
let avx2 = true;
|
||||
const cpuInfoFile = readFileSync('/proc/cpuinfo', 'utf-8');
|
||||
if (!cpuInfoFile.includes('avx2')) {
|
||||
avx2 = false;
|
||||
log.warn('warn.avx2');
|
||||
}
|
||||
let migration;
|
||||
let retry = 0;
|
||||
log.info('install.start');
|
||||
const defaultDict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
|
||||
String.random = function random(digit = 32, dict = defaultDict) {
|
||||
let str = '';
|
||||
for (let i = 1; i <= digit; i++) str += dict[Math.floor(Math.random() * dict.length)];
|
||||
return str;
|
||||
};
|
||||
let DATABASE_PASSWORD = String.random(32);
|
||||
// TODO read from args
|
||||
const CN = true;
|
||||
|
||||
const nixBin = `${process.env.HOME}/.nix-profile/bin`;
|
||||
const entry = (source: string, target = source, ro = true) => `\
|
||||
- type: bind
|
||||
source: ${source}
|
||||
target: ${target}${ro ? '\n readonly: true' : ''}`;
|
||||
const mount = `mount:
|
||||
${entry(nixBin, '/bin')}
|
||||
${entry(nixBin, '/usr/bin')}
|
||||
${entry('/nix', '/nix')}
|
||||
${entry('/dev/null', '/dev/null', false)}
|
||||
${entry('/dev/urandom', '/dev/urandom', false)}
|
||||
- type: tmpfs
|
||||
target: /w
|
||||
data: size=512m,nr_inodes=8k
|
||||
- type: tmpfs
|
||||
target: /tmp
|
||||
data: size=512m,nr_inodes=8k
|
||||
proc: true
|
||||
workDir: /w
|
||||
hostName: executor_server
|
||||
domainName: executor_server
|
||||
uid: 1536
|
||||
gid: 1536
|
||||
`;
|
||||
|
||||
function removeOptionalEsbuildDeps() {
|
||||
const yarnGlobalPath = exec('yarn global dir').output?.trim() || '';
|
||||
if (!yarnGlobalPath) return false;
|
||||
const pkgjson = `${yarnGlobalPath}/package.json`;
|
||||
const data = existsSync(pkgjson) ? require(pkgjson) : {};
|
||||
data.resolutions = data.resolutions || {};
|
||||
Object.assign(data.resolutions, Object.fromEntries([
|
||||
'@esbuild/linux-loong64',
|
||||
...['android', 'windows', 'darwin', 'freebsd'].flatMap((i) => [`${i}-64`, `${i}-arm64`, `${i}-32`]),
|
||||
...['32', 'arm', 'mips64', 'ppc64', 'riscv64', 's390x'].map((i) => `esbuild-linux-${i}`),
|
||||
...['netbsd', 'openbsd', 'sunos'].map((i) => `esbuild-${i}-64`),
|
||||
].map((i) => [i, 'link:/dev/null'])));
|
||||
exec(`mkdir -p ${yarnGlobalPath}`);
|
||||
writeFileSync(pkgjson, JSON.stringify(data, null, 2));
|
||||
return true;
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
init: 'install.preparing',
|
||||
operations: [
|
||||
() => {
|
||||
// Not implemented yet
|
||||
// if (fs.existsSync('/home/judge/src')) {
|
||||
// const res = cli.prompt('migrate.hustojFound');
|
||||
// if (res.toLowerCase().trim() === 'y') migration = 'hustoj';
|
||||
// }
|
||||
|
||||
// const docker = !exec1('docker -v').code;
|
||||
// if (!docker) return;
|
||||
// // TODO check more places
|
||||
// if (fs.exist('/root/OnlineJudgeDeploy/docker-compose.yml')) {
|
||||
// const res = cli.prompt('migrate.qduojFound');
|
||||
// if (res.toLowerCase().trim() === 'y') migration = 'qduoj';
|
||||
// }
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.mongodb',
|
||||
operations: [
|
||||
`nix-env -iA hydro.mongodb${avx2 ? 5 : 4}${CN ? '-cn' : ''}`,
|
||||
'nix-env -iA nixpkgs.mongodb-tools',
|
||||
`nix-env -iA hydro.mongosh${avx2 ? 5 : 4}${CN ? '-cn' : ''}`,
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.compiler',
|
||||
operations: [
|
||||
'nix-env -iA nixpkgs.gcc nixpkgs.fpc nixpkgs.python3',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.sandbox',
|
||||
skip: () => !exec('hydro-sandbox --help').code,
|
||||
operations: [
|
||||
'nix-env -iA hydro.sandbox',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.hydro',
|
||||
operations: [
|
||||
() => removeOptionalEsbuildDeps(),
|
||||
['yarn global add hydrooj @hydrooj/ui-default @hydrooj/hydrojudge', { retry: true }],
|
||||
() => writeFileSync(`${process.env.HOME}/.hydro/addon.json`, '["@hydrooj/ui-default","@hydrooj/hydrojudge"]'),
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.createDatabaseUser',
|
||||
skip: () => existsSync(`${process.env.HOME}/.hydro/config.json`),
|
||||
operations: [
|
||||
'pm2 start mongod',
|
||||
() => sleep(3000),
|
||||
() => writeFileSync('/tmp/createUser.js', `db.createUser(${JSON.stringify({
|
||||
user: 'hydro',
|
||||
pwd: DATABASE_PASSWORD,
|
||||
roles: [{ role: 'readWrite', db: 'hydro' }],
|
||||
})})`),
|
||||
['mongo 127.0.0.1:27017/hydro /tmp/createUser.js', { retry: true }],
|
||||
() => writeFileSync(`${process.env.HOME}/.hydro/config.json`, JSON.stringify({
|
||||
host: '127.0.0.1',
|
||||
port: 27017,
|
||||
name: 'hydro',
|
||||
username: 'hydro',
|
||||
password: DATABASE_PASSWORD,
|
||||
})),
|
||||
'pm2 stop mongod',
|
||||
'pm2 del mongod',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.starting',
|
||||
operations: [
|
||||
['pm2 stop all', { ignore: true }],
|
||||
() => writeFileSync(`${process.env.HOME}/.hydro/mount.yaml`, mount),
|
||||
'pm2 start mongod --name mongodb -- --auth --bind_ip 0.0.0.0',
|
||||
() => sleep(1000),
|
||||
`pm2 start bash --name hydro-sandbox -- -c "ulimit -s unlimited && hydro-sandbox -mount-conf ${process.env.HOME}/.hydro/mount.yaml"`,
|
||||
'pm2 start hydrooj',
|
||||
'pm2 startup',
|
||||
'pm2 save',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.migrateHustoj',
|
||||
skip: () => migration !== 'hustoj',
|
||||
silent: true,
|
||||
operations: [
|
||||
['yarn global add @hydrooj/migrate', { retry: true }],
|
||||
'hydrooj addon add @hydrooj/migrate',
|
||||
() => {
|
||||
const config = {
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
name: 'jol',
|
||||
dataDir: '/home/judge/data',
|
||||
// TODO: auto-read uname&passwd&contestType
|
||||
username: 'debian-sys-maint',
|
||||
password: '',
|
||||
contestType: 'acm',
|
||||
};
|
||||
exec(`hydrooj cli script migrateHustoj ${JSON.stringify(config)}`);
|
||||
},
|
||||
'pm2 restart hydrooj',
|
||||
],
|
||||
},
|
||||
{
|
||||
init: 'install.done',
|
||||
operations: [
|
||||
() => {
|
||||
DATABASE_PASSWORD = require(`${process.env.HOME}/.hydro/config.json`).password;
|
||||
},
|
||||
() => log.info('extra.dbUser'),
|
||||
() => log.info('extra.dbPassword', DATABASE_PASSWORD),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
async function main() {
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
const step = steps[i];
|
||||
if (!step.silent) log.info(step.init);
|
||||
if (!(step.skip && step.skip())) {
|
||||
for (let op of step.operations) {
|
||||
if (!(op instanceof Array)) op = [op, {}] as any;
|
||||
if (op[0].toString().startsWith('nix-env')) op[1].retry = true;
|
||||
if (typeof op[0] === 'string') {
|
||||
retry = 0;
|
||||
let res = exec(op[0], { stdio: 'inherit' });
|
||||
while (res.code && op[1].ignore !== true) {
|
||||
if (op[1].retry && retry < 30) {
|
||||
log.warn('Retry... (%s)', op[0]);
|
||||
res = exec(op[0], { stdio: 'inherit' });
|
||||
retry++;
|
||||
} else log.fatal('Error when running %s', op[0]);
|
||||
}
|
||||
} else {
|
||||
retry = 0;
|
||||
let res = op[0](op[1]);
|
||||
while (res === 'retry') {
|
||||
if (retry < 30) {
|
||||
log.warn('Retry...');
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
res = await op[0](op[1]);
|
||||
retry++;
|
||||
} else log.fatal('Error installing');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!step.silent) log.info('info.skip');
|
||||
}
|
||||
}
|
||||
main().catch(log.fatal);
|
||||
Vendored
-778
@@ -1,778 +0,0 @@
|
||||
/** 全局变量对象 */
|
||||
declare var global: any;
|
||||
|
||||
/** 是否 self-contained 方式执行 */
|
||||
declare const __selfcontained: boolean;
|
||||
|
||||
/** CPU数量 */
|
||||
declare const __cpucount: number;
|
||||
|
||||
/** 操作系统类型,darwin, freebsd, linux, windows */
|
||||
declare const __os: string;
|
||||
|
||||
/** 处理器架构,386, amd64, arm, s390x */
|
||||
declare const __arch: string;
|
||||
|
||||
/** JSSH版本号 */
|
||||
declare const __version: string;
|
||||
|
||||
/** JSSH二进制文件路径 */
|
||||
declare const __bin: string;
|
||||
|
||||
/** 当前进程PID */
|
||||
declare const __pid: number;
|
||||
|
||||
/** 临时文件目录 */
|
||||
declare const __tmpdir: string;
|
||||
|
||||
/** 当前用户HOME目录 */
|
||||
declare const __homedir: string;
|
||||
|
||||
/** 当前用户名 */
|
||||
declare const __user: string;
|
||||
|
||||
/** 当前主机名 */
|
||||
declare const __hostname: string;
|
||||
|
||||
/** 当前脚本目录名 */
|
||||
declare var __dirname: string;
|
||||
|
||||
/** 当前脚本文件名 */
|
||||
declare var __filename: string;
|
||||
|
||||
/** 当前命令行参数 */
|
||||
declare const __args: string[];
|
||||
|
||||
/** 当前环境变量 */
|
||||
declare const __env: Record<string, string>;
|
||||
|
||||
/** 最近一次执行命令输出的内容 */
|
||||
declare const __output: string;
|
||||
|
||||
/** 最近一次执行命令输出内容的字节数 */
|
||||
declare const __outputbytes: number;
|
||||
|
||||
/** 最近一次执行命令进程退出code */
|
||||
declare const __code: number;
|
||||
|
||||
/** 已自动加载的全局脚本文件列表 */
|
||||
declare const __globalfiles: string[];
|
||||
|
||||
/**
|
||||
* 加载 CommonJS 模块
|
||||
* @param modulename 模块名称
|
||||
* @return 模块内容
|
||||
*/
|
||||
// @ts-ignore
|
||||
declare function require(modulename: string): any;
|
||||
|
||||
/**
|
||||
* 以eval方式执行指定脚本文件
|
||||
* @param filename 文件路径或URL
|
||||
* @param content 文件内容,若指定此参数则无需实际读取文件内容
|
||||
* @return 返回结果
|
||||
*/
|
||||
declare function evalfile(filename: string, content?: string): any;
|
||||
|
||||
/**
|
||||
* 获取指定字符串的字节长度
|
||||
* @param data 字符串
|
||||
* @return 字节长度
|
||||
*/
|
||||
declare function bytesize(data: string): number;
|
||||
|
||||
/**
|
||||
* 读取标准输入的内容,返回字符串
|
||||
*/
|
||||
declare function stdin(): string;
|
||||
|
||||
/**
|
||||
* 读取标准输入的内容,返回 Uint8Array
|
||||
*/
|
||||
declare function stdinbytes(): Uint8Array;
|
||||
|
||||
/**
|
||||
* 格式化文本内容
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 格式化后的文本内容
|
||||
*/
|
||||
declare function format(format: any, ...args: any[]): string;
|
||||
|
||||
/**
|
||||
* 输出到控制台
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
declare function print(format: any, ...args: any[]): boolean;
|
||||
|
||||
/**
|
||||
* 输出一行到控制台
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
declare function println(format: any, ...args: any[]): boolean;
|
||||
|
||||
/**
|
||||
* 在stdout输出一行日志
|
||||
* @param message
|
||||
* @return 是否成功
|
||||
*/
|
||||
declare function stdoutlog(message: string): boolean;
|
||||
|
||||
/**
|
||||
* 在stderr输出一行日志
|
||||
* @param message
|
||||
* @return 是否成功
|
||||
*/
|
||||
declare function stderrlog(message: string): boolean;
|
||||
|
||||
/**
|
||||
* 从控制台获取用户一行的字符串输入
|
||||
* @return 用户输入的字符串
|
||||
*/
|
||||
declare function readline(): string;
|
||||
|
||||
/**
|
||||
* 睡眠
|
||||
* @param milliseconds 毫秒
|
||||
* @return 毫秒
|
||||
*/
|
||||
declare function sleep(milliseconds: number): number;
|
||||
|
||||
/**
|
||||
* 退出进程
|
||||
* @param code 进程退出code
|
||||
*/
|
||||
declare function exit(code?: number): void;
|
||||
|
||||
/**
|
||||
* 加载配置文件,支持*.json,*.yaml,*.toml格式
|
||||
* @param file 文件名
|
||||
* @param format 格式,如果没有指定则自动根据文件后缀名判断
|
||||
* @return 配置内容
|
||||
*/
|
||||
declare function loadconfig(
|
||||
file: string,
|
||||
format?: "json" | "yaml" | "toml"
|
||||
): any;
|
||||
|
||||
/**
|
||||
* base64编码
|
||||
* @param data 原始内容
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
declare function base64encode(data: string): string;
|
||||
|
||||
/**
|
||||
* base64解码
|
||||
* @param data 编码后的内容
|
||||
* @return 原始内容
|
||||
*/
|
||||
declare function base64decode(data: string): string;
|
||||
|
||||
/**
|
||||
* MD5编码
|
||||
* @param data 原始内容
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
declare function md5(data: string): string;
|
||||
|
||||
/**
|
||||
* SHA1编码
|
||||
* @param data 原始内容
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
declare function sha1(data: string): string;
|
||||
|
||||
/**
|
||||
* SAH256编码
|
||||
* @param data 原始内容
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
declare function sha256(data: string): string;
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
* @param size 长度
|
||||
* @param chars 可用的字符集,默认为 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
|
||||
* @return 生成的随机字符串
|
||||
*/
|
||||
declare function randomstring(size: number, chars?: string): string;
|
||||
|
||||
/**
|
||||
* 格式化日期时间,格式参考PHP的 date() 函数,文档参考 https://locutus.io/php/datetime/date/
|
||||
* @param format 格式
|
||||
* @param timestamp 秒时间戳或Date对象
|
||||
* @return 日期时间字符串
|
||||
*/
|
||||
declare function formatdate(format: string, timestamp?: number | Date): string;
|
||||
|
||||
/**
|
||||
* 深度合并两个对象
|
||||
* @param target 第一个对象
|
||||
* @param src 第二个对象,仅当第一个对象不存在该键才覆盖
|
||||
* @return 合并后的新对象
|
||||
*/
|
||||
declare function deepmerge(target: any, src: any): any;
|
||||
|
||||
/**
|
||||
* 设置环境变量
|
||||
* @param name 环境变量名称
|
||||
* @param value 环境变量值
|
||||
* @return 是否成功
|
||||
*/
|
||||
declare function setenv(name: string, value: string): boolean;
|
||||
|
||||
/**
|
||||
* 执行命令
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @param mode 输出模式,0=直接pipe输出结果到标准输出,1=执行中途不直接输出,存储到返回结果{output}中,2=pipe输出并且存储到返回结果{output}中
|
||||
* @return 进程信息
|
||||
*/
|
||||
declare function exec(
|
||||
cmd: string,
|
||||
env?: Record<string, string>,
|
||||
mode?: number
|
||||
): ExecResult;
|
||||
|
||||
/**
|
||||
* 执行命令 mode=1
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @return 进程信息
|
||||
*/
|
||||
declare function exec1(cmd: string, env?: Record<string, string>): ExecResult;
|
||||
|
||||
/**
|
||||
* 执行命令 mode=2
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @return 进程信息
|
||||
*/
|
||||
declare function exec2(cmd: string, env?: Record<string, string>): ExecResult;
|
||||
|
||||
/**
|
||||
* 后台执行命令
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @param mode 输出模式,0=直接pipe输出结果到标准输出,1=执行中途不直接输出,存储到返回结果{output}中,2=pipe输出并且存储到返回结果{output}中
|
||||
* @return 进程信息
|
||||
*/
|
||||
declare function bgexec(
|
||||
cmd: string,
|
||||
env?: Record<string, string>,
|
||||
mode?: number
|
||||
): ExecResult;
|
||||
|
||||
/**
|
||||
* 改变当前工作目录
|
||||
* @param dir 目录路径
|
||||
* @return 是否成功
|
||||
*/
|
||||
declare function chdir(dir: string): boolean;
|
||||
|
||||
/**
|
||||
* 改变当前工作目录
|
||||
* @param dir 目录路径
|
||||
* @return 是否成功
|
||||
*/
|
||||
declare function cd(dir: string): boolean;
|
||||
|
||||
/**
|
||||
* 获取当前工作目录
|
||||
* @return 当前工作目录路径
|
||||
*/
|
||||
declare function cwd(): string;
|
||||
|
||||
/**
|
||||
* 获取当前工作目录
|
||||
* @return 当前工作目录路径
|
||||
*/
|
||||
declare function pwd(): string;
|
||||
|
||||
/**
|
||||
* 获得网络接口信息
|
||||
* @return 网络接口数组
|
||||
*/
|
||||
declare function networkinterfaces(): Record<string, NetworkInterface>;
|
||||
|
||||
interface NetworkInterface {
|
||||
index: number;
|
||||
mac: string;
|
||||
list: {
|
||||
address: string;
|
||||
netmask: string;
|
||||
family: string;
|
||||
cidr: string;
|
||||
internal: boolean;
|
||||
multicast: boolean;
|
||||
unspecified: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
/** 文件相关操作模块 */
|
||||
declare const fs: FsModule;
|
||||
|
||||
/** 文件路径相关操作模块 */
|
||||
declare const path: PathModule;
|
||||
|
||||
/** 命令行参数相关操作模块 */
|
||||
declare const cli: CliModule;
|
||||
|
||||
/** HTTP相关操作模块 */
|
||||
declare const http: HttpModule;
|
||||
|
||||
/** 日志相关操作模块 */
|
||||
declare const log: LogModule;
|
||||
|
||||
/** SSH相关操作模块 */
|
||||
declare const ssh: SshModule;
|
||||
|
||||
/** Socket相关操作模块 */
|
||||
declare const socket: SocketModule;
|
||||
|
||||
interface FsModule {
|
||||
/**
|
||||
* 读取目录
|
||||
* @param path 路径
|
||||
* @return 文件信息列表
|
||||
*/
|
||||
readdir(path: string): FileStat[];
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param path 路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
readfile(path: string): string;
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param path 路径
|
||||
* @return 文件内容
|
||||
*/
|
||||
readfilebytes(path: string): Uint8Array;
|
||||
|
||||
/**
|
||||
* 读取文件信息
|
||||
* @param path 路径
|
||||
* @return 文件信息
|
||||
*/
|
||||
stat(path: string): FileStat;
|
||||
|
||||
/**
|
||||
* 判断文件是否存在
|
||||
* @param path 路径
|
||||
* @return 是否存在
|
||||
*/
|
||||
exist(path: string): boolean;
|
||||
|
||||
/**
|
||||
* 写入文件
|
||||
* @param path 路径
|
||||
* @param data 内容
|
||||
* @return 是否成功
|
||||
*/
|
||||
writefile(path: string, data: string | Uint8Array): boolean;
|
||||
|
||||
/**
|
||||
* 追加内容到文件末尾
|
||||
* @param path 路径
|
||||
* @param data 内容
|
||||
* @return 是否成功
|
||||
*/
|
||||
appendfile(path: string, data: string | Uint8Array): boolean;
|
||||
}
|
||||
|
||||
interface FileStat {
|
||||
/** 文件名 */
|
||||
name: string;
|
||||
/** 是否为目录 */
|
||||
isdir: boolean;
|
||||
/** 文件mode,如0644 */
|
||||
mode: number;
|
||||
/** 文件最后修改秒时间戳 */
|
||||
modtime: number;
|
||||
/** 文件大小 */
|
||||
size: number;
|
||||
}
|
||||
|
||||
interface PathModule {
|
||||
/**
|
||||
* 拼接文件路径
|
||||
* @param args 子路径列表
|
||||
* @return 拼接后的路径
|
||||
*/
|
||||
join(...args: string[]): string;
|
||||
|
||||
/**
|
||||
* 获取绝对路径
|
||||
* @param path 原始路径
|
||||
* @return 绝对路径
|
||||
*/
|
||||
abs(path: string): string;
|
||||
|
||||
/**
|
||||
* 获取文件名
|
||||
* @param path 路径
|
||||
* @return 文件名
|
||||
*/
|
||||
base(path: string): string;
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
* @param path 路径
|
||||
* @return 文件扩展名
|
||||
*/
|
||||
ext(path: string): string;
|
||||
|
||||
/**
|
||||
* 获取路径的上级目录
|
||||
* @param path 路径
|
||||
* @return 上级目录
|
||||
*/
|
||||
dir(path: string): string;
|
||||
}
|
||||
|
||||
interface CliModule {
|
||||
/**
|
||||
* 获取指定名称的参数
|
||||
* @param flag 参数名称
|
||||
* @return 参数值
|
||||
*/
|
||||
get(flag: string): string;
|
||||
|
||||
/**
|
||||
* 获取指定索引的参数
|
||||
* @param index 参数索引
|
||||
* @return 参数值
|
||||
*/
|
||||
get(index: number): string;
|
||||
|
||||
/**
|
||||
* 获取指定名称的参数的布尔值,f,false,0表示假,其余为真
|
||||
* @param flag 参数名称
|
||||
* @return 参数值
|
||||
*/
|
||||
bool(flag: string): boolean;
|
||||
|
||||
/**
|
||||
* 获取args参数列表
|
||||
* @return 参数列表
|
||||
*/
|
||||
args(): string[];
|
||||
|
||||
/**
|
||||
* 获取opts参数
|
||||
* @return 参数Map
|
||||
*/
|
||||
opts(): Record<string, string>;
|
||||
|
||||
/**
|
||||
* 等待用户输入一行,按Enter确认
|
||||
* @param message 提示
|
||||
* @return 输入的内容
|
||||
*/
|
||||
prompt(message?: string): string;
|
||||
|
||||
/**
|
||||
* 注册子命令处理函数
|
||||
* @param name 命令名称,* 表示其他情况
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
subcommand(name: string, callback: () => void): void;
|
||||
|
||||
/**
|
||||
* 开始解析执行子命令
|
||||
*/
|
||||
subcommandstart(): void;
|
||||
}
|
||||
|
||||
interface HttpModule {
|
||||
/**
|
||||
* 设置HTTP请求的超时时间
|
||||
* @param milliseconds 毫秒
|
||||
* @return 毫秒
|
||||
*/
|
||||
timeout(milliseconds: number): number;
|
||||
|
||||
/**
|
||||
* 发送HTTP请求
|
||||
* @param method 请求方法
|
||||
* @param url 请求URL
|
||||
* @param headers 请求头
|
||||
* @param body 请求体
|
||||
* @return 响应结果
|
||||
*/
|
||||
request(
|
||||
method: string,
|
||||
url: String,
|
||||
headers?: Record<string, string>,
|
||||
body?: string
|
||||
): HttpResponse;
|
||||
|
||||
/**
|
||||
* 通过HTTP下载文件
|
||||
* @param url 文件地址
|
||||
* @param filename 目标文件名,如果未指定则创建临时文件
|
||||
* @return 存储的文件名
|
||||
*/
|
||||
download(url: string, filename?: string): string;
|
||||
|
||||
/**
|
||||
* 发送HTTP GET请求
|
||||
* @param url 请求URL
|
||||
* @param headers 请求头
|
||||
* @return 响应结果
|
||||
*/
|
||||
get(url: String, headers?: Record<string, string>): HttpResponse;
|
||||
|
||||
/**
|
||||
* 发送HTTP HEAD请求
|
||||
* @param url 请求URL
|
||||
* @param headers 请求头
|
||||
* @return 响应结果
|
||||
*/
|
||||
head(url: String, headers?: Record<string, string>): HttpResponse;
|
||||
|
||||
/**
|
||||
* 发送HTTP OPTIONS请求
|
||||
* @param url 请求URL
|
||||
* @param headers 请求头
|
||||
* @return 响应结果
|
||||
*/
|
||||
options(url: String, headers?: Record<string, string>): HttpResponse;
|
||||
|
||||
/**
|
||||
* 发送HTTP POST请求
|
||||
* @param url 请求URL
|
||||
* @param headers 请求头
|
||||
* @param body 请求体
|
||||
* @return 响应结果
|
||||
*/
|
||||
post(
|
||||
url: String,
|
||||
headers?: Record<string, string>,
|
||||
body?: string
|
||||
): HttpResponse;
|
||||
|
||||
/**
|
||||
* 发送HTTP PUT请求
|
||||
* @param url 请求URL
|
||||
* @param headers 请求头
|
||||
* @param body 请求体
|
||||
* @return 响应结果
|
||||
*/
|
||||
put(
|
||||
url: String,
|
||||
headers?: Record<string, string>,
|
||||
body?: string
|
||||
): HttpResponse;
|
||||
|
||||
/**
|
||||
* 发送HTTP DELETE请求
|
||||
* @param url 请求URL
|
||||
* @param headers 请求头
|
||||
* @param body 请求体
|
||||
* @return 响应结果
|
||||
*/
|
||||
delete(
|
||||
url: String,
|
||||
headers?: Record<string, string>,
|
||||
body?: string
|
||||
): HttpResponse;
|
||||
}
|
||||
|
||||
interface HttpResponse {
|
||||
/** 状态码 */
|
||||
status: number;
|
||||
/** 响应头 */
|
||||
headers: Record<string, string | string[]>;
|
||||
/** 响应体 */
|
||||
body: string;
|
||||
}
|
||||
|
||||
interface LogModule {
|
||||
/**
|
||||
* 日志输出到控制台
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
debug(format: any, ...args: any[]): boolean;
|
||||
|
||||
/**
|
||||
* 日志输出到控制台
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
info(format: any, ...args: any[]): boolean;
|
||||
|
||||
/**
|
||||
* 日志输出到控制台
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
warn(format: any, ...args: any[]): boolean;
|
||||
|
||||
/**
|
||||
* 日志输出到控制台
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
error(format: any, ...args: any[]): boolean;
|
||||
|
||||
/**
|
||||
* 日志输出到控制台,并结束进程
|
||||
* @param format 模板,支持%s等格式
|
||||
* @param args 参数列表
|
||||
* @return 是否成功
|
||||
*/
|
||||
fatal(format: any, ...args: any[]): boolean;
|
||||
}
|
||||
|
||||
interface ExecResult {
|
||||
/**
|
||||
* 进程PID
|
||||
*/
|
||||
pid: number;
|
||||
/**
|
||||
* 进程退出code
|
||||
*/
|
||||
code?: number;
|
||||
/**
|
||||
* 进程输出内容,仅当combineOutput=true时有效
|
||||
*/
|
||||
output?: string;
|
||||
/**
|
||||
* 进出输出内容字节数,仅当combineOutput=true时有效
|
||||
*/
|
||||
outputbytes?: number;
|
||||
}
|
||||
|
||||
interface SshModule {
|
||||
/**
|
||||
* 设置SSH配置
|
||||
* @param name 配置名
|
||||
* @param value 配置值
|
||||
* @return 是否成功
|
||||
*/
|
||||
set(
|
||||
name: "user" | "password" | "key" | "keypass" | "auth" | "timeout" | "port",
|
||||
value: any
|
||||
): boolean;
|
||||
|
||||
/**
|
||||
* 连接到指定服务器
|
||||
* @param host 服务器地址
|
||||
* @return 是否成功
|
||||
*/
|
||||
open(host: string): boolean;
|
||||
|
||||
/**
|
||||
* 关闭服务器连接
|
||||
* @return 是否成功
|
||||
*/
|
||||
close(): boolean;
|
||||
|
||||
/**
|
||||
* 设置环境变量
|
||||
* @param name 环境变量名称
|
||||
* @param value 环境变量值
|
||||
* @return 是否成功
|
||||
*/
|
||||
setenv(name: string, value: string): boolean;
|
||||
|
||||
/**
|
||||
* 执行命令
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @param mode 输出模式,0=直接pipe输出结果到标准输出,1=执行中途不直接输出,存储到返回结果{output}中,2=pipe输出并且存储到返回结果{output}中
|
||||
* @return 进程信息
|
||||
*/
|
||||
exec(cmd: string, env?: Record<string, string>, mode?: number): SshExecResult;
|
||||
|
||||
/**
|
||||
* 执行命令 mode=1
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @return 进程信息
|
||||
*/
|
||||
exec1(cmd: string, env?: Record<string, string>): SshExecResult;
|
||||
|
||||
/**
|
||||
* 执行命令 mode=2
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @return 进程信息
|
||||
*/
|
||||
exec2(cmd: string, env?: Record<string, string>): SshExecResult;
|
||||
|
||||
/**
|
||||
* 模拟TTY设备执行命令
|
||||
* @param cmd 命令
|
||||
* @param env 额外的环境变量
|
||||
* @return 进程信息
|
||||
*/
|
||||
pty(cmd: string, env?: Record<string, string>): SshExecResult;
|
||||
}
|
||||
|
||||
interface SshExecResult {
|
||||
/**
|
||||
* 进程退出code
|
||||
*/
|
||||
code?: number;
|
||||
/**
|
||||
* 进程输出内容,仅当combineOutput=true时有效
|
||||
*/
|
||||
output?: string;
|
||||
/**
|
||||
* 进出输出内容字节数,仅当combineOutput=true时有效
|
||||
*/
|
||||
outputbytes?: number;
|
||||
}
|
||||
|
||||
interface SocketModule {
|
||||
/**
|
||||
* 设置超时时间
|
||||
* @param milliseconds 毫秒
|
||||
* @return 毫秒
|
||||
*/
|
||||
timeout(milliseconds: number): number;
|
||||
|
||||
/**
|
||||
* TCP发送消息
|
||||
* @param host 地址
|
||||
* @param port 端口
|
||||
* @param data 数据
|
||||
* @return 服务器返回的内容
|
||||
*/
|
||||
tcpsend(host: string, port: number, data: string): string;
|
||||
|
||||
/**
|
||||
* TCP端口连接测试
|
||||
* @param host 地址
|
||||
* @param port 端口
|
||||
* @return 是否成功
|
||||
*/
|
||||
tcptest(host: string, port: number): boolean;
|
||||
}
|
||||
|
||||
interface SqlExecResult {
|
||||
lastInsertId: number;
|
||||
rowsAffected: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单断言
|
||||
* @param ok 要断言的值
|
||||
* @param message 提示信息
|
||||
*/
|
||||
declare function assert(ok: boolean, message?: string);
|
||||
@@ -8,6 +8,7 @@
|
||||
"main": "package.json",
|
||||
"scripts": {
|
||||
"build": "node build/prepare.js && tsc -b --verbose",
|
||||
"build:installer": "esbuild ./install/install.js --minify --format=cjs | base64",
|
||||
"build:watch": "node build/prepare.js && tsc -b --watch",
|
||||
"build:ui": "yarn build:ui:gulp && node packages/ui-default/build",
|
||||
"build:ui:gulp": "node packages/ui-default/build --gulp",
|
||||
|
||||
Reference in new issue
Block a user