diff --git a/README.md b/README.md index 00cb97b2c..df7a93513 100644 --- a/README.md +++ b/README.md @@ -137,11 +137,12 @@ Hydro 用户群:1085853538 - serializer: 移除 showDisplayName 参数 - 移除了 loader.addScript, loader.addon 函数 - 移除了 Hydro.module.render (请使用 ctx.server.registerRenderer) +- 移除了 lib/paginate 和 lib/rank (请使用 db.paginate 和 db.ranked) ### Hydro 4.12.3 / UI 4.50.2 - core: 将 API 模块移入 service - core: 比赛时提交被 hack 不触发整体重测 -- core: 将添加 pid match 的逻辑移入搜索中 +- core: 将添加 pid match 的逻辑移入搜索中 - ui: 修正 cssFilter - judge: builtin: 维护 callback 顺序 - core: 优化邮件地址处理 diff --git a/packages/hydrojudge/package.json b/packages/hydrojudge/package.json index 329cf6aab..065f07433 100644 --- a/packages/hydrojudge/package.json +++ b/packages/hydrojudge/package.json @@ -1,7 +1,7 @@ { "name": "@hydrooj/hydrojudge", "bin": "bin/hydrojudge.js", - "version": "3.3.14", + "version": "3.3.15", "main": "./src/index.ts", "author": "undefined ", "repository": "https://github.com/hydro-dev/Hydro.git", diff --git a/packages/hydrojudge/src/hosts/builtin.ts b/packages/hydrojudge/src/hosts/builtin.ts index 9e78e6da2..889ce399b 100644 --- a/packages/hydrojudge/src/hosts/builtin.ts +++ b/packages/hydrojudge/src/hosts/builtin.ts @@ -99,7 +99,7 @@ export async function postInit(ctx) { function collectCompilerInfo() { const coll = db.collection('status'); compilerVersions(langs).then((compilers) => coll.updateOne( - { mid: info.mid, type: 'judge' }, + { mid: info.mid, type: 'server' }, { $set: { compilers } }, { upsert: true }, )).catch((e) => logger.error(e)); diff --git a/packages/hydrojudge/src/hosts/hydro.ts b/packages/hydrojudge/src/hosts/hydro.ts index ef501778b..eaf5e81c4 100644 --- a/packages/hydrojudge/src/hosts/hydro.ts +++ b/packages/hydrojudge/src/hosts/hydro.ts @@ -175,6 +175,7 @@ export default class Hydro implements Session { ? JSON.stringify({ key: 'config', ...config }) : '{"key":"ping"}'; let compilers = {}; + let compilerVersionCallback = () => { }; this.ws.on('message', (data) => { if (data.toString() === 'ping') { this.ws.send('pong'); @@ -185,6 +186,7 @@ export default class Hydro implements Session { this.language = request.language; compilerVersions(this.language).then((res) => { compilers = res; + compilerVersionCallback(); }); } if (request.task) queue.add(() => new JudgeTask(this, request.task).handle().catch((e) => log.error(e))); @@ -203,7 +205,10 @@ export default class Hydro implements Session { this.ws.send('{"key":"start"}'); if (!this.config.noStatus) { const info = await sysinfo.get(); - this.ws.send(JSON.stringify({ key: 'status', info: { ...info, compilers } })); + this.ws.send(JSON.stringify({ key: 'status', info: { ...info } })); + compilerVersionCallback = () => { + this.ws.send(JSON.stringify({ key: 'status', info: { ...info, compilers } })); + }; setInterval(async () => { const [mid, inf] = await sysinfo.update(); this.ws.send(JSON.stringify({ key: 'status', info: { mid, ...inf, compilers } })); diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 18873b8e3..f750db6bb 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "4.13.0", + "version": "4.13.1", "bin": "bin/hydrooj.js", "main": "src/plugin-api", "module": "src/plugin-api", diff --git a/packages/hydrooj/src/model/setting.ts b/packages/hydrooj/src/model/setting.ts index 8b7d7f1d2..feb7358b8 100644 --- a/packages/hydrooj/src/model/setting.ts +++ b/packages/hydrooj/src/model/setting.ts @@ -274,6 +274,7 @@ SystemSetting( Setting('setting_basic', 'pagination.contest', 20, 'number', 'pagination.contest', 'Contests per page'), Setting('setting_basic', 'pagination.discussion', 50, 'number', 'pagination.discussion', 'Discussions per page'), Setting('setting_basic', 'pagination.record', 100, 'number', 'pagination.record', 'Records per page'), + Setting('setting_basic', 'pagination.ranking', 100, 'number', 'pagination.ranking', 'Users per page'), Setting('setting_basic', 'pagination.solution', 20, 'number', 'pagination.solution', 'Solutions per page'), Setting('setting_basic', 'pagination.training', 10, 'number', 'pagination.training', 'Trainings per page'), Setting('setting_basic', 'pagination.reply', 50, 'number', 'pagination.reply', 'Replies per page'), diff --git a/packages/hydrooj/src/service/layers/domain.ts b/packages/hydrooj/src/service/layers/domain.ts index 93a43bb14..a9f1c92ae 100644 --- a/packages/hydrooj/src/service/layers/domain.ts +++ b/packages/hydrooj/src/service/layers/domain.ts @@ -1,4 +1,4 @@ -import { KoaContext } from '@hydrooj/framework'; +import { KoaContext, NotFoundError } from '@hydrooj/framework'; import BlackListModel from '../../model/blacklist'; import DomainModel from '../../model/domain'; import * as system from '../../model/system'; @@ -24,5 +24,12 @@ export default async (ctx: KoaContext, next) => { ctx.domainId = inferDomain?._id || domainId; ctx.domainInfo = inferDomain || absoluteDomain; if (ctx.domainInfo && ctx.domainId !== ctx.domainInfo._id) ctx.redirect(ctx.originalPath.replace(/^\/d\/[^/]+\//, `/d/${ctx.domainInfo._id}/`)); - else await next(); + else { + if (!ctx.domainInfo) { + ctx.pendingError = new NotFoundError(ctx.domainId); + ctx.domainId = 'system'; + ctx.domainInfo = await DomainModel.get('system'); + } + await next(); + } }; diff --git a/packages/hydrooj/src/service/layers/user.ts b/packages/hydrooj/src/service/layers/user.ts index 60575f45e..c9df9cb76 100644 --- a/packages/hydrooj/src/service/layers/user.ts +++ b/packages/hydrooj/src/service/layers/user.ts @@ -1,5 +1,4 @@ import type { KoaContext } from '@hydrooj/framework'; -import { NotFoundError } from '../../error'; import { PERM } from '../../model/builtin'; import UserModel from '../../model/user'; @@ -15,9 +14,5 @@ export default async (ctx: KoaContext, next) => { } if (user._id === 0) delete user.viewLang; ctx.HydroContext.user = await user.private(); - if (!domain) { - ctx.pendingError = new NotFoundError(args.domainId); - args.domainId = 'system'; - } await next(); }; diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index d75126913..cad5626b8 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -276,6 +276,7 @@ export async function apply(ctx: Context) { text: global.Hydro.module.oauth[key].text, })); if (!h.noCheckPermView && !h.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) h.checkPerm(PERM.PERM_VIEW); + if (h.context.pendingError) throw h.context.pendingError; }); on('app/listen', () => { diff --git a/packages/ui-default/build/config/webpack.ts b/packages/ui-default/build/config/webpack.ts index 7781491d8..7e19e3ec4 100644 --- a/packages/ui-default/build/config/webpack.ts +++ b/packages/ui-default/build/config/webpack.ts @@ -89,6 +89,7 @@ export default function (env: { watch?: boolean, production?: boolean, measure?: output: { path: root('public'), publicPath: '/', // overwrite in entry.js + workerPublicPath: '/', hashFunction: 'sha1', hashDigest: 'hex', hashDigestLength: 10, diff --git a/packages/ui-default/build/main.ts b/packages/ui-default/build/main.ts index 0c9fb5ffa..646cdc45e 100644 --- a/packages/ui-default/build/main.ts +++ b/packages/ui-default/build/main.ts @@ -49,7 +49,10 @@ async function runWebpack({ return; } if (argv.options.detail) console.log(stats.toString()); - if (!watch && (!stats || stats.hasErrors())) process.exitCode = 1; + if (!watch && (!stats || stats.hasErrors())) { + if (!argv.options.detail) console.log(stats.toString()); + process.exitCode = 1; + } resolve(stats); } if (watch) compiler.watch({}, compilerCallback); diff --git a/packages/ui-default/components/autocomplete/CustomSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/CustomSelectAutoComplete.tsx index c8f3c82a3..760850fa9 100644 --- a/packages/ui-default/components/autocomplete/CustomSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/CustomSelectAutoComplete.tsx @@ -24,9 +24,8 @@ const Component = React.forwardRef((props, ref) => { ); }); -export default class CustomSelectAutoComplete extends AutoComplete { +export default class CustomSelectAutoComplete extends AutoComplete { static DOMAttachKey = 'ucwCustomSelectAutoCompleteInstance'; - options: CustomSelectOptions & AutoCompleteOptions; constructor($dom, options: CustomSelectOptions & AutoCompleteOptions) { super($dom, { diff --git a/packages/ui-default/components/autocomplete/FileSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/FileSelectAutoComplete.tsx index e128c9e6b..a30207072 100644 --- a/packages/ui-default/components/autocomplete/FileSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/FileSelectAutoComplete.tsx @@ -27,9 +27,8 @@ const Component = React.forwardRef((props, ref) => { ); }); -export default class FileSelectAutoComplete extends AutoComplete { +export default class FileSelectAutoComplete extends AutoComplete { static DOMAttachKey = 'ucwFileSelectAutoCompleteInstance'; - options: FileSelectOptions & AutoCompleteOptions; constructor($dom, options: FileSelectOptions & AutoCompleteOptions) { super($dom, { diff --git a/packages/ui-default/components/autocomplete/index.tsx b/packages/ui-default/components/autocomplete/index.tsx index 2c603c9f0..52f0a6a01 100644 --- a/packages/ui-default/components/autocomplete/index.tsx +++ b/packages/ui-default/components/autocomplete/index.tsx @@ -8,6 +8,7 @@ export interface AutoCompleteOptions { defaultItems?: string; width?: string; height?: string; + classes?: string; listStyle?: any; allowEmptyQuery?: boolean; freeSolo?: boolean; @@ -18,17 +19,17 @@ export interface AutoCompleteOptions { text?: () => string; } -export default class AutoComplete extends DOMAttachedObject { +export default class AutoComplete = {}> extends DOMAttachedObject { static DOMAttachKey = 'ucwAutoCompleteInstance'; ref = null; container = document.createElement('div'); - options: AutoCompleteOptions; + options: AutoCompleteOptions & Options; component = ReactDOM.createRoot(this.container); changeListener = [ (val) => this.$dom.val(val), ]; - constructor($dom, options = {}) { + constructor($dom, options = {} as Options) { super($dom); this.options = { items: async () => [], diff --git a/packages/ui-default/components/message/index.page.ts b/packages/ui-default/components/message/index.page.ts index 723b6610c..2c45b18a9 100644 --- a/packages/ui-default/components/message/index.page.ts +++ b/packages/ui-default/components/message/index.page.ts @@ -1,5 +1,4 @@ import { nanoid } from 'nanoid'; -import ReconnectingWebsocket from 'reconnecting-websocket'; import { InfoDialog } from 'vj/components/dialog'; import VjNotification from 'vj/components/notification/index'; import { @@ -7,6 +6,7 @@ import { } from 'vj/constant/message'; import { AutoloadPage } from 'vj/misc/Page'; import { i18n, tpl } from 'vj/utils'; +import Sock from '../socket'; let previous: VjNotification; const onmessage = (msg) => { @@ -128,12 +128,8 @@ const messagePage = new AutoloadPage('messagePage', (pagename) => { isMaster = true; localStorage.setItem('page.master', selfId); const masterChannel = new BroadcastChannel('hydro-messages'); - const sock = new ReconnectingWebsocket(endpoint); - sock.onopen = () => console.log('Connected'); - sock.onerror = console.error; - sock.onclose = (...args) => console.log('Closed', ...args); + const sock = new Sock(endpoint); sock.onmessage = async (message) => { - if (process.env.NODE_ENV !== 'production') console.log('onmessage: ', message); const payload = JSON.parse(message.data); masterChannel.postMessage({ type: 'message', payload }); }; diff --git a/packages/ui-default/entry.js b/packages/ui-default/entry.js index a9f838e58..ae344f2d9 100644 --- a/packages/ui-default/entry.js +++ b/packages/ui-default/entry.js @@ -31,7 +31,7 @@ window.UserContext = JSON.parse(window.UserContext); if (process.env.NODE_ENV === 'production' && !UiContext.sentry_disable) { window.captureException = (e) => Sentry.captureException(e); Sentry.init({ - dsn: UiContext.sentry_dsn || 'https://04d1d77b095231d55e1202bdc912ff16@sentry.hydro.ac/3', + dsn: UiContext.sentry_dsn || 'https://2f95d53751e08c74c1af1c4b93ccaff7@sentry.hydro.ac/2', release: process.env.VERSION, integrations: [ Sentry.browserTracingIntegration(), diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 8a85c7963..210528490 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.51.0", + "version": "4.51.1", "author": "undefined ", "license": "AGPL-3.0", "main": "index.ts", @@ -51,6 +51,7 @@ "clean-webpack-plugin": "^4.0.0", "clipboard": "^2.0.11", "copy-webpack-plugin": "^12.0.2", + "core-js": "^3.37.1", "css-loader": "^6.11.0", "diff": "^5.2.0", "diff-dom": "^5.1.3", @@ -90,7 +91,6 @@ "prop-types": "^15.8.1", "qface": "^1.4.1", "qrcode": "^1.5.3", - "queue-microtask": "^1.2.3", "react": "^18.3.1", "react-colorful": "^5.6.1", "react-dnd": "^16.0.1", diff --git a/packages/ui-default/polyfill.ts b/packages/ui-default/polyfill.ts index 202215e4e..0c87826d9 100644 --- a/packages/ui-default/polyfill.ts +++ b/packages/ui-default/polyfill.ts @@ -1,9 +1,8 @@ import 'matchmedia-polyfill'; +import 'core-js/stable'; import browserUpdate from 'browser-update'; -import queueMicrotask from 'queue-microtask'; -window.queueMicrotask = queueMicrotask; browserUpdate({ required: { e: -10, f: -10, o: -3, s: -1, c: -10, diff --git a/packages/ui-default/templates/layout/html5.html b/packages/ui-default/templates/layout/html5.html index dc06df6fb..0c64c4441 100644 --- a/packages/ui-default/templates/layout/html5.html +++ b/packages/ui-default/templates/layout/html5.html @@ -47,7 +47,7 @@ var _htmlNode = document.documentElement; _htmlNode.className = _htmlNode.className.replace(' nojs', ' hasjs'); try { - import('data:text/javascript,');Object.fromEntries([]);window.matchMedia('all').addEventListener('change',()=>{}); + import('data:text/javascript,');Object.fromEntries([]);window.matchMedia('all').addEventListener('change',()=>{});Array.prototype.at(0); } catch(e) { const s = document.createElement('script'); s.async = false; diff --git a/packages/ui-default/utils/base.ts b/packages/ui-default/utils/base.ts index 53744dbcb..a68f2f6b2 100644 --- a/packages/ui-default/utils/base.ts +++ b/packages/ui-default/utils/base.ts @@ -34,8 +34,8 @@ export function secureRandomString(digit = 32, dict = defaultDict) { type Substitution = string | number | { templateRaw: true, html: string }; -export function tpl(node: React.ReactNode, reactive?: boolean); -export function tpl(pieces: TemplateStringsArray, ...substitutions: Substitution[]); +export function tpl(node: React.ReactNode, reactive?: T): T extends true ? React.ReactElement : string; +export function tpl(pieces: TemplateStringsArray, ...substitutions: Substitution[]): string; export function tpl(pieces: TemplateStringsArray | React.ReactNode, ...substitutions: Substitution[] | boolean[]) { if (React.isValidElement(pieces)) { if (substitutions[0]) { @@ -49,7 +49,7 @@ export function tpl(pieces: TemplateStringsArray | React.ReactNode, ...substitut for (let i = 0; i < substitutions.length; ++i) { const subst = substitutions[i]; let substHtml: string; - if (typeof subst === 'object' && subst.templateRaw) { + if (subst && typeof subst === 'object' && subst.templateRaw) { substHtml = subst.html; } else substHtml = _.escape(String(subst)); result += substHtml + pieces[i + 1];