diff --git a/install/install.ts b/install/install.ts index 4525259d..d0e16c6e 100644 --- a/install/install.ts +++ b/install/install.ts @@ -4,7 +4,7 @@ import { execSync, ExecSyncOptions } from 'child_process'; import { existsSync, readFileSync, writeFileSync } from 'fs'; import net from 'net'; -import os from 'os'; +import os, { cpus } from 'os'; import { createInterface } from 'readline/promises'; const exec = (command: string, args?: ExecSyncOptions) => { @@ -114,7 +114,7 @@ if (!cpuInfoFile.includes('avx2') && !installAsJudge) { avx2 = false; log.warn('warn.avx2'); } -let migration; +let migration = ''; let retry = 0; log.info('install.start'); const defaultDict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; @@ -157,7 +157,7 @@ gid: 1536 const Caddyfile = `\ # 如果你希望使用其他端口或使用域名,修改此处 :80 的值后在 ~/.hydro 目录下使用 caddy reload 重载配置。 # 如果你在当前配置下能够通过 http://你的域名/ 正常访问到网站,若需开启 ssl, -# 仅需将 :80 改为你的域名(如 hydro.ac)后直接重载配置即可自动签发 ssl 证书。 +# 仅需将 :80 改为你的域名(如 hydro.ac)后使用 caddy reload 重载配置即可自动签发 ssl 证书。 # 清注意在防火墙/安全组中放行端口,且部分运营商会拦截未经备案的域名。 # For more information, refer to caddy v2 documentation. :80 { @@ -175,19 +175,12 @@ const Caddyfile = `\ try_files {path} } } - @notStatic { - not { - file { - try_files {path} - } - } - } - handle @notStatic { - reverse_proxy http://127.0.0.1:8888 - } handle @static { file_server } + handle { + reverse_proxy http://127.0.0.1:8888 + } } `; @@ -207,12 +200,12 @@ processLimit: 128 testcases_max: 60 total_time_limit: 300 retry_delay_sec: 3 -parallelism: 10 +parallelism: ${Math.floor(cpus().length / 2)} rate: 1.00 rerun: 0 -secret: examplesecret +secret: Hydro-Judge-Secret env: | - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/nodejs/bin + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOME=/w `; @@ -273,6 +266,7 @@ const printInfo = [ 'echo "扫码加入QQ群:"', 'echo https://qm.qq.com/cgi-bin/qm/qr\\?k\\=0aTZfDKURRhPBZVpTYBohYG6P6sxABTw | qrencode -o - -m 2 -t UTF8', () => { + if (installAsJudge) return; password = new URL(require(`${process.env.HOME}/.hydro/config.json`).uri).password || '(No password)'; log.info('extra.dbUser'); log.info('extra.dbPassword', password); @@ -312,20 +306,35 @@ ${nixConfBase}`); 'nix-env -iA nixpkgs.pm2 nixpkgs.yarn nixpkgs.esbuild nixpkgs.bash nixpkgs.unzip nixpkgs.zip nixpkgs.diffutils', async () => { const rl = createInterface(process.stdin, process.stdout); - if (existsSync('/home/judge/src')) { - log.info('migrate.hustojFound'); - const res = await rl.question('>'); - if (res.toLowerCase().trim() === 'y') migration = 'hustoj'; + try { + if (existsSync('/home/judge/src')) { + log.info('migrate.hustojFound'); + const res = await rl.question('>'); + if (res.toLowerCase().trim() === 'y') migration = 'hustoj'; + } + if (migration) return; + const docker = !exec('docker -v').code; + if (!docker) return; + const containers = exec('docker ps -a --format json').output?.split('\n') + .map((i) => i.trim()).filter((i) => i).map((i) => JSON.parse(i)); + const uoj = containers?.find((i) => i.Image.toLowerCase === 'universaloj/uoj-system' && i.State === 'running'); + if (uoj) { + // not ready for production + return; + log.info('migrate.uojFound'); + const res = await rl.question('>'); + if (res.toLowerCase().trim() === 'y') migration = 'uoj'; + } + // // 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'; + // } + } catch (e) { + console.error('Failed migration detection'); + } finally { + rl.close(); } - - // 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'; - // } - rl.close(); }, ], }, @@ -443,12 +452,19 @@ ${nixConfBase}`); ], }, { - init: 'install.migrateHustoj', - skip: () => migration !== 'hustoj', + init: 'install.migrate', + skip: () => !migration, silent: true, operations: [ ['yarn global add @hydrooj/migrate', { retry: true }], 'hydrooj addon add @hydrooj/migrate', + ], + }, + { + init: 'install.migrateHustoj', + skip: () => migration !== 'hustoj', + silent: true, + operations: [ () => { const dbInc = readFileSync('/home/judge/src/web/include/db_info.inc.php', 'utf-8'); const l = dbInc.split('\n'); @@ -477,6 +493,43 @@ ${nixConfBase}`); 'pm2 restart hydrooj', ], }, + { + init: 'install.migrateUoj', + skip: () => migration !== 'uoj', + silent: true, + operations: [ + () => { + const containers = exec('docker ps -a --format json').output?.split('\n') + .map((i) => i.trim()).filter((i) => i).map((i) => JSON.parse(i)); + const uoj = containers!.find((i) => i.Image.toLowerCase === 'universaloj/uoj-system' && i.State === 'running')!; + const info = JSON.parse(exec(`docker inspect ${uoj.Id}`).output!); + const dir = info[0].GraphDriver.Data.MergedDir; + exec(`sed s/127.0.0.1/0.0.0.0/g -i ${dir}/etc/mysql/mysql.conf.d/mysqld.cnf`); + exec(`docker exec -i ${uoj.Id} /etc/init.d/mysql restart`); + const passwd = readFileSync(`${dir}/etc/mysql/debian.cnf`, 'utf-8') + .split('\n').find((i) => i.startsWith('password'))?.split('=')[1].trim(); + const script = [ + `CREATE USER 'hydromigrate'@'%' IDENTIFIED BY '${password}';`, + 'GRANT ALL PRIVILEGES ON *.* TO \'hydromigrate\'@\'%\' WITH GRANT OPTION;', + 'FLUSH PRIVILEGES;', + '', + ].join('\n'); + exec(`docker exec -i ${uoj.Id} mysql -u debian-sys-maint -p${passwd} -e "${script}"`); + const config = { + host: info[0].NetworkSettings.IPAddress, + port: 3306, + name: 'app_uoj233', + dataDir: `${dir}/var/uoj_data`, + username: 'hydromigrate', + password, + domainId: 'system', + }; + console.log(config); + // TODO mail config + exec(`hydrooj cli script migrateUoj '${JSON.stringify(config)}'`, { stdio: 'inherit' }); + }, + ], + }, { init: 'install.done', skip: () => installAsJudge,