mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core: use common plugin-api interface
This commit is contained in:
1 parent
c61c875bee
commit
70a96aecac
54 files changed
+360
-539
No files matched your search
+1
-1
@@ -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: {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -6,13 +6,6 @@
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"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": "*"
|
||||
}
|
||||
|
||||
@@ -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<ProblemDoc>) => {
|
||||
@@ -25,8 +23,8 @@ const processDocument = (doc: Partial<ProblemDoc>) => {
|
||||
};
|
||||
|
||||
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}`,
|
||||
@@ -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' });
|
||||
}
|
||||
@@ -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 <i@undefined.moe>",
|
||||
"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": {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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' {
|
||||
}
|
||||
}
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
if (process.env.NODE_APP_INSTANCE === '0') {
|
||||
bus.once('app/started', () => require('./hosts/builtin').postInit());
|
||||
ctx.once('app/started', () => require('./hosts/builtin').postInit());
|
||||
}
|
||||
}
|
||||
@@ -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 <i@undefined.moe>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -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<C extends Context = Context> extends cordis.Events<C>, EventMap {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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' });
|
||||
}
|
||||
@@ -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<ProblemSearchResponse>;
|
||||
|
||||
export { Context as PluginContext } from './context';
|
||||
|
||||
export interface Lib extends Record<string, any> {
|
||||
difficulty: typeof import('./lib/difficulty'),
|
||||
buildContent: typeof import('./lib/content').buildContent,
|
||||
@@ -725,7 +723,7 @@ export interface HydroGlobal {
|
||||
/** @deprecated */
|
||||
handler: Record<string, Function>;
|
||||
script: Record<string, Script>;
|
||||
service: Service;
|
||||
service: HydroService;
|
||||
lib: Lib;
|
||||
module: { [K in keyof ModuleInterfaces]: Record<string, ModuleInterfaces[K]> };
|
||||
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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -302,7 +302,7 @@ bus.on('app/ready', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
global.Hydro.model.discussion = {
|
||||
global.Hydro.model.DiscussionModel = {
|
||||
typeDisplay,
|
||||
PROJECTION_LIST,
|
||||
PROJECTION_PUBLIC,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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' });
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -6,13 +6,7 @@
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"preferUnplugged": true,
|
||||
"devDependencies": {
|
||||
"@types/superagent": "^4.1.15"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hydrooj": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"superagent": "^8.0.0"
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -6,13 +6,7 @@
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"preferUnplugged": true,
|
||||
"devDependencies": {
|
||||
"@types/superagent": "^4.1.15"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hydrooj": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"superagent": "^8.0.0"
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<string, number> = {};
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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<any[]>((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<DiscussionDoc> = {
|
||||
_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)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, {}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 });
|
||||
|
||||
@@ -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 = `\
|
||||
<script src="https://recaptcha.net/recaptcha/api.js?render=${system.get('recaptcha.key')}"></script>
|
||||
<input type="text" name="captcha" id="_captcha" style="display:none">
|
||||
<input type="submit" id="_submit" style="display:none">`;
|
||||
});
|
||||
@@ -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 = `\
|
||||
<script src="https://recaptcha.net/recaptcha/api.js?render=${SystemModel.get('recaptcha.key')}"></script>
|
||||
<input type="text" name="captcha" id="_captcha" style="display:none">
|
||||
<input type="submit" id="_submit" style="display:none">`;
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+23
-22
@@ -1,16 +1,18 @@
|
||||
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');
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.plugin(SonicService);
|
||||
|
||||
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 }));
|
||||
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,
|
||||
@@ -19,8 +21,8 @@ global.Hydro.lib.problemSearch = async (domainId, query, opts) => {
|
||||
};
|
||||
|
||||
async function run({ domainId }, report) {
|
||||
if (domainId) await sonic.flushb('problem', domainId);
|
||||
else await sonic.flushc('problem');
|
||||
if (domainId) await ctx.sonic.flushb('problem', domainId);
|
||||
else await ctx.sonic.flushc('problem');
|
||||
let i = 0;
|
||||
const cb = async (pdoc) => {
|
||||
i++;
|
||||
@@ -29,11 +31,11 @@ async function run({ domainId }, report) {
|
||||
const tasks = [];
|
||||
for (const did of [pdoc.domainId, ...union.map((j) => j._id)]) {
|
||||
tasks.push(
|
||||
pdoc.title && sonic.push(
|
||||
pdoc.title && ctx.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()),
|
||||
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));
|
||||
@@ -43,14 +45,13 @@ async function run({ domainId }, report) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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,
|
||||
);
|
||||
+13
-18
@@ -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);
|
||||
}
|
||||
@@ -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 '';
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface RemoteAccount {
|
||||
problemLists?: string[];
|
||||
enableOn?: string[];
|
||||
}
|
||||
declare module 'hydrooj/src/interface' {
|
||||
declare module 'hydrooj' {
|
||||
interface Collections {
|
||||
vjudge: RemoteAccount;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
Reference in new issue
Block a user