diff --git a/packages/hydrooj/src/handler/import.ts b/packages/hydrooj/src/handler/import.ts index bf988bd96..844ed0eb4 100644 --- a/packages/hydrooj/src/handler/import.ts +++ b/packages/hydrooj/src/handler/import.ts @@ -1,6 +1,8 @@ +import { sleep } from '@hydrooj/utils'; import { Context } from '../context'; import { ValidationError } from '../error'; import { PERM, PRIV } from '../model/builtin'; +import MessageModel from '../model/message'; import problem from '../model/problem'; import { Handler, param, Types } from '../service/server'; @@ -15,8 +17,16 @@ class ProblemImportHydroHandler extends Handler { if (keepUser) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM); if (!this.request.files.file) throw new ValidationError('file'); if (preferredPrefix && !/^[a-zA-Z]+$/.test(preferredPrefix)) throw new ValidationError('preferredPrefix'); - await problem.import(domainId, this.request.files.file.filepath, keepUser ? null : this.user._id, preferredPrefix); - this.response.redirect = this.url('problem_main'); + const promise = problem.import( + domainId, this.request.files.file.filepath, keepUser ? null : this.user._id, + preferredPrefix, this.progress.bind(this), + ).catch((e) => MessageModel.send(1, this.user._id, `Import failed: ${e.message}\n${e.stack}`)); + let resolved = false; + await Promise.race([ + promise.then(() => { resolved = true; }), + sleep(5000), + ]); + this.response.redirect = this.url('problem_main', resolved ? {} : { query: { showImport: 1 } }); } } diff --git a/packages/hydrooj/src/model/problem.ts b/packages/hydrooj/src/model/problem.ts index ef1bde802..55e0d1357 100644 --- a/packages/hydrooj/src/model/problem.ts +++ b/packages/hydrooj/src/model/problem.ts @@ -445,7 +445,7 @@ export class ProblemModel { return true; } - static async import(domainId: string, filepath: string, operator = 1, preferredPrefix?: string) { + static async import(domainId: string, filepath: string, operator = 1, preferredPrefix?: string, progress = null) { let tmpdir = ''; let del = false; try { @@ -527,7 +527,11 @@ export class ProblemModel { if (count > 5) continue; await RecordModel.add(domainId, docId, operator, f.name.split('.')[1], await fs.readFile(loc, 'utf-8'), true); } - if (process.env.HYDRO_CLI) logger.info(`Imported problem ${pdoc.pid} (${pdoc.title})`); + if (process.env.HYDRO_CLI) { + logger.info(`Imported problem ${pdoc.pid} (${pdoc.title})`); + } else { + progress?.(`Imported problem ${pdoc.pid} (${pdoc.title})`); + } } } finally { if (del) await fs.remove(tmpdir); diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index e86a69b1a..54fb52490 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -249,7 +249,7 @@ export async function apply(ctx: Context) { checkPriv(...args: number[]) { if (!this.user.hasPriv(...args)) throw new PrivilegeError(...args); }, - progress(message: string, params: any[]) { + progress(message: string, params: any[] = []) { Hydro.model.message.sendInfo(this.user._id, JSON.stringify({ message, params })); }, async limitRate( diff --git a/packages/ui-default/components/message/worker.ts b/packages/ui-default/components/message/worker.ts index 28d722b75..463438260 100644 --- a/packages/ui-default/components/message/worker.ts +++ b/packages/ui-default/components/message/worker.ts @@ -9,7 +9,7 @@ console.log('SharedWorker init'); let conn: ReconnectingWebsocket; let lcookie: string; -let ports: MessagePort[] = []; +const ports: Set = new Set(); interface RequestInitSharedConnPayload { type: 'conn'; cookie: string; @@ -29,6 +29,7 @@ function broadcastMsg(message: any) { for (const p of ports) p.postMessage(message); } function initConn(path: string, port: MessagePort, cookie: any) { + ports.add(port); if (cookie !== lcookie) conn?.close(); else if (conn && conn.readyState === conn.OPEN) return; const url = new URL(path, location.origin); @@ -37,7 +38,6 @@ function initConn(path: string, port: MessagePort, cookie: any) { console.log('Init connection for', path); conn?.close(); conn = new ReconnectingWebsocket(url.toString()); - ports.push(port); conn.onopen = () => { console.log('Connected to', path); broadcastMsg({ type: 'open' }); @@ -85,8 +85,8 @@ self.onconnect = function (e) { // eslint-disable-line no-undef port.addEventListener('message', (msg: { data: RequestPayload }) => { if (msg.data.type === 'conn') initConn(msg.data.path, port, msg.data.cookie); - if (msg.data.type === 'ack') ack[msg.data.id](); - if (msg.data.type === 'unload') ports = ports.filter((i) => i !== port); + if (msg.data.type === 'ack') ack[msg.data.id]?.(); + if (msg.data.type === 'unload') ports.delete(port); }); port.start();