From 70a96aecac6b53a66871929b77850e242c1ccc5b Mon Sep 17 00:00:00 2001 From: undefined Date: Fri, 30 Sep 2022 19:02:40 +0800 Subject: [PATCH] core: use common plugin-api interface --- build/prepare.js | 2 +- packages/center/{handler.ts => index.ts} | 11 +-- packages/center/package.json | 7 -- packages/elastic/{script.ts => index.ts} | 24 +++-- .../fps-importer/{handler.ts => index.ts} | 35 +++---- packages/fps-importer/package.json | 4 +- packages/hydrojudge/src/hosts/builtin.ts | 24 +++-- packages/hydrojudge/src/service.ts | 10 +- packages/hydrooj/package.json | 4 +- packages/hydrooj/src/context.ts | 3 +- packages/hydrooj/src/entry/cli.ts | 2 +- packages/hydrooj/src/entry/worker.ts | 2 +- packages/hydrooj/src/handler/import.ts | 4 +- packages/hydrooj/src/interface.ts | 11 +-- packages/hydrooj/src/loader.ts | 3 - packages/hydrooj/src/model/discussion.ts | 2 +- packages/hydrooj/src/plugin-api.ts | 48 +++++++++ packages/hydrooj/src/service/layers/base.ts | 6 +- packages/hydrooj/src/service/layers/domain.ts | 8 +- .../hydrooj/src/service/layers/renderer.ts | 8 +- .../hydrooj/src/service/layers/response.ts | 6 +- packages/hydrooj/src/service/layers/user.ts | 8 +- .../import-qduoj/{handler.ts => index.ts} | 27 +++--- packages/import-qduoj/package.json | 4 +- packages/login-with-github/index.ts | 21 ++-- packages/login-with-github/package.json | 6 -- .../login-with-google/{lib.ts => index.ts} | 27 +++--- packages/login-with-google/package.json | 6 -- packages/migrate/index.ts | 5 +- packages/migrate/package.json | 2 - packages/migrate/scripts/hustoj.ts | 56 +++++------ packages/migrate/scripts/syzoj.ts | 95 ++++++++---------- packages/migrate/scripts/vijos.ts | 7 +- packages/onlyoffice/handler.ts | 7 +- packages/prom-client/handler.ts | 16 ++- packages/prom-client/metrics.ts | 2 +- packages/recaptcha/handler.ts | 27 ------ packages/recaptcha/index.ts | 27 ++++++ packages/recaptcha/package.json | 5 +- packages/sonic/index.ts | 97 ++++++++++--------- packages/sonic/model.ts | 58 ----------- packages/sonic/script.ts | 37 ------- packages/sonic/service.ts | 31 +++--- packages/ui-default/handler.ts | 56 +++++------ packages/vjudge/src/interface.ts | 2 +- packages/vjudge/src/model.ts | 23 ++--- packages/vjudge/src/providers/codeforces.ts | 10 +- packages/vjudge/src/providers/csgoj.ts | 4 +- packages/vjudge/src/providers/index.ts | 2 +- packages/vjudge/src/providers/luogu.ts | 9 +- packages/vjudge/src/providers/poj.ts | 9 +- packages/vjudge/src/providers/spoj.ts | 9 +- packages/vjudge/src/providers/uoj.ts | 9 +- typedoc.json | 25 ----- 54 files changed, 387 insertions(+), 566 deletions(-) rename packages/center/{handler.ts => index.ts} (88%) rename packages/elastic/{script.ts => index.ts} (80%) rename packages/fps-importer/{handler.ts => index.ts} (77%) create mode 100644 packages/hydrooj/src/plugin-api.ts rename packages/import-qduoj/{handler.ts => index.ts} (83%) rename packages/login-with-google/{lib.ts => index.ts} (78%) delete mode 100644 packages/recaptcha/handler.ts create mode 100644 packages/recaptcha/index.ts delete mode 100644 packages/sonic/model.ts delete mode 100644 packages/sonic/script.ts delete mode 100644 typedoc.json diff --git a/build/prepare.js b/build/prepare.js index 175fa8598..21a105b98 100644 --- a/build/prepare.js +++ b/build/prepare.js @@ -9,7 +9,6 @@ const compilerOptionsBase = { module: 'commonjs', esModuleInterop: true, moduleResolution: 'node', - // declaration: true, sourceMap: false, composite: true, strictBindCallApply: true, @@ -48,6 +47,7 @@ const configFlat = (name) => (name === 'packages/ui-default' ? { allowSyntheticDefaultImports: true, target: 'es2020', baseUrl: '.', + outDir: path.join(baseOutDir, name), moduleResolution: 'node', types, paths: { diff --git a/packages/center/handler.ts b/packages/center/index.ts similarity index 88% rename from packages/center/handler.ts rename to packages/center/index.ts index 5ecf8da86..e2e127871 100644 --- a/packages/center/handler.ts +++ b/packages/center/index.ts @@ -1,9 +1,8 @@ import assert from 'assert'; import crypto from 'crypto'; -import yaml from 'js-yaml'; -import * as bus from 'hydrooj/src/service/bus'; -import db from 'hydrooj/src/service/db'; -import { Handler, post, Types } from 'hydrooj/src/service/server'; +import { + Context, db, Handler, post, Types, yaml, +} from 'hydrooj'; function decrypt(encrypted: string) { if (!encrypted) throw new Error(); @@ -13,7 +12,7 @@ function decrypt(encrypted: string) { return decrypted; } -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface Collections { dataReport: any; } @@ -61,6 +60,6 @@ class DataReportHandler extends Handler { } } -export function apply(ctx) { +export function apply(ctx: Context) { ctx.Route('data_report', '/center/report', DataReportHandler); } diff --git a/packages/center/package.json b/packages/center/package.json index f1827bee3..cb16b3495 100644 --- a/packages/center/package.json +++ b/packages/center/package.json @@ -6,13 +6,6 @@ "author": "undefined ", "license": "AGPL-3.0-or-later", "preferUnplugged": true, - "dependencies": { - "js-yaml": "^4.1.0", - "superagent": "^8.0.0" - }, - "devDependencies": { - "@types/js-yaml": "^4.0.5" - }, "peerDependencies": { "hydrooj": "*" } diff --git a/packages/elastic/script.ts b/packages/elastic/index.ts similarity index 80% rename from packages/elastic/script.ts rename to packages/elastic/index.ts index e40954c69..d88bc416e 100644 --- a/packages/elastic/script.ts +++ b/packages/elastic/index.ts @@ -1,13 +1,11 @@ import { Client } from '@elastic/elasticsearch'; import { omit } from 'lodash'; -import { Schema } from 'hydrooj'; -import DomainModel from 'hydrooj/src/model/domain'; -import ProblemModel, { ProblemDoc } from 'hydrooj/src/model/problem'; -import * as system from 'hydrooj/src/model/system'; -import { iterateAllProblem, iterateAllProblemInDomain } from 'hydrooj/src/pipelineUtils'; -import * as bus from 'hydrooj/src/service/bus'; +import { + Context, DomainModel, iterateAllProblem, iterateAllProblemInDomain, + ProblemDoc, ProblemModel, Schema, SystemModel, +} from 'hydrooj'; -const client = new Client({ node: system.get('elastic-search.url') || 'http://127.0.0.1:9200' }); +const client = new Client({ node: SystemModel.get('elastic-search.url') || 'http://127.0.0.1:9200' }); const indexOmit = ['_id', 'docType', 'data', 'additional_file', 'config', 'stats', 'assign']; const processDocument = (doc: Partial) => { @@ -25,8 +23,8 @@ const processDocument = (doc: Partial) => { }; global.Hydro.lib.problemSearch = async (domainId, q, opts) => { - const allowedSize = system.get('elasic-search.indexSize') || 10000; - const size = opts?.limit || system.get('pagination.problem'); + const allowedSize = SystemModel.get('elasic-search.indexSize') || 10000; + const size = opts?.limit || SystemModel.get('pagination.problem'); const from = Math.min(allowedSize - size, opts?.skip || 0); const union = await DomainModel.getUnion(domainId); const domainIds = [domainId, ...(union?.union || [])]; @@ -77,22 +75,22 @@ async function run({ domainId }, report) { return true; } -export const apply = (ctx) => { - bus.on('problem/add', async (doc, docId) => { +export const apply = (ctx: Context) => { + ctx.on('problem/add', async (doc, docId) => { await client.index({ index: 'problem', id: `${doc.domainId}/${docId}`, document: processDocument(doc), }); }); - bus.on('problem/edit', async (pdoc) => { + ctx.on('problem/edit', async (pdoc) => { await client.index({ index: 'problem', id: `${pdoc.domainId}/${pdoc.docId}`, document: processDocument(pdoc), }); }); - bus.on('problem/del', async (domainId, docId) => { + ctx.on('problem/del', async (domainId, docId) => { await client.delete({ index: 'problem', id: `${domainId}/${docId}`, diff --git a/packages/fps-importer/handler.ts b/packages/fps-importer/index.ts similarity index 77% rename from packages/fps-importer/handler.ts rename to packages/fps-importer/index.ts index 78a726905..a91beba9d 100644 --- a/packages/fps-importer/handler.ts +++ b/packages/fps-importer/index.ts @@ -1,16 +1,11 @@ /* eslint-disable no-await-in-loop */ -import AdmZip from 'adm-zip'; import decodeHTML from 'decode-html'; -import fs from 'fs-extra'; import { filter } from 'lodash'; import xml2js from 'xml2js'; -import type { ContentNode, PluginContext, ProblemConfigFile } from 'hydrooj'; -import { FileTooLargeError, ValidationError } from 'hydrooj/src/error'; -import { buildContent } from 'hydrooj/src/lib/content'; -import { PERM } from 'hydrooj/src/model/builtin'; -import problem from 'hydrooj/src/model/problem'; -import solution from 'hydrooj/src/model/solution'; -import { Handler } from 'hydrooj/src/service/server'; +import { + AdmZip, buildContent, ContentNode, Context, FileTooLargeError, fs, + Handler, PERM, ProblemConfigFile, ProblemModel, SolutionModel, ValidationError, +} from 'hydrooj'; class FpsProblemImportHandler extends Handler { async get() { @@ -65,8 +60,8 @@ class FpsProblemImportHandler extends Handler { }; const title = decodeHTML(p.title.join(' ')); const tags = filter(p.source, (i: string) => i.trim()); - const pid = await problem.add(domainId, null, title, buildContent(content, 'html'), this.user._id, tags); - await problem.addTestdata(domainId, pid, 'config.yaml', Buffer.from(`time: ${config.time}\nmemory: ${config.memory}`)); + const pid = await ProblemModel.add(domainId, null, title, buildContent(content, 'html'), this.user._id, tags); + await ProblemModel.addTestdata(domainId, pid, 'config.yaml', Buffer.from(`time: ${config.time}\nmemory: ${config.memory}`)); if (p.test_output) { for (let i = 0; i < p.test_input.length; i++) { const input = typeof p.test_input[i]?._ === 'string' @@ -79,22 +74,22 @@ class FpsProblemImportHandler extends Handler { : typeof p.test_output[i] === 'string' ? p.test_output[i] : ''; - await problem.addTestdata(domainId, pid, `${i + 1}.in`, Buffer.from(input)); - await problem.addTestdata(domainId, pid, `${i + 1}.out`, Buffer.from(output)); + await ProblemModel.addTestdata(domainId, pid, `${i + 1}.in`, Buffer.from(input)); + await ProblemModel.addTestdata(domainId, pid, `${i + 1}.out`, Buffer.from(output)); } } else if (p.test_input) { for (let i = 0; i < p.test_input.length / 2; i++) { - await problem.addTestdata(domainId, pid, `${i + 1}.in`, Buffer.from(p.test_input[2 * i])); - await problem.addTestdata(domainId, pid, `${i + 1}.out`, Buffer.from(p.test_input[2 * i + 1])); + await ProblemModel.addTestdata(domainId, pid, `${i + 1}.in`, Buffer.from(p.test_input[2 * i])); + await ProblemModel.addTestdata(domainId, pid, `${i + 1}.out`, Buffer.from(p.test_input[2 * i + 1])); } } - await problem.edit(domainId, pid, { html: true }); + await ProblemModel.edit(domainId, pid, { html: true }); if (p.solution) { let s = ''; for (const sol of p.solution) { s += `**${sol.$.language}** : \n\`\`\`\n${sol._}\n\`\`\`\n`; } - await solution.add(domainId, pid, this.user._id, s); + await SolutionModel.add(domainId, pid, this.user._id, s); } } } @@ -114,8 +109,8 @@ class FpsProblemImportHandler extends Handler { let zip: AdmZip; try { zip = new AdmZip(this.request.files.file.filepath); - } catch (e) { - throw new ValidationError('zip', null, e.message); + } catch (err) { + throw new ValidationError('zip', null, err.message); } for (const entry of zip.getEntries()) { try { @@ -134,7 +129,7 @@ class FpsProblemImportHandler extends Handler { } export const sideEffect = true; -export async function apply(ctx: PluginContext) { +export async function apply(ctx: Context) { ctx.Route('problem_import_fps', '/problem/import/fps', FpsProblemImportHandler, PERM.PERM_CREATE_PROBLEM); ctx.inject('ProblemAdd', 'problem_import_fps', { icon: 'copy', text: 'From FPS File' }); } diff --git a/packages/fps-importer/package.json b/packages/fps-importer/package.json index 96cf8473d..fb02388e2 100644 --- a/packages/fps-importer/package.json +++ b/packages/fps-importer/package.json @@ -2,7 +2,7 @@ "name": "@hydrooj/fps-importer", "version": "1.3.4", "description": "Import FPS problems", - "main": "package.json", + "main": "index.ts", "repository": "https://github.com/hydro-dev/Hydro.git", "author": "undefined ", "license": "SEE LICENSE IN LICENSE", @@ -10,8 +10,6 @@ "dependencies": { "adm-zip": "0.5.5", "decode-html": "^2.0.0", - "fs-extra": "^10.1.0", - "mongodb": "^3.7.3", "xml2js": "^0.4.23" }, "devDependencies": { diff --git a/packages/hydrojudge/src/hosts/builtin.ts b/packages/hydrojudge/src/hosts/builtin.ts index 87e32b677..dec423901 100644 --- a/packages/hydrojudge/src/hosts/builtin.ts +++ b/packages/hydrojudge/src/hosts/builtin.ts @@ -3,12 +3,10 @@ import path from 'path'; import fs from 'fs-extra'; import type { JudgeResultBody } from 'hydrooj'; +import { + Logger, SettingModel, StorageModel, SystemModel, TaskModel, +} from 'hydrooj'; import { end, next } from 'hydrooj/src/handler/judge'; -import { Logger } from 'hydrooj/src/logger'; -import * as setting from 'hydrooj/src/model/setting'; -import storage from 'hydrooj/src/model/storage'; -import * as system from 'hydrooj/src/model/system'; -import task from 'hydrooj/src/model/task'; import { processTestdata } from '../cases'; import { getConfig } from '../config'; import { FormatError, SystemError } from '../error'; @@ -18,11 +16,11 @@ import { JudgeTask } from '../task'; const logger = new Logger('judge'); const session = { - config: { detail: system.get('hydrojudge.detail') }, + config: { detail: SystemModel.get('hydrojudge.detail') }, async fetchFile(name: string) { name = name.split('#')[0]; const target = path.join(getConfig('tmp_dir'), name.replace(/\//g, '_')); - await storage.get(`submission/${name}`, target); + await StorageModel.get(`submission/${name}`, target); return target; }, getNext(t: Context) { @@ -42,8 +40,8 @@ const session = { }; }, getLang(lang: string, doThrow = true) { - if (setting.langs[lang]) return setting.langs[lang]; - if (lang === 'cpp' && setting.langs['cc']) return setting.langs['cc']; + if (SettingModel.langs[lang]) return SettingModel.langs[lang]; + if (lang === 'cpp' && SettingModel.langs['cc']) return SettingModel.langs['cc']; if (doThrow) throw new SystemError('Unsupported language {0}.', [lang]); return null; }, @@ -61,7 +59,7 @@ const session = { filenames.add(file.name); version[file.name] = file.etag + file.lastModified; if (etags[file.name] !== file.etag + file.lastModified) { - await storage.get(`problem/${source}/testdata/${file.name}`, path.join(filePath, file.name)); + await StorageModel.get(`problem/${source}/testdata/${file.name}`, path.join(filePath, file.name)); } } for (const name in etags) { @@ -77,8 +75,8 @@ const session = { }; export async function postInit() { - if (system.get('hydrojudge.disable')) return; + if (SystemModel.get('hydrojudge.disable')) return; const handle = (t) => (new JudgeTask(session, t)).handle().catch(logger.error); - task.consume({ type: 'judge' }, handle); - task.consume({ type: 'judge', priority: { $gt: -50 } }, handle); + TaskModel.consume({ type: 'judge' }, handle); + TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle); } diff --git a/packages/hydrojudge/src/service.ts b/packages/hydrojudge/src/service.ts index 286b3e724..a1d7c47ce 100644 --- a/packages/hydrojudge/src/service.ts +++ b/packages/hydrojudge/src/service.ts @@ -1,6 +1,6 @@ -import * as bus from 'hydrooj/src/service/bus'; +import { Context } from 'hydrooj'; -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface SystemKeys { 'hydrojudge.cache_dir': string; 'hydrojudge.tmp_dir': string; @@ -15,6 +15,8 @@ declare module 'hydrooj/src/interface' { } } -if (process.env.NODE_APP_INSTANCE === '0') { - bus.once('app/started', () => require('./hosts/builtin').postInit()); +export function apply(ctx: Context) { + if (process.env.NODE_APP_INSTANCE === '0') { + ctx.once('app/started', () => require('./hosts/builtin').postInit()); + } } diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 1f2e6db71..7678e0c11 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -2,8 +2,8 @@ "name": "hydrooj", "version": "3.17.12", "bin": "bin/hydrooj.js", - "main": "src/loader", - "module": "src/loader", + "main": "src/plugin-api", + "module": "src/plugin-api", "repository": "https://github.com/hydro-dev/Hydro.git", "author": "undefined ", "license": "AGPL-3.0-or-later", diff --git a/packages/hydrooj/src/context.ts b/packages/hydrooj/src/context.ts index 20462eccf..817d09f6c 100644 --- a/packages/hydrooj/src/context.ts +++ b/packages/hydrooj/src/context.ts @@ -1,7 +1,8 @@ import * as cordis from 'cordis'; import Schema from 'schemastery'; +import { ModuleInterfaces } from './interface'; import { inject } from './lib/ui'; -import { Loader, ModuleInterfaces } from './loader'; +import { Loader } from './loader'; import { EventMap } from './service/bus'; export interface Events extends cordis.Events, EventMap { diff --git a/packages/hydrooj/src/entry/cli.ts b/packages/hydrooj/src/entry/cli.ts index 67a169ae6..1575d954e 100644 --- a/packages/hydrooj/src/entry/cli.ts +++ b/packages/hydrooj/src/entry/cli.ts @@ -101,7 +101,7 @@ export async function load(ctx: Context) { model(pending, fail, ctx), setting(pending, fail, require('../model/setting')), ]); - await require('hydrooj/src/service/server').apply(ctx); + await require('../service/server').apply(ctx); await addon(pending, fail, ctx); const scriptDir = path.resolve(__dirname, '..', 'script'); for (const h of await fs.readdir(scriptDir)) { diff --git a/packages/hydrooj/src/entry/worker.ts b/packages/hydrooj/src/entry/worker.ts index 741e68a11..4a23aa463 100644 --- a/packages/hydrooj/src/entry/worker.ts +++ b/packages/hydrooj/src/entry/worker.ts @@ -38,7 +38,7 @@ export async function apply(ctx: Context) { await modelSystem.runConfig(); const storage = require('../service/storage'); await storage.loadStorageService(); - await require('hydrooj/src/service/server').apply(ctx); + await require('../service/server').apply(ctx); // Make sure everything is ready and then start main entry if (argv.options.watch) await ctx.loader.reloadPlugin(ctx, 'hydrooj/src/service/watcher', {}); await ctx.root.start(); diff --git a/packages/hydrooj/src/handler/import.ts b/packages/hydrooj/src/handler/import.ts index a3fe91443..945b5ade5 100644 --- a/packages/hydrooj/src/handler/import.ts +++ b/packages/hydrooj/src/handler/import.ts @@ -4,8 +4,8 @@ import path from 'path'; import AdmZip from 'adm-zip'; import fs from 'fs-extra'; import yaml from 'js-yaml'; +import { Context } from '../context'; import { ValidationError } from '../error'; -import { PluginContext } from '../loader'; import { PERM, PRIV } from '../model/builtin'; import problem, { ProblemDoc } from '../model/problem'; import { Handler } from '../service/server'; @@ -98,7 +98,7 @@ class ProblemImportHydroHandler extends Handler { } } -export async function apply(ctx: PluginContext) { +export async function apply(ctx: Context) { ctx.Route('problem_import_hydro', '/problem/import/hydro', ProblemImportHydroHandler, PERM.PERM_CREATE_PROBLEM); ctx.inject('ProblemAdd', 'problem_import_hydro', { icon: 'copy', text: 'Import From Hydro' }); } diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index ca132dbfe..8c717770c 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -1,7 +1,7 @@ import type fs from 'fs'; import type { Dictionary, NumericDictionary } from 'lodash'; import type { Cursor, ObjectID } from 'mongodb'; -import { Context } from './context'; +import type { Context } from './context'; import type { ProblemDoc } from './model/problem'; import type { Handler } from './service/server'; @@ -637,7 +637,7 @@ export interface Model { blog: typeof import('./model/blog'), builtin: typeof import('./model/builtin'), contest: typeof import('./model/contest'), - discussion: typeof import('./model/discussion'), + DiscussionModel: typeof import('./model/discussion'), document: typeof import('./model/document'), domain: typeof import('./model/domain').default, message: typeof import('./model/message').default, @@ -657,7 +657,7 @@ export interface Model { rp: typeof import('./script/rating').RpTypes, } -export interface Service { +export interface HydroService { /** @deprecated */ bus: Context, db: typeof import('./service/db'), @@ -683,8 +683,6 @@ export interface ProblemSearchOptions { export type ProblemSearch = (domainId: string, q: string, options?: ProblemSearchOptions) => Promise; -export { Context as PluginContext } from './context'; - export interface Lib extends Record { difficulty: typeof import('./lib/difficulty'), buildContent: typeof import('./lib/content').buildContent, @@ -725,7 +723,7 @@ export interface HydroGlobal { /** @deprecated */ handler: Record; script: Record; - service: Service; + service: HydroService; lib: Lib; module: { [K in keyof ModuleInterfaces]: Record }; ui: UI; @@ -742,6 +740,7 @@ declare global { addons: string[], } } + var bus: any; // eslint-disable-line var app: Context; // eslint-disable-line var Hydro: HydroGlobal; // eslint-disable-line var addons: string[]; // eslint-disable-line diff --git a/packages/hydrooj/src/loader.ts b/packages/hydrooj/src/loader.ts index 828cbaa83..7bd73b386 100644 --- a/packages/hydrooj/src/loader.ts +++ b/packages/hydrooj/src/loader.ts @@ -16,9 +16,6 @@ import { Logger } from './logger'; import { Context } from './context'; // eslint-disable-next-line import/no-duplicates import { unwrapExports } from './utils'; -// This is the main entry. So let's re-export some modules. -export * from './interface'; -export { Schema, Logger }; const argv = cac().parse(); const logger = new Logger('loader'); diff --git a/packages/hydrooj/src/model/discussion.ts b/packages/hydrooj/src/model/discussion.ts index e466f8202..49836c0b6 100644 --- a/packages/hydrooj/src/model/discussion.ts +++ b/packages/hydrooj/src/model/discussion.ts @@ -302,7 +302,7 @@ bus.on('app/ready', async () => { } }); -global.Hydro.model.discussion = { +global.Hydro.model.DiscussionModel = { typeDisplay, PROJECTION_LIST, PROJECTION_PUBLIC, diff --git a/packages/hydrooj/src/plugin-api.ts b/packages/hydrooj/src/plugin-api.ts new file mode 100644 index 000000000..a65c946d7 --- /dev/null +++ b/packages/hydrooj/src/plugin-api.ts @@ -0,0 +1,48 @@ +import AdmZip from 'adm-zip'; +import fs from 'fs-extra'; +import yaml from 'js-yaml'; +import Schema from 'schemastery'; +import superagent from 'superagent'; +import * as db from './service/db'; +export { ObjectID, ObjectId } from 'mongodb'; +export * from './utils'; +export * from './interface'; +export * from './pipelineUtils'; +export * from './error'; +export * from './settings'; +export * as SystemModel from './model/system'; +export * as TrainingModel from './model/training'; +export * as OpcountModel from './model/opcount'; +export * as OplogModel from './model/oplog'; +export * as BlogModel from './model/blog'; +export * as SettingModel from './model/setting'; +export * as DiscussionModel from './model/discussion'; +export * as DocumentModel from './model/document'; +export * as BuiltinModel from './model/builtin'; +export * as ContestModel from './model/contest'; +export { default as TokenModel } from './model/token'; +export { default as UserModel } from './model/user'; +export { default as ProblemModel } from './model/problem'; +export { default as RecordModel } from './model/record'; +export { default as SolutionModel } from './model/solution'; +export { default as MessageModel } from './model/message'; +export { default as OauthModel } from './model/oauth'; +export { default as BlackListModel } from './model/blacklist'; +export { default as DomainModel } from './model/domain'; +export { default as StorageModel } from './model/storage'; +export { default as TaskModel } from './model/task'; +export * from './model/builtin'; +export * as JudgeHandler from './handler/judge'; +export { postJudge } from './handler/judge'; +export { Service, Context } from './context'; +export { buildContent } from './lib/content'; +export * as validator from './lib/validator'; +export { default as rank } from './lib/rank'; +export { default as paginate } from './lib/paginate'; +export * from './service/decorators'; +export { Handler, ConnectionHandler } from './service/server'; +export { UiContextBase } from './service/layers/base'; +export { EventMap } from './service/bus'; +export { + db, Schema, yaml, fs, AdmZip, superagent, +}; diff --git a/packages/hydrooj/src/service/layers/base.ts b/packages/hydrooj/src/service/layers/base.ts index 1db2b70bf..19921b308 100644 --- a/packages/hydrooj/src/service/layers/base.ts +++ b/packages/hydrooj/src/service/layers/base.ts @@ -1,8 +1,8 @@ import { PassThrough } from 'stream'; import { cloneDeep } from 'lodash'; -import * as system from 'hydrooj/src/model/system'; -import token from 'hydrooj/src/model/token'; -import type { HydroRequest, HydroResponse } from 'hydrooj/src/service/server'; +import * as system from '../../model/system'; +import token from '../../model/token'; +import type { HydroRequest, HydroResponse } from '../server'; export interface UiContextBase { cdn_prefix: string; diff --git a/packages/hydrooj/src/service/layers/domain.ts b/packages/hydrooj/src/service/layers/domain.ts index b77cb6fc7..cbe8d43c1 100644 --- a/packages/hydrooj/src/service/layers/domain.ts +++ b/packages/hydrooj/src/service/layers/domain.ts @@ -1,7 +1,7 @@ -import BlackListModel from 'hydrooj/src/model/blacklist'; -import DomainModel from 'hydrooj/src/model/domain'; -import * as system from 'hydrooj/src/model/system'; -import token from 'hydrooj/src/model/token'; +import BlackListModel from '../../model/blacklist'; +import DomainModel from '../../model/domain'; +import * as system from '../../model/system'; +import token from '../../model/token'; export default async (ctx, next) => { const forceDomain = /^\/d\/([^/]+)\//.exec(ctx.request.path); diff --git a/packages/hydrooj/src/service/layers/renderer.ts b/packages/hydrooj/src/service/layers/renderer.ts index 264da7dc4..d6a6eac54 100644 --- a/packages/hydrooj/src/service/layers/renderer.ts +++ b/packages/hydrooj/src/service/layers/renderer.ts @@ -1,8 +1,8 @@ import { ObjectID } from 'mongodb'; -import avatar from 'hydrooj/src/lib/avatar'; -import { PERM } from 'hydrooj/src/model/builtin'; -import * as system from 'hydrooj/src/model/system'; -import { User } from 'hydrooj/src/model/user'; +import avatar from '../../lib/avatar'; +import { PERM } from '../../model/builtin'; +import * as system from '../../model/system'; +import { User } from '../../model/user'; import type { KoaContext } from '../server'; const serializer = (showDisplayName = false) => (k: string, v: any) => { diff --git a/packages/hydrooj/src/service/layers/response.ts b/packages/hydrooj/src/service/layers/response.ts index b2bc5da4c..06fed2605 100644 --- a/packages/hydrooj/src/service/layers/response.ts +++ b/packages/hydrooj/src/service/layers/response.ts @@ -1,7 +1,7 @@ import { errorMessage } from '@hydrooj/utils/lib/utils'; -import { SystemError, UserFacingError } from 'hydrooj/src/error'; -import { PERM } from 'hydrooj/src/model/builtin'; -import { User } from 'hydrooj/src/model/user'; +import { SystemError, UserFacingError } from '../../error'; +import { PERM } from '../../model/builtin'; +import { User } from '../../model/user'; import type { KoaContext } from '../server'; const serializer = (showDisplayName = false) => (k: string, v: any) => { diff --git a/packages/hydrooj/src/service/layers/user.ts b/packages/hydrooj/src/service/layers/user.ts index 6997269ae..598d0a0e0 100644 --- a/packages/hydrooj/src/service/layers/user.ts +++ b/packages/hydrooj/src/service/layers/user.ts @@ -1,7 +1,7 @@ -import { CsrfTokenError, NotFoundError } from 'hydrooj/src/error'; -import avatar from 'hydrooj/src/lib/avatar'; -import { PERM } from 'hydrooj/src/model/builtin'; -import UserModel from 'hydrooj/src/model/user'; +import { CsrfTokenError, NotFoundError } from '../../error'; +import avatar from '../../lib/avatar'; +import { PERM } from '../../model/builtin'; +import UserModel from '../../model/user'; import type { KoaContext } from '../server'; export default async (ctx: KoaContext, next) => { diff --git a/packages/import-qduoj/handler.ts b/packages/import-qduoj/index.ts similarity index 83% rename from packages/import-qduoj/handler.ts rename to packages/import-qduoj/index.ts index ef05679a3..a74f1b03e 100644 --- a/packages/import-qduoj/handler.ts +++ b/packages/import-qduoj/index.ts @@ -1,15 +1,10 @@ /* eslint-disable no-await-in-loop */ import os from 'os'; import path from 'path'; -import AdmZip from 'adm-zip'; -import fs from 'fs-extra'; -import yaml from 'js-yaml'; -import type { ContentNode, PluginContext, ProblemConfigFile } from 'hydrooj'; -import { ValidationError } from 'hydrooj/src/error'; -import { buildContent } from 'hydrooj/src/lib/content'; -import { PERM } from 'hydrooj/src/model/builtin'; -import problem from 'hydrooj/src/model/problem'; -import { Handler } from 'hydrooj/src/service/server'; +import { + AdmZip, buildContent, ContentNode, Context, fs, Handler, PERM, + ProblemConfigFile, ProblemModel, ValidationError, yaml, +} from 'hydrooj'; fs.ensureDirSync('/tmp/hydro/import-qduoj'); @@ -79,9 +74,9 @@ class ImportQduojHandler extends Handler { }); } if (+pdoc.display_id) pdoc.display_id = `P${pdoc.display_id}`; - const n = await problem.get(domainId, pdoc.display_id); + const n = await ProblemModel.get(domainId, pdoc.display_id); if (n) pdoc.display_id = null; - const pid = await problem.add(domainId, pdoc.display_id, pdoc.title, buildContent(content, 'html'), this.user._id, pdoc.tags); + const pid = await ProblemModel.add(domainId, pdoc.display_id, pdoc.title, buildContent(content, 'html'), this.user._id, pdoc.tags); const config: ProblemConfigFile = { time: `${pdoc.time_limit}ms`, memory: `${pdoc.memory_limit}m`, @@ -89,11 +84,11 @@ class ImportQduojHandler extends Handler { }; for (const tc of pdoc.test_case_score) { await Promise.all([ - problem.addTestdata( + ProblemModel.addTestdata( domainId, pid, tc.input_name, path.join(tmp, folder, 'testcase', tc.input_name), ), - problem.addTestdata( + ProblemModel.addTestdata( domainId, pid, tc.output_name, path.join(tmp, folder, 'testcase', tc.output_name), ), @@ -107,8 +102,8 @@ class ImportQduojHandler extends Handler { }); } await Promise.all([ - problem.addTestdata(domainId, pid, 'config.yaml', Buffer.from(yaml.dump(config))), - problem.edit(domainId, pid, { html: true }), + ProblemModel.addTestdata(domainId, pid, 'config.yaml', Buffer.from(yaml.dump(config))), + ProblemModel.edit(domainId, pid, { html: true }), ]); } } finally { @@ -130,7 +125,7 @@ class ImportQduojHandler extends Handler { } } -export async function apply(ctx: PluginContext) { +export async function apply(ctx: Context) { ctx.Route('problem_import_qduoj', '/problem/import/qduoj', ImportQduojHandler, PERM.PERM_CREATE_PROBLEM); ctx.inject('ProblemAdd', 'problem_import_qduoj', { icon: 'copy', text: 'From QDUOJ Export' }); } diff --git a/packages/import-qduoj/package.json b/packages/import-qduoj/package.json index 35ab4d7cb..b36b03422 100644 --- a/packages/import-qduoj/package.json +++ b/packages/import-qduoj/package.json @@ -8,8 +8,6 @@ "license": "AGPL-3.0-or-later", "preferUnplugged": true, "dependencies": { - "adm-zip": "0.5.5", - "decode-html": "^2.0.0", - "fs-extra": "^10.1.0" + "decode-html": "^2.0.0" } } diff --git a/packages/login-with-github/index.ts b/packages/login-with-github/index.ts index 4a8f18f62..4625cf07e 100644 --- a/packages/login-with-github/index.ts +++ b/packages/login-with-github/index.ts @@ -1,8 +1,7 @@ -import superagent from 'superagent'; -import { PluginContext } from 'hydrooj'; -import { ForbiddenError, UserFacingError, ValidationError } from 'hydrooj/src/error'; -import * as system from 'hydrooj/src/model/system'; -import token from 'hydrooj/src/model/token'; +import { + Context, ForbiddenError, superagent, SystemModel, + TokenModel, UserFacingError, ValidationError, +} from 'hydrooj'; declare module 'hydrooj' { interface SystemKeys { @@ -14,21 +13,21 @@ declare module 'hydrooj' { async function get() { const [appid, [state]] = await Promise.all([ - system.get('login-with-github.id'), - token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }), + SystemModel.get('login-with-github.id'), + TokenModel.add(TokenModel.TYPE_OAUTH, 600, { redirect: this.request.referer }), ]); this.response.redirect = `https://github.com/login/oauth/authorize?client_id=${appid}&state=${state}&scope=read:user,user:email`; } async function callback({ state, code }) { const [[appid, secret, endpoint, url], s] = await Promise.all([ - system.getMany([ + SystemModel.getMany([ 'login-with-github.id', 'login-with-github.secret', 'login-with-github.endpoint', 'server.url', ]), - token.get(state, token.TYPE_OAUTH), + TokenModel.get(state, TokenModel.TYPE_OAUTH), ]); if (!s) throw new ValidationError('token'); const res = await superagent.post(`${endpoint || 'https://github.com'}/login/oauth/access_token`) @@ -66,12 +65,12 @@ async function callback({ state, code }) { ret.email = emailInfo.body.find((e) => e.primary && e.verified).email; } } - await token.del(s._id, token.TYPE_OAUTH); + await TokenModel.del(s._id, TokenModel.TYPE_OAUTH); if (!ret.email) throw new ForbiddenError("You don't have a verified email."); return ret; } -export function apply(ctx: PluginContext) { +export function apply(ctx: Context) { ctx.provideModule('oauth', 'github', { text: 'Login with Github', callback, diff --git a/packages/login-with-github/package.json b/packages/login-with-github/package.json index 515c1470d..bf2e1f5dd 100644 --- a/packages/login-with-github/package.json +++ b/packages/login-with-github/package.json @@ -6,13 +6,7 @@ "author": "undefined ", "license": "AGPL-3.0-or-later", "preferUnplugged": true, - "devDependencies": { - "@types/superagent": "^4.1.15" - }, "peerDependencies": { "hydrooj": "*" - }, - "dependencies": { - "superagent": "^8.0.0" } } diff --git a/packages/login-with-google/lib.ts b/packages/login-with-google/index.ts similarity index 78% rename from packages/login-with-google/lib.ts rename to packages/login-with-google/index.ts index 16080969f..7e94bcb01 100644 --- a/packages/login-with-google/lib.ts +++ b/packages/login-with-google/index.ts @@ -1,5 +1,6 @@ -import * as superagent from 'superagent'; -import { PluginContext } from 'hydrooj'; +import { + Context, superagent, SystemModel, TokenModel, UserFacingError, +} from 'hydrooj'; declare module 'hydrooj' { interface SystemKeys { @@ -9,11 +10,10 @@ declare module 'hydrooj' { } async function get() { - const { system, token } = global.Hydro.model; const [appid, url, [state]] = await Promise.all([ - system.get('login-with-google.id'), - system.get('server.url'), - token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }), + SystemModel.get('login-with-google.id'), + SystemModel.get('server.url'), + TokenModel.add(TokenModel.TYPE_OAUTH, 600, { redirect: this.request.referer }), ]); // eslint-disable-next-line max-len this.response.redirect = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${appid}&response_type=code&redirect_uri=${url}oauth/google/callback&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&state=${state}`; @@ -45,17 +45,12 @@ function decodeJWT(idToken: string) { async function callback({ state, code, error, }) { - const { system, token } = global.Hydro.model; - const { UserFacingError } = global.Hydro.error; if (error) throw new UserFacingError(error); - const [ - [appid, secret, url], - s, - ] = await Promise.all([ - system.getMany([ + const [[appid, secret, url], s] = await Promise.all([ + SystemModel.getMany([ 'login-with-google.id', 'login-with-google.secret', 'server.url', ]), - token.get(state, token.TYPE_OAUTH), + TokenModel.get(state, TokenModel.TYPE_OAUTH), ]); const res = await superagent.post('https://oauth2.googleapis.com/token') .send({ @@ -66,7 +61,7 @@ async function callback({ redirect_uri: `${url}oauth/google/callback`, }); const payload = decodeJWT(res.body.id_token).payload; - await token.del(state, token.TYPE_OAUTH); + await TokenModel.del(state, TokenModel.TYPE_OAUTH); this.response.redirect = s.redirect; return { // TODO use openid @@ -77,7 +72,7 @@ async function callback({ }; } -export function apply(ctx: PluginContext) { +export function apply(ctx: Context) { ctx.provideModule('oauth', 'google', { text: 'Login with Google', callback, diff --git a/packages/login-with-google/package.json b/packages/login-with-google/package.json index 58d496780..9c315542d 100644 --- a/packages/login-with-google/package.json +++ b/packages/login-with-google/package.json @@ -6,13 +6,7 @@ "author": "undefined ", "license": "AGPL-3.0-or-later", "preferUnplugged": true, - "devDependencies": { - "@types/superagent": "^4.1.15" - }, "peerDependencies": { "hydrooj": "*" - }, - "dependencies": { - "superagent": "^8.0.0" } } diff --git a/packages/migrate/index.ts b/packages/migrate/index.ts index 35608a942..6b3cf9db8 100644 --- a/packages/migrate/index.ts +++ b/packages/migrate/index.ts @@ -1,12 +1,11 @@ -import { md5, sha1 } from '@hydrooj/utils/lib/utils'; -import { PluginContext } from 'hydrooj'; +import { Context, md5, sha1 } from 'hydrooj'; import { apply as hustoj } from './scripts/hustoj'; import { apply as syzoj } from './scripts/syzoj'; import { apply as vijos } from './scripts/vijos'; const RE_MD5 = /^[\da-f]{32}$/; -export function apply(ctx: PluginContext) { +export function apply(ctx: Context) { hustoj(ctx); vijos(ctx); syzoj(ctx); diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 3cb0832ae..23f08b50c 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -11,8 +11,6 @@ "@types/turndown": "^5.0.1" }, "dependencies": { - "@hydrooj/utils": "workspace:*", - "js-yaml": "^4.1.0", "mariadb": "^3.0.1", "mongodb": "^3.7.3", "mysql": "^2.18.1", diff --git a/packages/migrate/scripts/hustoj.ts b/packages/migrate/scripts/hustoj.ts index cab44a455..cb9232b18 100644 --- a/packages/migrate/scripts/hustoj.ts +++ b/packages/migrate/scripts/hustoj.ts @@ -1,23 +1,11 @@ /* eslint-disable no-tabs */ /* eslint-disable no-await-in-loop */ -import fs from 'fs-extra'; -import { ObjectID } from 'mongodb'; import mysql from 'mysql'; import TurndownService from 'turndown'; -import { STATUS } from '@hydrooj/utils/lib/status'; -import { noop, Time } from '@hydrooj/utils/lib/utils'; -import { Schema } from 'hydrooj'; -import { NotFoundError } from 'hydrooj/src/error'; -import { postJudge } from 'hydrooj/src/handler/judge'; -import { RecordDoc } from 'hydrooj/src/interface'; -import { buildContent } from 'hydrooj/src/lib/content'; -import * as contest from 'hydrooj/src/model/contest'; -import domain from 'hydrooj/src/model/domain'; -import problem from 'hydrooj/src/model/problem'; -import record from 'hydrooj/src/model/record'; -import SolutionModel from 'hydrooj/src/model/solution'; -import * as system from 'hydrooj/src/model/system'; -import user from 'hydrooj/src/model/user'; +import { + buildContent, ContestModel, DomainModel, fs, noop, NotFoundError, ObjectID, postJudge, ProblemModel, + RecordDoc, RecordModel, Schema, SolutionModel, STATUS, SystemModel, Time, UserModel, +} from 'hydrooj'; const turndown = new TurndownService({ codeBlockStyle: 'fenced', @@ -81,7 +69,7 @@ export async function run({ else res([val, fields]); }); }); - const target = await domain.get(domainId); + const target = await DomainModel.get(domainId); if (!target) throw new NotFoundError(domainId); report({ message: 'Connected to database' }); /* @@ -101,22 +89,22 @@ export async function run({ */ const uidMap: Record = {}; const [udocs] = await query('SELECT * FROM `users`'); - const precheck = await user.getMulti({ unameLower: { $in: udocs.map((u) => u.user_id.toLowerCase()) } }).toArray(); + const precheck = await UserModel.getMulti({ unameLower: { $in: udocs.map((u) => u.user_id.toLowerCase()) } }).toArray(); if (precheck.length) throw new Error(`Conflict username: ${precheck.map((u) => u.unameLower).join(', ')}`); for (const udoc of udocs) { if (randomMail) delete udoc.email; - let current = await user.getByEmail(domainId, udoc.email || `${udoc.user_id}@hustoj.local`); - if (!current) current = await user.getByUname(domainId, udoc.user_id); + let current = await UserModel.getByEmail(domainId, udoc.email || `${udoc.user_id}@hustoj.local`); + if (!current) current = await UserModel.getByUname(domainId, udoc.user_id); if (current) { report({ message: `duplicate user with email ${udoc.email}: ${current.uname},${udoc.user_id}` }); uidMap[udoc.user_id] = current._id; } else { - const uid = await user.create( + const uid = await UserModel.create( udoc.email || `${udoc.user_id}@hustoj.local`, udoc.user_id, '', - null, udoc.ip, udoc.defunct === 'Y' ? 0 : system.get('default.priv'), + null, udoc.ip, udoc.defunct === 'Y' ? 0 : SystemModel.get('default.priv'), ); uidMap[udoc.user_id] = uid; - await user.setById(uid, { + await UserModel.setById(uid, { loginat: udoc.accesstime, regat: udoc.reg_time, hash: udoc.password, @@ -124,7 +112,7 @@ export async function run({ school: udoc.school || '', hashType: 'hust', }); - await domain.setUserInDomain(domainId, uid, { + await DomainModel.setUserInDomain(domainId, uid, { displayName: udoc.nick || '', school: udoc.school || '', nSubmit: udoc.submit, @@ -134,7 +122,7 @@ export async function run({ } const [admins] = await query("SELECT * FROM `privilege` WHERE `rightstr` = 'administrator'"); - for (const admin of admins) await domain.setUserRole(domainId, uidMap[admin.user_id], 'root'); + for (const admin of admins) await DomainModel.setUserRole(domainId, uidMap[admin.user_id], 'root'); const adminUids = admins.map((admin) => uidMap[admin.user_id]); report({ message: 'user finished' }); @@ -167,11 +155,11 @@ export async function run({ const [pdocs] = await query(`SELECT * FROM \`problem\` LIMIT ${pageId * step}, ${step}`); for (const pdoc of pdocs) { if (rerun) { - const opdoc = await problem.get(domainId, `P${pdoc.problem_id}`); + const opdoc = await ProblemModel.get(domainId, `P${pdoc.problem_id}`); if (opdoc) pidMap[pdoc.problem_id] = opdoc.docId; } if (!pidMap[pdoc.problem_id]) { - const pid = await problem.add( + const pid = await ProblemModel.add( domainId, `P${pdoc.problem_id}`, pdoc.title, buildContent({ description: pdoc.description, @@ -188,7 +176,7 @@ export async function run({ const [cdoc] = await query(`SELECT * FROM \`privilege\` WHERE \`rightstr\` = 'p${pdoc.problem_id}'`); const maintainer = []; for (let i = 1; i < cdoc.length; i++) maintainer.push(uidMap[cdoc[i].user_id]); - await problem.edit(domainId, pidMap[pdoc.problem_id], { + await ProblemModel.edit(domainId, pidMap[pdoc.problem_id], { nAccept: 0, nSubmit: pdoc.submit, config: `time: ${pdoc.time_limit}s\nmemory: ${pdoc.memory_limit}m`, @@ -221,7 +209,7 @@ export async function run({ for (const tdoc of tdocs) { const [pdocs] = await query(`SELECT * FROM \`contest_problem\` WHERE \`contest_id\` = ${tdoc.contest_id}`); const pids = pdocs.map((i) => pidMap[i.problem_id]).filter((i) => i); - const tid = await contest.add( + const tid = await ContestModel.add( domainId, tdoc.title, tdoc.description || 'Description', adminUids[0], contestType, tdoc.start_time, tdoc.end_time, pids, true, { _code: password }, @@ -282,9 +270,9 @@ export async function run({ if (source[0]?.source) data.code = source[0].source; if (rdoc.contest_id) { data.contest = new ObjectID(tidMap[rdoc.contest_id]); - await contest.attend(domainId, data.contest, uidMap[rdoc.user_id]).catch(noop); + await ContestModel.attend(domainId, data.contest, uidMap[rdoc.user_id]).catch(noop); } - await record.coll.insertOne(data); + await RecordModel.coll.insertOne(data); await postJudge(data).catch((err) => report({ message: err.message })); } } @@ -298,15 +286,15 @@ export async function run({ for (const file of files) { if (!file.isDirectory()) continue; const datas = await fs.readdir(`${dataDir}/${file.name}`, { withFileTypes: true }); - const pdoc = await problem.get(domainId, `P${file.name}`, undefined, true); + const pdoc = await ProblemModel.get(domainId, `P${file.name}`, undefined, true); if (!pdoc) continue; report({ message: `Syncing testdata for ${file.name}` }); for (const data of datas) { if (data.isDirectory()) continue; const filename = nameMap[data.name] || data; - await problem.addTestdata(domainId, pdoc.docId, filename, `${dataDir}/${file.name}/${data.name}`); + await ProblemModel.addTestdata(domainId, pdoc.docId, filename, `${dataDir}/${file.name}/${data.name}`); } - await problem.addTestdata(domainId, pdoc.docId, 'config.yaml', Buffer.from(pdoc.config as string)); + await ProblemModel.addTestdata(domainId, pdoc.docId, 'config.yaml', Buffer.from(pdoc.config as string)); } return true; } diff --git a/packages/migrate/scripts/syzoj.ts b/packages/migrate/scripts/syzoj.ts index 034239cf6..6b1dd4e4d 100644 --- a/packages/migrate/scripts/syzoj.ts +++ b/packages/migrate/scripts/syzoj.ts @@ -1,23 +1,10 @@ /* eslint-disable no-await-in-loop */ -import fs from 'fs-extra'; -import yaml from 'js-yaml'; import mariadb from 'mariadb'; -import { ObjectID } from 'mongodb'; -import { STATUS } from '@hydrooj/utils/lib/status'; -import { noop, Time } from '@hydrooj/utils/lib/utils'; -import { Schema } from 'hydrooj'; -import { NotFoundError } from 'hydrooj/src/error'; -import { postJudge } from 'hydrooj/src/handler/judge'; -import { DiscussionDoc, DiscussionReplyDoc, RecordDoc } from 'hydrooj/src/interface'; -import { buildContent } from 'hydrooj/src/lib/content'; -import { PERM } from 'hydrooj/src/model/builtin'; -import * as contest from 'hydrooj/src/model/contest'; -import * as document from 'hydrooj/src/model/document'; -import domain from 'hydrooj/src/model/domain'; -import problem from 'hydrooj/src/model/problem'; -import record from 'hydrooj/src/model/record'; -import * as system from 'hydrooj/src/model/system'; -import user from 'hydrooj/src/model/user'; +import { + buildContent, ContestModel, DiscussionDoc, DiscussionReplyDoc, DocumentModel, DomainModel, + fs, noop, NotFoundError, ObjectID, PERM, postJudge, ProblemModel, RecordDoc, RecordModel, + Schema, STATUS, SystemModel, Time, UserModel, yaml, +} from 'hydrooj'; const contentTypeMap = { noi: 'oi', @@ -99,7 +86,7 @@ export async function run({ const query = (q: string) => new Promise((res, rej) => { src.query(q).then((r) => res(r)).catch((e) => rej(e)); }); - const target = await domain.get(domainId); + const target = await DomainModel.get(domainId); if (!target) throw new NotFoundError(domainId); report({ message: 'Connected to database' }); /* @@ -124,24 +111,24 @@ export async function run({ const superAdmin = []; const udocs = await query('SELECT * FROM `user`'); report({ message: udocs.map((u) => u.username.toLowerCase()) }); - const precheck = await user.getMulti({ unameLower: { $in: udocs.map((u) => u.username.toLowerCase()) } }).toArray(); + const precheck = await UserModel.getMulti({ unameLower: { $in: udocs.map((u) => u.username.toLowerCase()) } }).toArray(); if (precheck.length) throw new Error(`Conflict username: ${precheck.map((u) => u.unameLower).join(', ')}`); for (const udoc of udocs) { if (randomMail) delete udoc.email; - let current = await user.getByEmail(domainId, udoc.email || `${udoc.username}@syzoj.local`); - if (!current) current = await user.getByUname(domainId, udoc.username); + let current = await UserModel.getByEmail(domainId, udoc.email || `${udoc.username}@syzoj.local`); + if (!current) current = await UserModel.getByUname(domainId, udoc.username); if (current) { report({ message: `duplicate user with email ${udoc.email}: ${current.uname},${udoc.username}` }); uidMap[udoc.id] = current._id; } else { - const uid = await user.create( + const uid = await UserModel.create( udoc.email || `${udoc.username}@syzoj.local`, udoc.username, '', - null, udoc.ip, system.get('default.priv'), + null, udoc.ip, SystemModel.get('default.priv'), ); - if (udoc.is_admin) await user.setSuperAdmin(uid); + if (udoc.is_admin) await UserModel.setSuperAdmin(uid); superAdmin.push(uid); uidMap[udoc.id] = uid; - await user.setById(uid, { + await UserModel.setById(uid, { regat: new Date(udoc.register_time * 1000), hash: udoc.password, salt: udoc.password, @@ -149,7 +136,7 @@ export async function run({ bio: udoc.information || '', gender: sexMap[udoc.sex] || 3, }); - await domain.setUserInDomain(domainId, uid, { + await DomainModel.setUserInDomain(domainId, uid, { displayName: udoc.nickname || '', nSubmit: udoc.submit_num, nAccept: udoc.ac_num, @@ -158,19 +145,19 @@ export async function run({ } // I think manage_problem_tag is a useless role - await domain.addRole(domainId, 'manage_problem', + await DomainModel.addRole(domainId, 'manage_problem', PERM.PERM_DEFAULT | PERM.PERM_CREATE_PROBLEM | PERM.PERM_EDIT_PROBLEM | PERM.PERM_VIEW_PROBLEM_HIDDEN | PERM.PERM_READ_PROBLEM_DATA | PERM.PERM_EDIT_PROBLEM_SOLUTION | PERM.PERM_DELETE_PROBLEM_SOLUTION | PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY); - await domain.addRole(domainId, 'manage_user', PERM.PERM_DEFAULT | PERM.PERM_EDIT_DOMAIN); + await DomainModel.addRole(domainId, 'manage_user', PERM.PERM_DEFAULT | PERM.PERM_EDIT_DOMAIN); const privileges = await query('SELECT user_id,group_concat(privilege) as privilege FROM `user_privilege` group by user_id'); for (const privilege of privileges) { if (!superAdmin.includes(privilege.user_id)) { if (privilege.privilege.split(',').includes('manage_problem') && privilege.privilege.split(',').includes('manage_user')) { - await domain.setUserRole(domainId, uidMap[privilege.user_id], 'root'); + await DomainModel.setUserRole(domainId, uidMap[privilege.user_id], 'root'); } else if (privilege.privilege.split(',').includes('manage_problem')) { - await domain.setUserRole(domainId, uidMap[privilege.user_id], 'manage_problem'); + await DomainModel.setUserRole(domainId, uidMap[privilege.user_id], 'manage_problem'); } else if (privilege.privilege.split(',').includes('manage_user')) { - await domain.setUserRole(domainId, uidMap[privilege.user_id], 'manage_user'); + await DomainModel.setUserRole(domainId, uidMap[privilege.user_id], 'manage_user'); } } } @@ -215,7 +202,7 @@ export async function run({ const pdocs = await query(`SELECT * FROM \`problem\` LIMIT ${pageId * step}, ${step}`); for (const pdoc of pdocs) { if (rerun) { - const opdoc = await problem.get(domainId, `P${pdoc.id}`); + const opdoc = await ProblemModel.get(domainId, `P${pdoc.id}`); if (opdoc) pidMap[pdoc.id] = opdoc.docId; } if (!pidMap[pdoc.id]) { @@ -232,13 +219,13 @@ export async function run({ else problemAdditionalFile[`P${pdoc.id}`].push({ fromPid: pid, filename }); content = content.replace(origialPath, `file://${filename}`); } - const pid = await problem.add(domainId, `P${pdoc.id}`, pdoc.title, content, uidMap[pdoc.user_id] || 1); + const pid = await ProblemModel.add(domainId, `P${pdoc.id}`, pdoc.title, content, uidMap[pdoc.user_id] || 1); pidMap[pdoc.id] = pid; } const tags = await query(`SELECT * FROM \`problem_tag_map\` WHERE \`problem_id\` = ${pdoc.id}`); const tagList = []; for (const tag of tags) tagList.push(tagMap[tag.tag_id]); - await problem.edit(domainId, pidMap[pdoc.id], { + await ProblemModel.edit(domainId, pidMap[pdoc.id], { nAccept: pdoc.ac_num || 0, nSubmit: pdoc.submit_num || 0, hidden: pdoc.is_public !== 1, @@ -250,7 +237,7 @@ export async function run({ const additionalFile = await query(`SELECT * FROM \`file\` WHERE \`id\` = ${pdoc.additional_file_id}`); if (additionalFile.length) { const [afdoc] = additionalFile; - await problem.addAdditionalFile(domainId, pdoc.docId, + await ProblemModel.addAdditionalFile(domainId, pdoc.docId, `additional_file_${pdoc.additional_file_id}.zip`, `${dataDir}/additional_file/${afdoc.md5}`); } } @@ -282,7 +269,7 @@ export async function run({ const pdocs = tdoc.problems.split('|').map((i) => i.trim()); const pids = pdocs.map((i) => pidMap[i]).filter((i) => i); const admin = uidMap[tdoc.holder_id] || uidMap[tdoc.admins.split('|')[0]]; - const tid = await contest.add( + const tid = await ContestModel.add( domainId, tdoc.title, `${tdoc.subtitle ? `#### ${tdoc.subtitle}\n` : ''}${tdoc.information || 'No Description'}`, admin, contentTypeMap[tdoc.type], new Date(tdoc.start_time * 1000), new Date(tdoc.end_time * 1000), pids, ratedTids.includes(tdoc.id), { maintainer: tdoc.admins.split('|').map((i) => uidMap[i]) }, @@ -359,9 +346,9 @@ export async function run({ } if (rdoc.type) { data.contest = new ObjectID(tidMap[rdoc.type_info]); - await contest.attend(domainId, data.contest, uidMap[rdoc.user_id]).catch(noop); + await ContestModel.attend(domainId, data.contest, uidMap[rdoc.user_id]).catch(noop); } - await record.coll.insertOne(data); + await RecordModel.coll.insertOne(data); await postJudge(data).catch((err) => report({ message: err.message })); } } @@ -385,7 +372,7 @@ export async function run({ for (const ddoc of ddocs) { const data: Partial = { _id: Time.getObjectID(new Date(ddoc.public_time * 1000), false), - docType: document.TYPE_DISCUSSION, + docType: DocumentModel.TYPE_DISCUSSION, docId: Time.getObjectID(new Date(ddoc.public_time * 1000), false), owner: uidMap[ddoc.user_id] || 0, title: ddoc.title, @@ -397,11 +384,11 @@ export async function run({ lock: ddoc.allow_comment === 0, pin: ddoc.is_notice === 1, highlight: ddoc.is_notice === 1, - parentType: ddoc.problem_id ? document.TYPE_PROBLEM : document.TYPE_DISCUSSION_NODE, + parentType: ddoc.problem_id ? DocumentModel.TYPE_PROBLEM : DocumentModel.TYPE_DISCUSSION_NODE, parentId: pidMap[ddoc.problem_id] || 'Hydro', ip: '127.0.0.1', }; - await document.coll.insertOne(data); + await DocumentModel.coll.insertOne(data); didMap[ddoc.id] = data._id; } @@ -418,14 +405,14 @@ export async function run({ _id: Time.getObjectID(new Date(drdoc.public_time * 1000), false), domainId, docId: Time.getObjectID(new Date(drdoc.public_time * 1000), false), - docType: document.TYPE_DISCUSSION_REPLY, + docType: DocumentModel.TYPE_DISCUSSION_REPLY, content: drdoc.content, owner: uidMap[drdoc.user_id], - parentType: document.TYPE_DISCUSSION, + parentType: DocumentModel.TYPE_DISCUSSION, parentId: didMap[drdoc.article_id], ip: '127.0.0.1', }; - await document.coll.insertOne(data); + await DocumentModel.coll.insertOne(data); } report({ message: 'article finished' }); @@ -437,21 +424,21 @@ export async function run({ for (const file of files) { if (!file.isDirectory()) continue; const datas = await fs.readdir(`${dataDir}/testdata/${file.name}`, { withFileTypes: true }); - const pdoc = await problem.get(domainId, `P${file.name}`, undefined, true); + const pdoc = await ProblemModel.get(domainId, `P${file.name}`, undefined, true); if (!pdoc) continue; report({ message: `Syncing testdata for ${file.name}` }); for (const data of datas) { if (data.isDirectory()) continue; - await problem.addTestdata(domainId, pdoc.docId, data.name, `${dataDir}/testdata/${file.name}/${data.name}`); + await ProblemModel.addTestdata(domainId, pdoc.docId, data.name, `${dataDir}/testdata/${file.name}/${data.name}`); if (data.name.startsWith('spj_')) { report({ message: `Syncing spj for ${file.name}` }); - await problem.addTestdata(domainId, pdoc.docId, + await ProblemModel.addTestdata(domainId, pdoc.docId, `spj.${langMap[data.name.split('spj_')[1].split('.')[0]]}`, `${dataDir}/testdata/${file.name}/${data.name}`); pdoc.config += `\nchecker_type: syzoj\nchecker: spj.${langMap[data.name.split('spj_')[1].split('.')[0]]}`; } } if (!(datas.find((i) => i.name === 'data.yml'))) { - await problem.addTestdata(domainId, pdoc.docId, 'config.yaml', Buffer.from(configMap[`P${file.name}`])); + await ProblemModel.addTestdata(domainId, pdoc.docId, 'config.yaml', Buffer.from(configMap[`P${file.name}`])); } else { report({ message: `Transfering data.yml for ${file.name}` }); const config = yaml.load(configMap[`P${file.name}`]) as any; @@ -459,7 +446,7 @@ export async function run({ if (syzojConfig.specialJudge) { report({ message: `Syncing spj config for ${file.name}` }); config.checker_type = 'syzoj'; - await problem.addTestdata(domainId, pdoc.docId, + await ProblemModel.addTestdata(domainId, pdoc.docId, `spj.${langMap[syzojConfig.specialJudge.language]}`, `${dataDir}/testdata/${file.name}/${syzojConfig.specialJudge.fileName}`); config.checker = `spj.${langMap[syzojConfig.specialJudge.language]}`; } @@ -476,7 +463,7 @@ export async function run({ } if (syzojConfig.extraSourceFiles?.length === 1) { for (const { name: sourceName, dest } of syzojConfig.extraSourceFiles[0].files) { - await problem.addTestdata(domainId, pdoc.docId, dest, + await ProblemModel.addTestdata(domainId, pdoc.docId, dest, `${dataDir}/testdata/${file.name}/${sourceName}`); } config.user_extra_files = syzojConfig.extraSourceFiles[0].files.map((x) => x.dest); @@ -490,17 +477,17 @@ export async function run({ if (syzojConfig.interactor) { report({ message: `Syncing interactor config for ${file.name}` }); config.type = 'interactive'; - await problem.addTestdata(domainId, pdoc.docId, + await ProblemModel.addTestdata(domainId, pdoc.docId, `spj.${langMap[syzojConfig.interactor.language]}`, `${dataDir}/testdata/${file.name}/${syzojConfig.interactor.fileName}`); config.interactor = `spj.${langMap[syzojConfig.interactor.language]}`; } - await problem.addTestdata(domainId, pdoc.docId, 'config.yaml', Buffer.from(yaml.dump(config))); + await ProblemModel.addTestdata(domainId, pdoc.docId, 'config.yaml', Buffer.from(yaml.dump(config))); } if (problemAdditionalFile[`P${file.name}`]) { report({ message: `Syncing additional_file for ${file.name}` }); for (const data of problemAdditionalFile[`P${file.name}`]) { if (!fs.existsSync(`${dataDir}/testdata/${data.fromPid}/${decodeURIComponent(data.filename)}`)) continue; - await problem.addAdditionalFile(domainId, pdoc.docId, data.filename, + await ProblemModel.addAdditionalFile(domainId, pdoc.docId, data.filename, `${dataDir}/testdata/${data.fromPid}/${decodeURIComponent(data.filename)}`); } } diff --git a/packages/migrate/scripts/vijos.ts b/packages/migrate/scripts/vijos.ts index 98cb98cf9..af929459e 100644 --- a/packages/migrate/scripts/vijos.ts +++ b/packages/migrate/scripts/vijos.ts @@ -2,12 +2,11 @@ /* eslint-disable no-await-in-loop */ import mongodb, { Cursor, Db } from 'mongodb'; import { - DiscussionTailReplyDoc, MessageDoc, + DiscussionModel, DiscussionTailReplyDoc, MessageDoc, RecordDoc, Schema, TestCase, TrainingNode, } from 'hydrooj'; const dst = global.Hydro.service.db; -const { discussion } = global.Hydro.model; const map = {}; const pid = (id) => { @@ -300,11 +299,11 @@ async function discussionNode(src: Db, report: Function) { const nodes = item[1]; for (const node of nodes || []) { if (node.pic) { - t.push(discussion.addNode( + t.push(DiscussionModel.addNode( doc.domain_id, node.name, category, { pic: node.pic }, )); } else { - t.push(discussion.addNode(doc.domain_id, node.name, category, {})); + t.push(DiscussionModel.addNode(doc.domain_id, node.name, category, {})); } } } diff --git a/packages/onlyoffice/handler.ts b/packages/onlyoffice/handler.ts index db05dfe4a..412fd0368 100644 --- a/packages/onlyoffice/handler.ts +++ b/packages/onlyoffice/handler.ts @@ -1,9 +1,8 @@ -import * as system from 'hydrooj/src/model/system'; -import { UiContextBase } from 'hydrooj/src/service/layers/base'; +import { SystemModel, UiContextBase } from 'hydrooj'; -declare module 'hydrooj/src/service/layers/base' { +declare module 'hydrooj' { interface UiContextBase { onlyofficeApi?: string; } } -UiContextBase.onlyofficeApi = system.get('onlyoffice.api'); +UiContextBase.onlyofficeApi = SystemModel.get('onlyoffice.api'); diff --git a/packages/prom-client/handler.ts b/packages/prom-client/handler.ts index bc5f0ab52..193a322ec 100644 --- a/packages/prom-client/handler.ts +++ b/packages/prom-client/handler.ts @@ -1,15 +1,13 @@ import { AggregatorRegistry, metric } from 'prom-client'; -import * as system from 'hydrooj/src/model/system'; -import * as bus from 'hydrooj/src/service/bus'; -import { Handler } from 'hydrooj/src/service/server'; +import { Context, Handler, SystemModel } from 'hydrooj'; import { registry } from './metrics'; -declare module 'hydrooj/src/service/bus' { +declare module 'hydrooj' { interface EventMap { metrics: (id: string, metrics: any) => void; } } -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface SystemKeys { 'prom-client.name': string; 'prom-client.password': string; @@ -30,7 +28,7 @@ class MetricsHandler extends Handler { this.response.addHeader('WWW-Authenticate', 'Basic'); return; } - const [name, password] = system.getMany(['prom-client.name', 'prom-client.password']); + const [name, password] = SystemModel.getMany(['prom-client.name', 'prom-client.password']); const key = this.request.headers.authorization?.split('Basic ')?.[1]; if (!key || key !== Buffer.from(`${name}:${password}`).toString('base64')) { this.response.status = 403; @@ -42,10 +40,10 @@ class MetricsHandler extends Handler { } } -export function apply(ctx) { +export function apply(ctx: Context) { ctx.on('metrics', (id, metrics) => { instances[id] = metrics; }); ctx.setInterval(async () => { - bus.broadcast('metrics', process.env.NODE_APP_INSTANCE!, await registry.getMetricsAsJSON()); - }, 5000 * (+system.get('prom-client.collect_rate') || 1)); + ctx.broadcast('metrics', process.env.NODE_APP_INSTANCE!, await registry.getMetricsAsJSON()); + }, 5000 * (+SystemModel.get('prom-client.collect_rate') || 1)); ctx.Route('metrics', '/metrics', MetricsHandler); } diff --git a/packages/prom-client/metrics.ts b/packages/prom-client/metrics.ts index 0076e00b0..f89473e6d 100644 --- a/packages/prom-client/metrics.ts +++ b/packages/prom-client/metrics.ts @@ -1,8 +1,8 @@ import { collectDefaultMetrics, Counter, Gauge, Metric, Registry, } from 'prom-client'; +import { db } from 'hydrooj'; import * as bus from 'hydrooj/src/service/bus'; -import * as db from 'hydrooj/src/service/db'; export const registry = new Registry(); registry.setDefaultLabels({ instanceid: process.env.NODE_APP_INSTANCE }); diff --git a/packages/recaptcha/handler.ts b/packages/recaptcha/handler.ts deleted file mode 100644 index a2dde1eda..000000000 --- a/packages/recaptcha/handler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import superagent from 'superagent'; -import { ForbiddenError, ValidationError } from 'hydrooj/src/error'; -import { PRIV } from 'hydrooj/src/model/builtin'; -import * as system from 'hydrooj/src/model/system'; -import * as bus from 'hydrooj/src/service/bus'; - -bus.on('handler/before/UserRegister', async (thisArg) => { - if (!system.get('recaptcha.key')) return; - if (thisArg.request.method !== 'post') { - thisArg.UiContext.recaptchaKey = system.get('recaptcha.key'); - return; - } - if (thisArg.user.hasPriv(PRIV.PRIV_UNLIMITED_ACCESS)) return; - if (!thisArg.args.captcha) throw new ValidationError('captcha'); - const response = await superagent.post('https://recaptcha.net/recaptcha/api/siteverify') - .field('secret', system.get('recaptcha.secret')) - .field('response', thisArg.args.captcha) - .field('remoteip', thisArg.request.ip); - if (!response.body.success) throw new ForbiddenError('captcha fail'); -}); - -bus.on('handler/after/UserRegister', async (thisArg) => { - thisArg.response.body.captcha = `\ - - - `; -}); diff --git a/packages/recaptcha/index.ts b/packages/recaptcha/index.ts new file mode 100644 index 000000000..04db12dcc --- /dev/null +++ b/packages/recaptcha/index.ts @@ -0,0 +1,27 @@ +import { + Context, ForbiddenError, PRIV, superagent, SystemModel, ValidationError, +} from 'hydrooj'; + +export function apply(ctx: Context) { + ctx.on('handler/before/UserRegister', async (thisArg) => { + if (!SystemModel.get('recaptcha.key')) return; + if (thisArg.request.method !== 'post') { + thisArg.UiContext.recaptchaKey = SystemModel.get('recaptcha.key'); + return; + } + if (thisArg.user.hasPriv(PRIV.PRIV_UNLIMITED_ACCESS)) return; + if (!thisArg.args.captcha) throw new ValidationError('captcha'); + const response = await superagent.post('https://recaptcha.net/recaptcha/api/siteverify') + .field('secret', SystemModel.get('recaptcha.secret')) + .field('response', thisArg.args.captcha) + .field('remoteip', thisArg.request.ip); + if (!response.body.success) throw new ForbiddenError('captcha fail'); + }); + + ctx.on('handler/after/UserRegister', async (thisArg) => { + thisArg.response.body.captcha = `\ + + +`; + }); +} diff --git a/packages/recaptcha/package.json b/packages/recaptcha/package.json index 568e61187..4ab2518dd 100644 --- a/packages/recaptcha/package.json +++ b/packages/recaptcha/package.json @@ -6,8 +6,5 @@ "repository": "https://github.com/hydro-dev/Hydro.git", "author": "undefined", "license": "AGPL-3.0-or-later", - "preferUnplugged": true, - "dependencies": { - "superagent": "^8.0.0" - } + "preferUnplugged": true } diff --git a/packages/sonic/index.ts b/packages/sonic/index.ts index a0815a1ff..b0b94e59b 100644 --- a/packages/sonic/index.ts +++ b/packages/sonic/index.ts @@ -1,56 +1,57 @@ -import { Schema } from 'hydrooj'; -import { Logger } from 'hydrooj/src/logger'; -import DomainModel from 'hydrooj/src/model/domain'; -import * as system from 'hydrooj/src/model/system'; -import { iterateAllProblem, iterateAllProblemInDomain } from 'hydrooj/src/pipelineUtils'; -import sonic from './service'; +import { + Context, DomainModel, iterateAllProblem, iterateAllProblemInDomain, + Logger, Schema, SystemModel, +} from 'hydrooj'; +import * as SonicService from './service'; const logger = new Logger('sonic'); -global.Hydro.lib.problemSearch = async (domainId, query, opts) => { - const limit = opts?.limit || system.get('pagination.problem'); - const ids = await sonic.query('problem', `${domainId}@title`, query, { limit }); - if (limit - ids.length > 0) ids.push(...await sonic.query('problem', `${domainId}@content`, query, { limit: limit - ids.length })); - return { - countRelation: ids.length >= limit ? 'gte' : 'eq', - total: ids.length, - hits: ids, - }; -}; +export function apply(ctx: Context) { + ctx.plugin(SonicService); -async function run({ domainId }, report) { - if (domainId) await sonic.flushb('problem', domainId); - else await sonic.flushc('problem'); - let i = 0; - const cb = async (pdoc) => { - i++; - if (!(i % 1000)) report({ message: `${i} problems indexed` }); - const union = await DomainModel.searchUnion({ union: pdoc.domainId, problem: true }); - const tasks = []; - for (const did of [pdoc.domainId, ...union.map((j) => j._id)]) { - tasks.push( - pdoc.title && sonic.push( - 'problem', `${did}@title`, `${pdoc.domainId}/${pdoc.docId}`, - `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag.join(' ')}`, - ), - pdoc.content.toString() && sonic.push('problem', `${did}@content`, `${pdoc.domainId}/${pdoc.docId}`, pdoc.content.toString()), - ); - } - await Promise.all(tasks).catch((e) => console.log(`${pdoc.domainId}/${pdoc.docId}`, e)); + global.Hydro.lib.problemSearch = async (domainId, query, opts) => { + const limit = opts?.limit || SystemModel.get('pagination.problem'); + const ids = await ctx.sonic.query('problem', `${domainId}@title`, query, { limit }); + if (limit - ids.length > 0) ids.push(...await ctx.sonic.query('problem', `${domainId}@content`, query, { limit: limit - ids.length })); + return { + countRelation: ids.length >= limit ? 'gte' : 'eq', + total: ids.length, + hits: ids, + }; }; - if (domainId) await iterateAllProblemInDomain(domainId, ['title', 'content'], cb); - else await iterateAllProblem(['title', 'content', 'tag'], cb); - return true; -} -export function apply(ctx) { + async function run({ domainId }, report) { + if (domainId) await ctx.sonic.flushb('problem', domainId); + else await ctx.sonic.flushc('problem'); + let i = 0; + const cb = async (pdoc) => { + i++; + if (!(i % 1000)) report({ message: `${i} problems indexed` }); + const union = await DomainModel.searchUnion({ union: pdoc.domainId, problem: true }); + const tasks = []; + for (const did of [pdoc.domainId, ...union.map((j) => j._id)]) { + tasks.push( + pdoc.title && ctx.sonic.push( + 'problem', `${did}@title`, `${pdoc.domainId}/${pdoc.docId}`, + `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag.join(' ')}`, + ), + pdoc.content.toString() && ctx.sonic.push('problem', `${did}@content`, `${pdoc.domainId}/${pdoc.docId}`, pdoc.content.toString()), + ); + } + await Promise.all(tasks).catch((e) => console.log(`${pdoc.domainId}/${pdoc.docId}`, e)); + }; + if (domainId) await iterateAllProblemInDomain(domainId, ['title', 'content'], cb); + else await iterateAllProblem(['title', 'content', 'tag'], cb); + return true; + } + ctx.on('problem/add', async (doc, docId) => { const union = await DomainModel.searchUnion({ union: doc.domainId, problem: true }); const tasks = []; for (const domainId of [doc.domainId, ...union.map((i) => i._id)]) { tasks.push( - sonic.push('problem', `${domainId}@title`, `${doc.domainId}/${docId}`, `${doc.pid || ''} ${doc.title} ${doc.tag?.join(' ')}`), - sonic.push('problem', `${domainId}@content`, `${doc.domainId}/${docId}`, doc.content.toString()), + ctx.sonic.push('problem', `${domainId}@title`, `${doc.domainId}/${docId}`, `${doc.pid || ''} ${doc.title} ${doc.tag?.join(' ')}`), + ctx.sonic.push('problem', `${domainId}@content`, `${doc.domainId}/${docId}`, doc.content.toString()), ); } Promise.all(tasks).catch((e) => logger.error(e)); @@ -62,10 +63,10 @@ export function apply(ctx) { const id = `${pdoc.domainId}/${pdoc.docId}`; for (const domainId of [pdoc.domainId, ...union.map((i) => i._id)]) { tasks.push( - sonic.flusho('problem', `${domainId}@title`, id) - .then(() => sonic.push('problem', `${domainId}@title`, id, `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag?.join(' ')}`)), - sonic.flusho('problem', `${domainId}@content`, id) - .then(() => sonic.push('problem', `${domainId}@content`, id, pdoc.content.toString())), + ctx.sonic.flusho('problem', `${domainId}@title`, id) + .then(() => ctx.sonic.push('problem', `${domainId}@title`, id, `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag?.join(' ')}`)), + ctx.sonic.flusho('problem', `${domainId}@content`, id) + .then(() => ctx.sonic.push('problem', `${domainId}@content`, id, pdoc.content.toString())), ); } Promise.all(tasks).catch((e) => logger.error(e)); @@ -77,8 +78,8 @@ export function apply(ctx) { const id = `${domainId}/${docId}`; for (const domain of [domainId, ...union.map((i) => i._id)]) { tasks.push( - sonic.flusho('problem', `${domain}@title`, id), - sonic.flusho('problem', `${domain}@content`, id), + ctx.sonic.flusho('problem', `${domain}@title`, id), + ctx.sonic.flusho('problem', `${domain}@content`, id), ); } await Promise.all(tasks); diff --git a/packages/sonic/model.ts b/packages/sonic/model.ts deleted file mode 100644 index 81a0fd6f2..000000000 --- a/packages/sonic/model.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Logger } from 'hydrooj/src/logger'; -import DomainModel from 'hydrooj/src/model/domain'; -import * as system from 'hydrooj/src/model/system'; -import * as bus from 'hydrooj/src/service/bus'; -import sonic from './service'; - -const logger = new Logger('sonic'); - -bus.on('problem/add', async (doc, docId) => { - const union = await DomainModel.searchUnion({ union: doc.domainId, problem: true }); - const tasks = []; - for (const domainId of [doc.domainId, ...union.map((i) => i._id)]) { - tasks.push( - sonic.push('problem', `${domainId}@title`, `${doc.domainId}/${docId}`, `${doc.pid || ''} ${doc.title} ${doc.tag?.join(' ')}`), - sonic.push('problem', `${domainId}@content`, `${doc.domainId}/${docId}`, doc.content.toString()), - ); - } - Promise.all(tasks).catch((e) => logger.error(e)); -}); - -bus.on('problem/edit', async (pdoc) => { - const union = await DomainModel.searchUnion({ union: pdoc.domainId, problem: true }); - const tasks = []; - const id = `${pdoc.domainId}/${pdoc.docId}`; - for (const domainId of [pdoc.domainId, ...union.map((i) => i._id)]) { - tasks.push( - sonic.flusho('problem', `${domainId}@title`, id) - .then(() => sonic.push('problem', `${domainId}@title`, id, `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag?.join(' ')}`)), - sonic.flusho('problem', `${domainId}@content`, id) - .then(() => sonic.push('problem', `${domainId}@content`, id, pdoc.content.toString())), - ); - } - Promise.all(tasks).catch((e) => logger.error(e)); -}); - -bus.on('problem/del', async (domainId, docId) => { - const union = await DomainModel.searchUnion({ union: domainId, problem: true }); - const tasks = []; - const id = `${domainId}/${docId}`; - for (const domain of [domainId, ...union.map((i) => i._id)]) { - tasks.push( - sonic.flusho('problem', `${domain}@title`, id), - sonic.flusho('problem', `${domain}@content`, id), - ); - } - await Promise.all(tasks); -}); - -global.Hydro.lib.problemSearch = async (domainId, query, opts) => { - const limit = opts?.limit || system.get('pagination.problem'); - const ids = await sonic.query('problem', `${domainId}@title`, query, { limit }); - if (limit - ids.length > 0) ids.push(...await sonic.query('problem', `${domainId}@content`, query, { limit: limit - ids.length })); - return { - countRelation: ids.length >= limit ? 'gte' : 'eq', - total: ids.length, - hits: ids, - }; -}; diff --git a/packages/sonic/script.ts b/packages/sonic/script.ts deleted file mode 100644 index 3013b6207..000000000 --- a/packages/sonic/script.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Schema } from 'hydrooj'; -import DomainModel from 'hydrooj/src/model/domain'; -import { iterateAllProblem, iterateAllProblemInDomain } from 'hydrooj/src/pipelineUtils'; -import sonic from './service'; - -async function run({ domainId }, report) { - if (domainId) await sonic.flushb('problem', domainId); - else await sonic.flushc('problem'); - let i = 0; - const cb = async (pdoc) => { - i++; - if (!(i % 1000)) report({ message: `${i} problems indexed` }); - const union = await DomainModel.searchUnion({ union: pdoc.domainId, problem: true }); - const tasks = []; - for (const did of [pdoc.domainId, ...union.map((j) => j._id)]) { - tasks.push( - pdoc.title && sonic.push( - 'problem', `${did}@title`, `${pdoc.domainId}/${pdoc.docId}`, - `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag.join(' ')}`, - ), - pdoc.content.toString() && sonic.push('problem', `${did}@content`, `${pdoc.domainId}/${pdoc.docId}`, pdoc.content.toString()), - ); - } - await Promise.all(tasks).catch((e) => console.log(`${pdoc.domainId}/${pdoc.docId}`, e)); - }; - if (domainId) await iterateAllProblemInDomain(domainId, ['title', 'content'], cb); - else await iterateAllProblem(['title', 'content', 'tag'], cb); - return true; -} - -export const apply = (ctx) => ctx.addScript( - 'ensureSonicSearch', 'Sonic problem search re-index', - Schema.object({ - domainId: Schema.string(), - }), - run, -); diff --git a/packages/sonic/service.ts b/packages/sonic/service.ts index 8ba1225ec..32cb9115a 100644 --- a/packages/sonic/service.ts +++ b/packages/sonic/service.ts @@ -2,28 +2,29 @@ import { Ingest, PushOptions, QueryOptions, Search, SuggestOptions, } from 'sonic-channel'; -import { BaseService } from 'hydrooj'; -import { Logger } from 'hydrooj/src/logger'; -import * as system from 'hydrooj/src/model/system'; +import { + Context, Logger, Service, SystemModel, +} from 'hydrooj'; -const logger = new Logger('sonic'); - -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface SystemKeys { 'sonic.host': string; 'sonic.port': number; 'sonic.auth': string; } + interface Context { + sonic: SonicService; + } } +const logger = new Logger('sonic'); + function getHandler(type: string, that: any) { return { connected() { - that.started = true; logger.info(`Sonic Channel succeeded to connect to host (${type}).`); }, disconnected() { - that.started = false; logger.error(`Sonic Channel is now disconnected (${type}).`); }, timeout() { @@ -39,14 +40,13 @@ function getHandler(type: string, that: any) { }; } -class SonicService implements BaseService { +class SonicService extends Service { public search: Search; public ingest: Ingest; - public started = false; public error = ''; async start() { - const [host, port, auth] = system.getMany(['sonic.host', 'sonic.port', 'sonic.auth']); + const [host, port, auth] = SystemModel.getMany(['sonic.host', 'sonic.port', 'sonic.auth']); const cfg = { host: host || '::1', port: port || 1491, @@ -108,11 +108,6 @@ class SonicService implements BaseService { } } -const service = new SonicService(); -global.Hydro.service.sonic = service; -export = service; -declare module 'hydrooj/src/interface' { - interface Service { - sonic: typeof service - } +export function apply() { + Context.service('sonic', SonicService); } diff --git a/packages/ui-default/handler.ts b/packages/ui-default/handler.ts index 1cf2755c1..62a3eaadd 100644 --- a/packages/ui-default/handler.ts +++ b/packages/ui-default/handler.ts @@ -2,26 +2,18 @@ /* eslint-disable camelcase */ import crypto from 'crypto'; import esbuild from 'esbuild'; -import fs from 'fs-extra'; -import { Logger } from 'hydrooj/src/logger'; -import { PERM, PRIV } from 'hydrooj/src/model/builtin'; -import * as contest from 'hydrooj/src/model/contest'; -import problem from 'hydrooj/src/model/problem'; -import * as setting from 'hydrooj/src/model/setting'; -import * as system from 'hydrooj/src/model/system'; -import user from 'hydrooj/src/model/user'; -import { UiContextBase } from 'hydrooj/src/service/layers/base'; -import { Handler } from 'hydrooj/src/service/server'; -import { SystemSettings } from 'hydrooj/src/settings'; +import { + ContestModel, fs, Handler, Logger, ObjectID, PERM, PRIV, ProblemModel, Schema, + SettingModel, SystemModel, SystemSettings, UiContextBase, + UserModel, +} from 'hydrooj'; import { debounce } from 'lodash'; -import { ObjectID } from 'mongodb'; import { tmpdir } from 'os'; import { join, resolve } from 'path'; -import Schema from 'schemastery'; import convert from 'schemastery-jsonschema'; import markdown from './backendlib/markdown'; -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface UI { esbuildPlugins?: esbuild.Plugin[] } @@ -29,8 +21,6 @@ declare module 'hydrooj/src/interface' { 'ui-default.nav_logo_dark': string; 'ui-default.nav_logo_dark_2x': string; } -} -declare module 'hydrooj/src/service/layers/base' { interface UiContextBase { nav_logo_dark: string; nav_logo_dark_2x: string; @@ -70,7 +60,7 @@ export async function buildUI() { if (build.errors.length) console.error(build.errors); if (build.warnings.length) console.warn(build.warnings); const pages = build.outputFiles.map((i) => i.text); - const payload = [`window.LANGS=${JSON.stringify(setting.langs)};`, ...pages]; + const payload = [`window.LANGS=${JSON.stringify(SettingModel.langs)};`, ...pages]; const c = crypto.createHash('sha1'); c.update(JSON.stringify(payload)); @@ -80,7 +70,7 @@ export async function buildUI() { logger.success('UI addons built in %d ms', Date.now() - start); } function updateLogo() { - [UiContextBase.nav_logo_dark, UiContextBase.nav_logo_dark_2x] = system.getMany([ + [UiContextBase.nav_logo_dark, UiContextBase.nav_logo_dark_2x] = SystemModel.getMany([ 'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x', ]); } @@ -89,7 +79,7 @@ class WikiHelpHandler extends Handler { noCheckPermView = true; async get({ domainId }) { - const LANGS = setting.langs; + const LANGS = SettingModel.langs; const languages = {}; for (const key in LANGS) { if (LANGS[key].domain && !LANGS[key].domain.includes(domainId)) continue; @@ -105,9 +95,9 @@ class WikiAboutHandler extends Handler { noCheckPermView = true; async get() { - let raw = system.get('ui-default.about') || ''; + let raw = SystemModel.get('ui-default.about') || ''; // TODO template engine - raw = raw.replace(/{{ name }}/g, this.domain.ui?.name || system.get('server.name')).trim(); + raw = raw.replace(/{{ name }}/g, this.domain.ui?.name || SystemModel.get('server.name')).trim(); const lines = raw.split('\n'); const sections = []; for (const line of lines) { @@ -130,7 +120,7 @@ class SetThemeHandler extends Handler { async get({ theme }) { this.checkPriv(PRIV.PRIV_USER_PROFILE); - await user.setById(this.user._id, { theme }); + await UserModel.setById(this.user._id, { theme }); this.back(); } } @@ -165,7 +155,7 @@ class UiConstantsHandler extends ResourceHandler { class LanguageHandler extends ResourceHandler { async all({ lang }) { - if (!global.Hydro.locales[lang]) lang = system.get('server.language'); + if (!global.Hydro.locales[lang]) lang = SystemModel.get('server.language'); this.response.body = `window.LOCALES=${JSON.stringify(global.Hydro.locales[lang])};`; this.response.type = 'application/javascript'; } @@ -181,34 +171,34 @@ class SystemConfigSchemaHandler extends Handler { class RichMediaHandler extends Handler { async renderUser(domainId, payload) { let d = payload.domainId || domainId; - const cur = payload.domainId ? await user.getById(payload.domainId, this.user._id) : this.user; + const cur = payload.domainId ? await UserModel.getById(payload.domainId, this.user._id) : this.user; if (!cur.hasPerm(PERM.PERM_VIEW)) d = domainId; - const udoc = Number.isNaN(+payload.id) ? await user.getByUname(d, payload.id) : await user.getById(d, +payload.id); + const udoc = Number.isNaN(+payload.id) ? await UserModel.getByUname(d, payload.id) : await UserModel.getById(d, +payload.id); return await this.renderHTML('partials/user.html', { udoc }); } async renderProblem(domainId, payload) { - const cur = payload.domainId ? await user.getById(payload.domainId, this.user._id) : this.user; + const cur = payload.domainId ? await UserModel.getById(payload.domainId, this.user._id) : this.user; let pdoc = cur.hasPerm(PERM.PERM_VIEW | PERM.PERM_VIEW_PROBLEM) - ? await problem.get(payload.domainId || domainId, payload.id) || problem.default - : problem.default; - if (pdoc.hidden && !cur.own(pdoc) && !cur.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) pdoc = problem.default; + ? await ProblemModel.get(payload.domainId || domainId, payload.id) || ProblemModel.default + : ProblemModel.default; + if (pdoc.hidden && !cur.own(pdoc) && !cur.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) pdoc = ProblemModel.default; return await this.renderHTML('partials/problem.html', { pdoc }); } async renderContest(domainId, payload) { - const cur = payload.domainId ? await user.getById(payload.domainId, this.user._id) : this.user; + const cur = payload.domainId ? await UserModel.getById(payload.domainId, this.user._id) : this.user; const tdoc = cur.hasPerm(PERM.PERM_VIEW | PERM.PERM_VIEW_CONTEST) - ? await contest.get(payload.domainId || domainId, new ObjectID(payload.id)) + ? await ContestModel.get(payload.domainId || domainId, new ObjectID(payload.id)) : null; if (tdoc) return await this.renderHTML('partials/contest.html', { tdoc }); return ''; } async renderHomework(domainId, payload) { - const cur = payload.domainId ? await user.getById(payload.domainId, this.user._id) : this.user; + const cur = payload.domainId ? await UserModel.getById(payload.domainId, this.user._id) : this.user; const tdoc = cur.hasPerm(PERM.PERM_VIEW | PERM.PERM_VIEW_HOMEWORK) - ? await contest.get(payload.domainId || domainId, new ObjectID(payload.id)) + ? await ContestModel.get(payload.domainId || domainId, new ObjectID(payload.id)) : null; if (tdoc) return await this.renderHTML('partials/homework.html', { tdoc }); return ''; diff --git a/packages/vjudge/src/interface.ts b/packages/vjudge/src/interface.ts index 46cc66c45..f8cda18e0 100644 --- a/packages/vjudge/src/interface.ts +++ b/packages/vjudge/src/interface.ts @@ -13,7 +13,7 @@ export interface RemoteAccount { problemLists?: string[]; enableOn?: string[]; } -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface Collections { vjudge: RemoteAccount; } diff --git a/packages/vjudge/src/model.ts b/packages/vjudge/src/model.ts index a62fb3a7d..7701afd44 100644 --- a/packages/vjudge/src/model.ts +++ b/packages/vjudge/src/model.ts @@ -1,14 +1,9 @@ /* eslint-disable no-await-in-loop */ import os from 'os'; -import { sleep } from '@hydrooj/utils/lib/utils'; -import * as Judge from 'hydrooj/src/handler/judge'; -import { Logger } from 'hydrooj/src/logger'; -import { STATUS } from 'hydrooj/src/model/builtin'; -import DomainModel from 'hydrooj/src/model/domain'; -import ProblemModel from 'hydrooj/src/model/problem'; -import TaskModel from 'hydrooj/src/model/task'; -import * as bus from 'hydrooj/src/service/bus'; -import db from 'hydrooj/src/service/db'; +import { + Context, db, DomainModel, JudgeHandler, Logger, + ProblemModel, sleep, STATUS, TaskModel, +} from 'hydrooj'; import { BasicProvider, IBasicProvider, RemoteAccount } from './interface'; import { getDifficulty } from './providers/codeforces'; import providers from './providers/index'; @@ -40,8 +35,8 @@ class Service { } async judge(task) { - const next = (payload) => Judge.next({ ...payload, rid: task.rid }); - const end = (payload) => Judge.end({ ...payload, rid: task.rid }); + const next = (payload) => JudgeHandler.next({ ...payload, rid: task.rid }); + const end = (payload) => JudgeHandler.end({ ...payload, rid: task.rid }); await next({ status: STATUS.STATUS_FETCHED }); try { const rid = await this.api.submitProblem(task.target, task.lang, task.code, task, next, end); @@ -137,7 +132,7 @@ async function loadAccounts() { } } -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface Model { vjudge: VJudgeModel; } @@ -155,4 +150,6 @@ class VJudgeModel { global.Hydro.model.vjudge = VJudgeModel; -bus.on('app/started', loadAccounts); +export function apply(ctx: Context) { + ctx.on('ready', loadAccounts); +} diff --git a/packages/vjudge/src/providers/codeforces.ts b/packages/vjudge/src/providers/codeforces.ts index b257d1125..edfa4f362 100644 --- a/packages/vjudge/src/providers/codeforces.ts +++ b/packages/vjudge/src/providers/codeforces.ts @@ -9,11 +9,9 @@ import PortalPlugin from 'puppeteer-extra-plugin-portal'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import superagent from 'superagent'; import proxy from 'superagent-proxy'; -import { STATUS } from '@hydrooj/utils/lib/status'; -import { sleep } from '@hydrooj/utils/lib/utils'; -import { buildContent } from 'hydrooj/src/lib/content'; -import { Logger } from 'hydrooj/src/logger'; -import * as setting from 'hydrooj/src/model/setting'; +import { + buildContent, Logger, SettingModel, sleep, STATUS, +} from 'hydrooj'; import { IBasicProvider, RemoteAccount } from '../interface'; import { VERDICT } from '../verdict'; @@ -386,7 +384,7 @@ export default class CodeforcesProvider implements IBasicProvider { async submitProblem(id: string, lang: string, code: string, info, next, end) { const programTypeId = lang.includes('codeforces.') ? lang.split('codeforces.')[1] : '54'; - const comment = setting.langs[lang].comment; + const comment = SettingModel.langs[lang].comment; if (comment) { const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`; if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`; diff --git a/packages/vjudge/src/providers/csgoj.ts b/packages/vjudge/src/providers/csgoj.ts index 86fa65c4f..77c83770a 100644 --- a/packages/vjudge/src/providers/csgoj.ts +++ b/packages/vjudge/src/providers/csgoj.ts @@ -3,9 +3,7 @@ import { PassThrough } from 'stream'; import { JSDOM } from 'jsdom'; import * as superagent from 'superagent'; import proxy from 'superagent-proxy'; -import { STATUS } from '@hydrooj/utils/lib/status'; -import { sleep } from '@hydrooj/utils/lib/utils'; -import { Logger } from 'hydrooj/src/logger'; +import { Logger, sleep, STATUS } from 'hydrooj'; import { IBasicProvider, RemoteAccount } from '../interface'; proxy(superagent as any); diff --git a/packages/vjudge/src/providers/index.ts b/packages/vjudge/src/providers/index.ts index cb2e5362e..f25cb0566 100644 --- a/packages/vjudge/src/providers/index.ts +++ b/packages/vjudge/src/providers/index.ts @@ -5,7 +5,7 @@ import poj from './poj'; import spoj from './spoj'; import uoj from './uoj'; -declare module 'hydrooj/src/interface' { +declare module 'hydrooj' { interface HydroGlobal { vjudge: typeof vjudge; } diff --git a/packages/vjudge/src/providers/luogu.ts b/packages/vjudge/src/providers/luogu.ts index a2e8eb6f7..69f76d994 100644 --- a/packages/vjudge/src/providers/luogu.ts +++ b/packages/vjudge/src/providers/luogu.ts @@ -3,10 +3,9 @@ import { JSDOM } from 'jsdom'; import { flattenDeep } from 'lodash'; import superagent from 'superagent'; import proxy from 'superagent-proxy'; -import { STATUS } from '@hydrooj/utils/lib/status'; -import { sleep } from '@hydrooj/utils/lib/utils'; -import { Logger } from 'hydrooj/src/logger'; -import * as setting from 'hydrooj/src/model/setting'; +import { + Logger, SettingModel, sleep, STATUS, +} from 'hydrooj'; import { IBasicProvider, RemoteAccount } from '../interface'; proxy(superagent); @@ -104,7 +103,7 @@ export default class LuoguProvider implements IBasicProvider { async submitProblem(id: string, lang: string, code: string, info, next, end) { let enableO2 = 0; - const comment = setting.langs[lang]?.comment; + const comment = SettingModel.langs[lang]?.comment; if (code.length < 10) { end({ status: STATUS.STATUS_COMPILE_ERROR, message: 'Code too short' }); return null; diff --git a/packages/vjudge/src/providers/poj.ts b/packages/vjudge/src/providers/poj.ts index 51f75d0ae..e1c22440b 100644 --- a/packages/vjudge/src/providers/poj.ts +++ b/packages/vjudge/src/providers/poj.ts @@ -3,12 +3,9 @@ import { PassThrough } from 'stream'; import { JSDOM } from 'jsdom'; import * as superagent from 'superagent'; import proxy from 'superagent-proxy'; -import { STATUS } from '@hydrooj/utils/lib/status'; import { - htmlEncode, parseMemoryMB, parseTimeMS, sleep, -} from '@hydrooj/utils/lib/utils'; -import { Logger } from 'hydrooj/src/logger'; -import * as setting from 'hydrooj/src/model/setting'; + htmlEncode, Logger, parseMemoryMB, parseTimeMS, SettingModel, sleep, STATUS, +} from 'hydrooj'; import { IBasicProvider, RemoteAccount } from '../interface'; import { VERDICT } from '../verdict'; @@ -221,7 +218,7 @@ export default class POJProvider implements IBasicProvider { async submitProblem(id: string, lang: string, code: string, info) { await this.ensureLogin(); const language = lang.includes('poj.') ? lang.split('poj.')[1] : '0'; - const comment = setting.langs[lang].comment; + const comment = SettingModel.langs[lang].comment; if (comment) { const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`; if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`; diff --git a/packages/vjudge/src/providers/spoj.ts b/packages/vjudge/src/providers/spoj.ts index 930ab0751..a07e0b06d 100644 --- a/packages/vjudge/src/providers/spoj.ts +++ b/packages/vjudge/src/providers/spoj.ts @@ -3,10 +3,9 @@ import { PassThrough } from 'stream'; import { JSDOM } from 'jsdom'; import superagent from 'superagent'; import proxy from 'superagent-proxy'; -import { STATUS } from '@hydrooj/utils/lib/status'; -import { parseMemoryMB, sleep } from '@hydrooj/utils/lib/utils'; -import { Logger } from 'hydrooj/src/logger'; -import * as setting from 'hydrooj/src/model/setting'; +import { + Logger, parseMemoryMB, SettingModel, sleep, STATUS, +} from 'hydrooj'; import { IBasicProvider, RemoteAccount } from '../interface'; proxy(superagent); @@ -118,7 +117,7 @@ langs: ${JSON.stringify(langs)}`), } async submitProblem(problemcode: string, lang: string, code: string, info, next, end) { - const comment = setting.langs[lang]?.comment; + const comment = SettingModel.langs[lang]?.comment; if (comment) { const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`; if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`; diff --git a/packages/vjudge/src/providers/uoj.ts b/packages/vjudge/src/providers/uoj.ts index 463e292ed..549d21ed1 100644 --- a/packages/vjudge/src/providers/uoj.ts +++ b/packages/vjudge/src/providers/uoj.ts @@ -3,10 +3,9 @@ import { PassThrough } from 'stream'; import { JSDOM } from 'jsdom'; import * as superagent from 'superagent'; import proxy from 'superagent-proxy'; -import { STATUS } from '@hydrooj/utils/lib/status'; -import { parseMemoryMB, parseTimeMS, sleep } from '@hydrooj/utils/lib/utils'; -import { Logger } from 'hydrooj/src/logger'; -import * as setting from 'hydrooj/src/model/setting'; +import { + Logger, parseMemoryMB, parseTimeMS, SettingModel, sleep, STATUS, +} from 'hydrooj'; import { IBasicProvider, RemoteAccount } from '../interface'; import { VERDICT } from '../verdict'; @@ -165,7 +164,7 @@ export default class UOJProvider implements IBasicProvider { async submitProblem(id: string, lang: string, code: string, info) { let programTypeId = lang.includes('uoj.') ? lang.split('uoj.')[1] : 'C++11'; if (programTypeId === 'Python27') programTypeId = 'Python2.7'; - const comment = setting.langs[lang].comment; + const comment = SettingModel.langs[lang].comment; if (comment) { const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`; if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`; diff --git a/typedoc.json b/typedoc.json deleted file mode 100644 index 37c2c10f1..000000000 --- a/typedoc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "entryPoints": [ - "packages/hydrooj/src/model/blacklist.ts", - "packages/hydrooj/src/model/builtin.ts", - "packages/hydrooj/src/model/contest.ts", - "packages/hydrooj/src/model/discussion.ts", - "packages/hydrooj/src/model/document.ts", - "packages/hydrooj/src/model/domain.ts", - "packages/hydrooj/src/model/message.ts", - "packages/hydrooj/src/model/oauth.ts", - "packages/hydrooj/src/model/opcount.ts", - "packages/hydrooj/src/model/oplog.ts", - "packages/hydrooj/src/model/problem.ts", - "packages/hydrooj/src/model/record.ts", - "packages/hydrooj/src/model/setting.ts", - "packages/hydrooj/src/model/solution.ts", - "packages/hydrooj/src/model/system.ts", - "packages/hydrooj/src/model/task.ts", - "packages/hydrooj/src/model/token.ts", - "packages/hydrooj/src/model/training.ts", - "packages/hydrooj/src/model/user.ts" - ], - "tsconfig": "packages/hydrooj/tsconfig.json", - "out": "docs" -}