diff --git a/install/install.ts b/install/install.ts index df84d6ff1..ea696a83f 100644 --- a/install/install.ts +++ b/install/install.ts @@ -305,7 +305,7 @@ const Steps = () => [ { init: 'install.preparing', operations: [ - () => { + async () => { if (process.env.IGNORE_BT) return; const res = exec('bt default'); if (!res.code) { @@ -314,6 +314,7 @@ const Steps = () => [ process.exit(1); } else { log.warn('warn.bt'); + await sleep(5000); } } }, @@ -407,7 +408,7 @@ ${nixConfBase}`); }, { init: 'install.caddy', - skip: () => !exec('caddy version').code || installAsJudge || noCaddy, + skip: () => !exec('caddy version').code || installAsJudge || noCaddy || !existsSync(`${process.env.HOME}/.hydro/Caddyfile`), hidden: installAsJudge, operations: [ 'nix-env -iA nixpkgs.caddy', diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 94c38e893..a07fa55eb 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "4.16.0", + "version": "4.16.1", "bin": "bin/hydrooj.js", "main": "src/plugin-api", "module": "src/plugin-api", @@ -25,7 +25,6 @@ "adm-zip": "0.5.14", "cac": "^6.7.14", "cordis": "3.18.1", - "detect-browser": "^5.3.0", "emoji-regex": "^10.4.0", "emojis-list": "2.1.0", "fs-extra": "^11.2.0", @@ -51,7 +50,8 @@ "shorty.js": "^1.0.1", "superagent": "^10.1.0", "tar": "6.2.1", - "thirty-two": "^1.0.2" + "thirty-two": "^1.0.2", + "ua-parser-js": "^1.0.39" }, "devDependencies": { "@types/adm-zip": "^0.4.34", diff --git a/packages/hydrooj/src/handler/contest.ts b/packages/hydrooj/src/handler/contest.ts index fe774722b..efd8a3954 100644 --- a/packages/hydrooj/src/handler/contest.ts +++ b/packages/hydrooj/src/handler/contest.ts @@ -285,6 +285,13 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler { const pid = (i: number) => String.fromCharCode(65 + i); const escape = (i: string) => i.replace(/[",]/g, ''); const unknownSchool = this.translate('Unknown School'); + const statusMap = { + [STATUS.STATUS_ACCEPTED]: 'AC', + [STATUS.STATUS_WRONG_ANSWER]: 'WA', + [STATUS.STATUS_COMPILE_ERROR]: 'CE', + [STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'TL', + [STATUS.STATUS_RUNTIME_ERROR]: 'RT', + }; const submissions = teams.flatMap((i, idx) => { if (!i.journal) return []; const journal = i.journal.filter((s) => tdoc.pids.includes(s.pid)); @@ -292,7 +299,7 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler { return journal.map((s) => { const id = pid(tdoc.pids.indexOf(s.pid)); c[id]++; - return `@s ${idx + 1},${id},${c[id]},${time(s.rid)},${s.status === STATUS.STATUS_ACCEPTED ? 'AC' : 'RJ'}`; + return `@s ${idx + 1},${id},${c[id]},${time(s.rid)},${statusMap[s.status] || 'RJ'}`; }); }); const res = [ diff --git a/packages/hydrooj/src/handler/home.ts b/packages/hydrooj/src/handler/home.ts index fa42f38ff..d2eefa4e1 100644 --- a/packages/hydrooj/src/handler/home.ts +++ b/packages/hydrooj/src/handler/home.ts @@ -3,6 +3,7 @@ import { generateRegistrationOptions, verifyRegistrationResponse } from '@simple import yaml from 'js-yaml'; import { pick } from 'lodash'; import { Binary, ObjectId } from 'mongodb'; +import Parser from 'ua-parser-js'; import { Context } from '../context'; import { AuthOperationError, BlacklistedError, DomainAlreadyExistsError, InvalidTokenError, @@ -12,7 +13,6 @@ import { import { DomainDoc, MessageDoc, Setting } from '../interface'; import avatar, { validate } from '../lib/avatar'; import * as mail from '../lib/mail'; -import * as useragent from '../lib/useragent'; import { verifyTFA } from '../lib/verifyTFA'; import BlackListModel from '../model/blacklist'; import { PERM, PRIV } from '../model/builtin'; @@ -187,7 +187,11 @@ class HomeSecurityHandler extends Handler { for (const session of sessions) { session.isCurrent = session._id === this.session._id; session._id = md5(session._id); - session.updateUa = useragent.parse(session.updateUa || session.createUa || ''); + const ua = session.updateUa || session.createUa; + if (ua) { + const parser = new Parser(ua); + session.updateUaInfo = parser.getResult(); + } session.updateGeoip = this.ctx.geoip?.lookup?.( session.updateIp || session.createIp, this.translate('geoip_locale'), @@ -202,7 +206,6 @@ class HomeSecurityHandler extends Handler { 'authenticatorAttachment', 'regat', 'fmt', ])), geoipProvider: this.ctx?.geoip?.provider, - icon: (str = '') => str.split(' ')[0].toLowerCase(), }; } diff --git a/packages/hydrooj/src/handler/user.ts b/packages/hydrooj/src/handler/user.ts index 305b0b0c0..7213801f6 100644 --- a/packages/hydrooj/src/handler/user.ts +++ b/packages/hydrooj/src/handler/user.ts @@ -271,6 +271,7 @@ class UserRegisterWithCodeHandler extends Handler { if (mailDomain === 'qq.com' && !Number.isNaN(+id)) $set.avatar = `qq:${id}`; if (this.session.viewLang) $set.viewLang = this.session.viewLang; if (Object.keys($set).length) await user.setById(uid, $set); + if (Object.keys(this.tdoc.setInDomain || {}).length) await domain.setUserInDomain(domainId, uid, this.tdoc.setInDomain); if (this.tdoc.oauth) await oauth.set(this.tdoc.oauth[1], uid); this.context.HydroContext.user = await user.getById(domainId, uid); this.session.viewLang = ''; @@ -453,7 +454,7 @@ class OauthCallbackHandler extends Handler { break; } } - const set: Partial = { oauth: args.type }; + const set: Partial = { ...(r.set || {}), oauth: args.type }; if (r.bio) set.bio = r.bio; if (r.viewLang) set.viewLang = r.viewLang; if (r.avatar) set.avatar = r.avatar; @@ -465,6 +466,7 @@ class OauthCallbackHandler extends Handler { username, redirect: this.domain.registerRedirect, set, + setInDomain: r.setInDomain, oauth: [args.type, r._id], }, ); diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index 9ffda1dda..7feedcc25 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -58,6 +58,8 @@ export interface OAuthUserResponse { bio?: string; uname?: string[]; viewLang?: string; + set?: Record; + setInDomain?: Record; } export interface Authenticator { diff --git a/packages/hydrooj/src/lib/useragent.ts b/packages/hydrooj/src/lib/useragent.ts deleted file mode 100644 index 0dfb414d2..000000000 --- a/packages/hydrooj/src/lib/useragent.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as browser from 'detect-browser'; - -export function parse(str: string) { - return browser.parseUserAgent(str); -} diff --git a/packages/hydrooj/src/service/db.ts b/packages/hydrooj/src/service/db.ts index 9430b9ea6..6dee3f3fd 100644 --- a/packages/hydrooj/src/service/db.ts +++ b/packages/hydrooj/src/service/db.ts @@ -22,6 +22,7 @@ interface MongoConfig { url?: string, uri?: string, prefix?: string, + collectionMap?: Record, } class MongoService { @@ -54,8 +55,9 @@ class MongoService { } public collection(c: K) { - if (this.opts.prefix) return this.db.collection(`${this.opts.prefix}.${c}`); - return this.db.collection(c); + let coll = this.opts.prefix ? `${this.opts.prefix}.${c}` : c; + if (this.opts.collectionMap?.[coll]) coll = this.opts.collectionMap[coll]; + return this.db.collection(coll); } public async fixExpireAfter() { diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index 131e18227..e1d11976e 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -238,7 +238,7 @@ export async function apply(ctx: Context) { return res; }, paginate(cursor: FindCursor, page: number, key: string) { - return db.paginate(cursor, page, this.ctx.setting.get(`pagination.${key}`)); + return db.paginate(cursor, page, this.ctx.setting.get(`pagination.${key}`) || 20); }, checkPerm(...args: bigint[]) { if (!this.user.hasPerm(...args)) { diff --git a/packages/onlyoffice/index.ts b/packages/onlyoffice/index.ts index b35f78f37..a5b6963ae 100644 --- a/packages/onlyoffice/index.ts +++ b/packages/onlyoffice/index.ts @@ -18,6 +18,7 @@ class OnlyofficeJWTHandler extends Handler { if (SystemModel.get('onlyoffice.externalSign')) { const res = await superagent.get(SystemModel.get('onlyoffice.externalSign')).query({ url }); this.response.body = res.body; + return; } let path: string; try { diff --git a/packages/onlyoffice/package.json b/packages/onlyoffice/package.json index b0ca013a1..064a776d2 100644 --- a/packages/onlyoffice/package.json +++ b/packages/onlyoffice/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/onlyoffice", - "version": "1.4.3", + "version": "1.4.4", "main": "index.ts", "repository": "git@github.com:hydro-dev/Hydro.git", "author": "undefined ", diff --git a/packages/ui-default/backendlib/markdown-it-xss.ts b/packages/ui-default/backendlib/markdown-it-xss.ts index d9fbe90a8..c3033a8e4 100644 --- a/packages/ui-default/backendlib/markdown-it-xss.ts +++ b/packages/ui-default/backendlib/markdown-it-xss.ts @@ -151,7 +151,7 @@ export const xss = new FilterXSS({ css: cssFilterOptions, safeAttrValue(tag, name, value) { if (name === 'id') return escapeAttrValue(`xss-id-${value}`); - if (name === 'class') return value.split(' ').filter((i) => whitelistClasses.includes(i)).join(' '); + if (name === 'class') return value.split(' ').filter((i) => whitelistClasses.includes(i) || i.startsWith('language-')).join(' '); return safeAttrValue(tag, name, value, CssFilter); }, }); diff --git a/packages/ui-default/backendlib/template.ts b/packages/ui-default/backendlib/template.ts index 7758a60dd..51222dc63 100644 --- a/packages/ui-default/backendlib/template.ts +++ b/packages/ui-default/backendlib/template.ts @@ -157,6 +157,14 @@ env.addGlobal('set', (obj, key, val) => { env.addGlobal('findSubModule', (prefix) => Object.keys(global.Hydro.ui.template).filter((n) => n.startsWith(prefix))); env.addGlobal('templateExists', (name) => !!global.Hydro.ui.template[name]); +const platformIconMap = { 'mac os': 'mac' }; +const supportedPlatformIcons = ['android', 'chromeos', 'ios', 'linux', 'mac', 'mobile', 'windows']; +env.addGlobal('platformIcon', (platform) => { + const key = platformIconMap[platform?.toLowerCase()] || platform?.toLowerCase(); + if (supportedPlatformIcons.includes(key)) return key; + return 'unknown'; +}); + const render = (name: string, state: any) => new Promise((resolve, reject) => { env.render(name, { page_name: name.split('.')[0], diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index f11e063c3..51593d6ea 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.54.3", + "version": "4.54.4", "author": "undefined ", "license": "AGPL-3.0", "main": "index.ts", diff --git a/packages/ui-default/pages/record_detail.page.tsx b/packages/ui-default/pages/record_detail.page.tsx index ae5684d89..723753639 100644 --- a/packages/ui-default/pages/record_detail.page.tsx +++ b/packages/ui-default/pages/record_detail.page.tsx @@ -13,7 +13,7 @@ export default new NamedPage('record_detail', async () => { $(document).on('click', '.subtask-case', function () { const text = $(this).find('.message').text(); const data = $(this).find('.message').html(); - if (!text?.trim()) return; + if (!text?.trim() || (!text.includes('\n') && text.length < 20)) return; new InfoDialog({ $body: tpl(
),
     }).open();
diff --git a/packages/ui-default/templates/home_security.html b/packages/ui-default/templates/home_security.html
index 406f933f5..ae47ea3d1 100644
--- a/packages/ui-default/templates/home_security.html
+++ b/packages/ui-default/templates/home_security.html
@@ -153,7 +153,7 @@
           
  • - +

    {{ _('Last Update At') }}: {{ datetimeSpan(session.updateAt)|safe }}

    @@ -161,8 +161,8 @@

    {{ _('Hostname') }}: {{ session.createHost }}

    {% endif %}

    {{ _('Location') }}: {{ _(session.updateGeoip.display) }} ({{ session.updateIp }})

    -

    {{ _('Operating System') }}: {{ session.updateUa.os }}

    -

    {{ _('Browser') }}: {{ session.updateUa.name }} {{ session.updateUa.version }}

    +

    {{ _('Operating System') }}: {{ session.updateUaInfo.os.name }} {{ session.updateUaInfo.os.version }}

    +

    {{ _('Browser') }}: {{ session.updateUaInfo.browser.name }} {{ session.updateUaInfo.browser.version }}

    {% if session.isCurrent %}

    {{ _('This is the current session') }}

    {% endif %} diff --git a/packages/ui-default/templates/partials/login_dialog.html b/packages/ui-default/templates/partials/login_dialog.html index 2ecb04545..400315d37 100644 --- a/packages/ui-default/templates/partials/login_dialog.html +++ b/packages/ui-default/templates/partials/login_dialog.html @@ -1,13 +1,11 @@