diff --git a/build/publish.ts b/build/publish.ts index 29f68ec2f..a69127ac9 100644 --- a/build/publish.ts +++ b/build/publish.ts @@ -14,7 +14,9 @@ const { CI, GITHUB_EVENT_NAME, GITHUB_REF, } = process.env; -if (CI && (GITHUB_REF !== 'refs/heads/master' || GITHUB_EVENT_NAME !== 'push')) { +const tag = GITHUB_REF === 'refs/heads/master' ? 'latest' : GITHUB_REF === 'refs/heads/develop' ? 'dev' : null; + +if (CI && (!tag || GITHUB_EVENT_NAME !== 'push')) { console.log('publish skipped.'); process.exit(0); } @@ -35,7 +37,7 @@ if (CI && (GITHUB_REF !== 'refs/heads/master' || GITHUB_EVENT_NAME !== 'push')) try { meta = require(`../${name}/package.json`); if (!meta.private) { - const version = await latest(meta.name); + const version = await latest(meta.name, { version: tag }); if (gt(meta.version, version)) bumpMap[name] = meta.version; } } catch (e) { @@ -48,7 +50,7 @@ if (CI && (GITHUB_REF !== 'refs/heads/master' || GITHUB_EVENT_NAME !== 'push')) if (Object.keys(bumpMap).length) { for (const name in bumpMap) { console.log(`publishing ${name}@${bumpMap[name]} ...`); - await spawnAsync(`yarn publish ${name} --new-version ${bumpMap[name]} --access public`); + await spawnAsync(`yarn publish ${name} --new-version ${bumpMap[name]}${tag === 'dev' ? '-dev' : ''} --access public --tag ${tag}`); } } console.log('Release created successfully.'); diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 4e12eadb2..69abaf9e1 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.21.0", + "version": "2.21.1", "bin": "bin/hydrooj.js", "main": "dist/loader.js", "typings": "dist/loader.d.ts", diff --git a/packages/hydrooj/src/entry/common.ts b/packages/hydrooj/src/entry/common.ts index fb088dfe0..855a3c4be 100644 --- a/packages/hydrooj/src/entry/common.ts +++ b/packages/hydrooj/src/entry/common.ts @@ -26,7 +26,7 @@ export const builtinModel = [ export const builtinHandler = [ 'home', 'problem', 'record', 'judge', 'user', 'contest', 'training', 'discussion', 'manage', 'import.syzoj', - 'misc', 'homework', 'domain', 'remote', + 'misc', 'homework', 'domain', 'remote', 'status', ]; export const builtinScript = [ diff --git a/packages/hydrooj/src/handler/misc.ts b/packages/hydrooj/src/handler/misc.ts index af8c138b7..8a7b5217f 100644 --- a/packages/hydrooj/src/handler/misc.ts +++ b/packages/hydrooj/src/handler/misc.ts @@ -2,45 +2,10 @@ import { BadRequestError } from '../error'; import { PRIV } from '../model/builtin'; import user from '../model/user'; -import db from '../service/db'; import { Route, Handler, Types, param, } from '../service/server'; -const coll = db.collection('status'); - -class StatusHandler extends Handler { - async get() { - const stats = await coll.find().sort({ type: 1, updateAt: -1 }).toArray(); - for (const i in stats) { - let desc = ''; - const online = new Date(stats[i].updateAt).getTime() > new Date().getTime() - 300000; - if (!online) desc = 'Offline'; - desc = desc || 'Online'; - stats[i].isOnline = online; - stats[i].status = desc; - } - const path = [ - ['Hydro', 'homepage'], - ['status', null], - ]; - this.response.body = { stats, path }; - this.response.template = 'status.html'; - } -} - -class StatusUpdateHandler extends Handler { - async post(args) { - this.checkPriv(PRIV.PRIV_JUDGE); - args.type = 'judge'; - return coll.updateOne( - { mid: args.mid, type: 'judge' }, - { $set: args }, - { upsert: true }, - ); - } -} - class CheckInHandler extends Handler { async prepare() { const { checkincnt, lastcheckin } = this.user; @@ -82,8 +47,6 @@ class SwitchLanguageHandler extends Handler { } export async function apply() { - Route('status', '/status', StatusHandler); - Route('status_update', '/status/update', StatusUpdateHandler); Route('check_in', '/checkin', CheckInHandler, PRIV.PRIV_USER_PROFILE); Route('switch_language', '/language/:lang', SwitchLanguageHandler); } diff --git a/packages/hydrooj/src/handler/notfound.ts b/packages/hydrooj/src/handler/notfound.ts index 83255b5be..ea6da1308 100644 --- a/packages/hydrooj/src/handler/notfound.ts +++ b/packages/hydrooj/src/handler/notfound.ts @@ -12,14 +12,7 @@ class NotFoundHandler extends Handler { } export async function apply() { - Route('notfound', '/:a', NotFoundHandler); - Route('notfound', '/:a/:b', NotFoundHandler); - Route('notfound', '/:a/:b/:c', NotFoundHandler); - Route('notfound', '/:a/:b/:c/:d', NotFoundHandler); - Route('notfound', '/:a/:b/:c/:d/:e', NotFoundHandler); - Route('notfound', '/:a/:b/:c/:d/:e/:f', NotFoundHandler); - Route('notfound', '/:a/:b/:c/:d/:e/:f/:g', NotFoundHandler); - Route('notfound', '/:a/:b/:c/:d/:e/:f/:g/:h', NotFoundHandler); + Route('notfound', '(/.*)+', NotFoundHandler); } global.Hydro.handler.notfound = apply; diff --git a/packages/hydrooj/src/handler/status.ts b/packages/hydrooj/src/handler/status.ts new file mode 100644 index 000000000..55f413296 --- /dev/null +++ b/packages/hydrooj/src/handler/status.ts @@ -0,0 +1,81 @@ +import { PRIV } from '../model/builtin'; +import RecordModel from '../model/record'; +import DomainModel from '../model/domain'; +import * as DocumentModel from '../model/document'; +import db from '../service/db'; +import { Route, Handler } from '../service/server'; +import UserModel from '../model/user'; + +const coll = db.collection('status'); + +async function getStatus() { + const stats = await coll.find().sort({ type: 1, updateAt: -1 }).toArray(); + for (const stat of stats) { + let desc = ''; + const online = new Date(stat.updateAt).getTime() > new Date().getTime() - 300000; + if (!online) desc = 'Offline'; + desc = desc || 'Online'; + stat.isOnline = online; + stat.status = desc; + } + return stats; +} + +class StatusHandler extends Handler { + async get() { + const path = [ + ['Hydro', 'homepage'], + ['status', null], + ]; + const stats = await getStatus(); + for (const stat of stats) { + if (!stat.battery.hasBattery) stat.battery = 'No battery'; + else stat.battery = `${stat.battery.type} ${stat.battery.model} ${stat.battery.percent}%${stat.battery.isCharging ? ' Charging' : ''}`; + } + this.response.body = { stats, path }; + this.response.template = 'status.html'; + } +} + +class AdminStatusHandler extends Handler { + async get() { + const record = await RecordModel.stat(); + const status = await getStatus(); + const [domainCount, userCount, problemCount, discussionCount, recordCount] = await Promise.all([ + DomainModel.getMulti().count(), + UserModel.getMulti().count(), + DocumentModel.coll.find({ docType: DocumentModel.TYPE_PROBLEM }).count(), + DocumentModel.coll.find({ docType: DocumentModel.TYPE_DISCUSSION }).count(), + RecordModel.coll.find().count(), + ]); + this.response.body = { + record, + status, + domainCount, + userCount, + problemCount, + discussionCount, + recordCount, + }; + } +} + +class StatusUpdateHandler extends Handler { + async post(args) { + this.checkPriv(PRIV.PRIV_JUDGE); + args.type = 'judge'; + return coll.updateOne( + { mid: args.mid, type: 'judge' }, + { $set: args }, + { upsert: true }, + ); + } +} + +export async function apply() { + Route('status', '/status', StatusHandler); + Route('status_admin', '/.status', AdminStatusHandler); + Route('status_update', '/status/update', StatusUpdateHandler); +} + +global.Hydro.handler.status = apply; diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index b1d233e2a..fe18dd912 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -565,6 +565,7 @@ declare global { namespace NodeJS { interface Global { Hydro: { + version: Record, model: Model, handler: Dict, script: Dict