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
+387
-566
No files matched your search
+1
-1
@@ -9,7 +9,6 @@ const compilerOptionsBase = {
|
|||||||
module: 'commonjs',
|
module: 'commonjs',
|
||||||
esModuleInterop: true,
|
esModuleInterop: true,
|
||||||
moduleResolution: 'node',
|
moduleResolution: 'node',
|
||||||
// declaration: true,
|
|
||||||
sourceMap: false,
|
sourceMap: false,
|
||||||
composite: true,
|
composite: true,
|
||||||
strictBindCallApply: true,
|
strictBindCallApply: true,
|
||||||
@@ -48,6 +47,7 @@ const configFlat = (name) => (name === 'packages/ui-default' ? {
|
|||||||
allowSyntheticDefaultImports: true,
|
allowSyntheticDefaultImports: true,
|
||||||
target: 'es2020',
|
target: 'es2020',
|
||||||
baseUrl: '.',
|
baseUrl: '.',
|
||||||
|
outDir: path.join(baseOutDir, name),
|
||||||
moduleResolution: 'node',
|
moduleResolution: 'node',
|
||||||
types,
|
types,
|
||||||
paths: {
|
paths: {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import yaml from 'js-yaml';
|
import {
|
||||||
import * as bus from 'hydrooj/src/service/bus';
|
Context, db, Handler, post, Types, yaml,
|
||||||
import db from 'hydrooj/src/service/db';
|
} from 'hydrooj';
|
||||||
import { Handler, post, Types } from 'hydrooj/src/service/server';
|
|
||||||
|
|
||||||
function decrypt(encrypted: string) {
|
function decrypt(encrypted: string) {
|
||||||
if (!encrypted) throw new Error();
|
if (!encrypted) throw new Error();
|
||||||
@@ -13,7 +12,7 @@ function decrypt(encrypted: string) {
|
|||||||
return decrypted;
|
return decrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'hydrooj/src/interface' {
|
declare module 'hydrooj' {
|
||||||
interface Collections {
|
interface Collections {
|
||||||
dataReport: any;
|
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);
|
ctx.Route('data_report', '/center/report', DataReportHandler);
|
||||||
}
|
}
|
||||||
@@ -6,13 +6,6 @@
|
|||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"dependencies": {
|
|
||||||
"js-yaml": "^4.1.0",
|
|
||||||
"superagent": "^8.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/js-yaml": "^4.0.5"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"hydrooj": "*"
|
"hydrooj": "*"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { Client } from '@elastic/elasticsearch';
|
import { Client } from '@elastic/elasticsearch';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { Schema } from 'hydrooj';
|
import {
|
||||||
import DomainModel from 'hydrooj/src/model/domain';
|
Context, DomainModel, iterateAllProblem, iterateAllProblemInDomain,
|
||||||
import ProblemModel, { ProblemDoc } from 'hydrooj/src/model/problem';
|
ProblemDoc, ProblemModel, Schema, SystemModel,
|
||||||
import * as system from 'hydrooj/src/model/system';
|
} from 'hydrooj';
|
||||||
import { iterateAllProblem, iterateAllProblemInDomain } from 'hydrooj/src/pipelineUtils';
|
|
||||||
import * as bus from 'hydrooj/src/service/bus';
|
|
||||||
|
|
||||||
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 indexOmit = ['_id', 'docType', 'data', 'additional_file', 'config', 'stats', 'assign'];
|
||||||
const processDocument = (doc: Partial<ProblemDoc>) => {
|
const processDocument = (doc: Partial<ProblemDoc>) => {
|
||||||
@@ -25,8 +23,8 @@ const processDocument = (doc: Partial<ProblemDoc>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
global.Hydro.lib.problemSearch = async (domainId, q, opts) => {
|
global.Hydro.lib.problemSearch = async (domainId, q, opts) => {
|
||||||
const allowedSize = system.get('elasic-search.indexSize') || 10000;
|
const allowedSize = SystemModel.get('elasic-search.indexSize') || 10000;
|
||||||
const size = opts?.limit || system.get('pagination.problem');
|
const size = opts?.limit || SystemModel.get('pagination.problem');
|
||||||
const from = Math.min(allowedSize - size, opts?.skip || 0);
|
const from = Math.min(allowedSize - size, opts?.skip || 0);
|
||||||
const union = await DomainModel.getUnion(domainId);
|
const union = await DomainModel.getUnion(domainId);
|
||||||
const domainIds = [domainId, ...(union?.union || [])];
|
const domainIds = [domainId, ...(union?.union || [])];
|
||||||
@@ -77,22 +75,22 @@ async function run({ domainId }, report) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const apply = (ctx) => {
|
export const apply = (ctx: Context) => {
|
||||||
bus.on('problem/add', async (doc, docId) => {
|
ctx.on('problem/add', async (doc, docId) => {
|
||||||
await client.index({
|
await client.index({
|
||||||
index: 'problem',
|
index: 'problem',
|
||||||
id: `${doc.domainId}/${docId}`,
|
id: `${doc.domainId}/${docId}`,
|
||||||
document: processDocument(doc),
|
document: processDocument(doc),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
bus.on('problem/edit', async (pdoc) => {
|
ctx.on('problem/edit', async (pdoc) => {
|
||||||
await client.index({
|
await client.index({
|
||||||
index: 'problem',
|
index: 'problem',
|
||||||
id: `${pdoc.domainId}/${pdoc.docId}`,
|
id: `${pdoc.domainId}/${pdoc.docId}`,
|
||||||
document: processDocument(pdoc),
|
document: processDocument(pdoc),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
bus.on('problem/del', async (domainId, docId) => {
|
ctx.on('problem/del', async (domainId, docId) => {
|
||||||
await client.delete({
|
await client.delete({
|
||||||
index: 'problem',
|
index: 'problem',
|
||||||
id: `${domainId}/${docId}`,
|
id: `${domainId}/${docId}`,
|
||||||
@@ -1,16 +1,11 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import AdmZip from 'adm-zip';
|
|
||||||
import decodeHTML from 'decode-html';
|
import decodeHTML from 'decode-html';
|
||||||
import fs from 'fs-extra';
|
|
||||||
import { filter } from 'lodash';
|
import { filter } from 'lodash';
|
||||||
import xml2js from 'xml2js';
|
import xml2js from 'xml2js';
|
||||||
import type { ContentNode, PluginContext, ProblemConfigFile } from 'hydrooj';
|
import {
|
||||||
import { FileTooLargeError, ValidationError } from 'hydrooj/src/error';
|
AdmZip, buildContent, ContentNode, Context, FileTooLargeError, fs,
|
||||||
import { buildContent } from 'hydrooj/src/lib/content';
|
Handler, PERM, ProblemConfigFile, ProblemModel, SolutionModel, ValidationError,
|
||||||
import { PERM } from 'hydrooj/src/model/builtin';
|
} from 'hydrooj';
|
||||||
import problem from 'hydrooj/src/model/problem';
|
|
||||||
import solution from 'hydrooj/src/model/solution';
|
|
||||||
import { Handler } from 'hydrooj/src/service/server';
|
|
||||||
|
|
||||||
class FpsProblemImportHandler extends Handler {
|
class FpsProblemImportHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
@@ -65,8 +60,8 @@ class FpsProblemImportHandler extends Handler {
|
|||||||
};
|
};
|
||||||
const title = decodeHTML(p.title.join(' '));
|
const title = decodeHTML(p.title.join(' '));
|
||||||
const tags = filter(p.source, (i: string) => i.trim());
|
const tags = filter(p.source, (i: string) => i.trim());
|
||||||
const pid = await problem.add(domainId, null, title, buildContent(content, 'html'), this.user._id, tags);
|
const pid = await ProblemModel.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}`));
|
await ProblemModel.addTestdata(domainId, pid, 'config.yaml', Buffer.from(`time: ${config.time}\nmemory: ${config.memory}`));
|
||||||
if (p.test_output) {
|
if (p.test_output) {
|
||||||
for (let i = 0; i < p.test_input.length; i++) {
|
for (let i = 0; i < p.test_input.length; i++) {
|
||||||
const input = typeof p.test_input[i]?._ === 'string'
|
const input = typeof p.test_input[i]?._ === 'string'
|
||||||
@@ -79,22 +74,22 @@ class FpsProblemImportHandler extends Handler {
|
|||||||
: typeof p.test_output[i] === 'string'
|
: typeof p.test_output[i] === 'string'
|
||||||
? p.test_output[i]
|
? p.test_output[i]
|
||||||
: '';
|
: '';
|
||||||
await problem.addTestdata(domainId, pid, `${i + 1}.in`, Buffer.from(input));
|
await ProblemModel.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}.out`, Buffer.from(output));
|
||||||
}
|
}
|
||||||
} else if (p.test_input) {
|
} else if (p.test_input) {
|
||||||
for (let i = 0; i < p.test_input.length / 2; i++) {
|
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 ProblemModel.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}.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) {
|
if (p.solution) {
|
||||||
let s = '';
|
let s = '';
|
||||||
for (const sol of p.solution) {
|
for (const sol of p.solution) {
|
||||||
s += `**${sol.$.language}** : \n\`\`\`\n${sol._}\n\`\`\`\n`;
|
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;
|
let zip: AdmZip;
|
||||||
try {
|
try {
|
||||||
zip = new AdmZip(this.request.files.file.filepath);
|
zip = new AdmZip(this.request.files.file.filepath);
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
throw new ValidationError('zip', null, e.message);
|
throw new ValidationError('zip', null, err.message);
|
||||||
}
|
}
|
||||||
for (const entry of zip.getEntries()) {
|
for (const entry of zip.getEntries()) {
|
||||||
try {
|
try {
|
||||||
@@ -134,7 +129,7 @@ class FpsProblemImportHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const sideEffect = true;
|
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.Route('problem_import_fps', '/problem/import/fps', FpsProblemImportHandler, PERM.PERM_CREATE_PROBLEM);
|
||||||
ctx.inject('ProblemAdd', 'problem_import_fps', { icon: 'copy', text: 'From FPS File' });
|
ctx.inject('ProblemAdd', 'problem_import_fps', { icon: 'copy', text: 'From FPS File' });
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "@hydrooj/fps-importer",
|
"name": "@hydrooj/fps-importer",
|
||||||
"version": "1.3.4",
|
"version": "1.3.4",
|
||||||
"description": "Import FPS problems",
|
"description": "Import FPS problems",
|
||||||
"main": "package.json",
|
"main": "index.ts",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
@@ -10,8 +10,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "0.5.5",
|
"adm-zip": "0.5.5",
|
||||||
"decode-html": "^2.0.0",
|
"decode-html": "^2.0.0",
|
||||||
"fs-extra": "^10.1.0",
|
|
||||||
"mongodb": "^3.7.3",
|
|
||||||
"xml2js": "^0.4.23"
|
"xml2js": "^0.4.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -3,12 +3,10 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import type { JudgeResultBody } from 'hydrooj';
|
import type { JudgeResultBody } from 'hydrooj';
|
||||||
|
import {
|
||||||
|
Logger, SettingModel, StorageModel, SystemModel, TaskModel,
|
||||||
|
} from 'hydrooj';
|
||||||
import { end, next } from 'hydrooj/src/handler/judge';
|
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 { processTestdata } from '../cases';
|
||||||
import { getConfig } from '../config';
|
import { getConfig } from '../config';
|
||||||
import { FormatError, SystemError } from '../error';
|
import { FormatError, SystemError } from '../error';
|
||||||
@@ -18,11 +16,11 @@ import { JudgeTask } from '../task';
|
|||||||
const logger = new Logger('judge');
|
const logger = new Logger('judge');
|
||||||
|
|
||||||
const session = {
|
const session = {
|
||||||
config: { detail: system.get('hydrojudge.detail') },
|
config: { detail: SystemModel.get('hydrojudge.detail') },
|
||||||
async fetchFile(name: string) {
|
async fetchFile(name: string) {
|
||||||
name = name.split('#')[0];
|
name = name.split('#')[0];
|
||||||
const target = path.join(getConfig('tmp_dir'), name.replace(/\//g, '_'));
|
const target = path.join(getConfig('tmp_dir'), name.replace(/\//g, '_'));
|
||||||
await storage.get(`submission/${name}`, target);
|
await StorageModel.get(`submission/${name}`, target);
|
||||||
return target;
|
return target;
|
||||||
},
|
},
|
||||||
getNext(t: Context) {
|
getNext(t: Context) {
|
||||||
@@ -42,8 +40,8 @@ const session = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
getLang(lang: string, doThrow = true) {
|
getLang(lang: string, doThrow = true) {
|
||||||
if (setting.langs[lang]) return setting.langs[lang];
|
if (SettingModel.langs[lang]) return SettingModel.langs[lang];
|
||||||
if (lang === 'cpp' && setting.langs['cc']) return setting.langs['cc'];
|
if (lang === 'cpp' && SettingModel.langs['cc']) return SettingModel.langs['cc'];
|
||||||
if (doThrow) throw new SystemError('Unsupported language {0}.', [lang]);
|
if (doThrow) throw new SystemError('Unsupported language {0}.', [lang]);
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
@@ -61,7 +59,7 @@ const session = {
|
|||||||
filenames.add(file.name);
|
filenames.add(file.name);
|
||||||
version[file.name] = file.etag + file.lastModified;
|
version[file.name] = file.etag + file.lastModified;
|
||||||
if (etags[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) {
|
for (const name in etags) {
|
||||||
@@ -77,8 +75,8 @@ const session = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function postInit() {
|
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);
|
const handle = (t) => (new JudgeTask(session, t)).handle().catch(logger.error);
|
||||||
task.consume({ type: 'judge' }, handle);
|
TaskModel.consume({ type: 'judge' }, handle);
|
||||||
task.consume({ type: 'judge', priority: { $gt: -50 } }, 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 {
|
interface SystemKeys {
|
||||||
'hydrojudge.cache_dir': string;
|
'hydrojudge.cache_dir': string;
|
||||||
'hydrojudge.tmp_dir': string;
|
'hydrojudge.tmp_dir': string;
|
||||||
@@ -15,6 +15,8 @@ declare module 'hydrooj/src/interface' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.NODE_APP_INSTANCE === '0') {
|
export function apply(ctx: Context) {
|
||||||
bus.once('app/started', () => require('./hosts/builtin').postInit());
|
if (process.env.NODE_APP_INSTANCE === '0') {
|
||||||
|
ctx.once('app/started', () => require('./hosts/builtin').postInit());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "3.17.12",
|
"version": "3.17.12",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "src/loader",
|
"main": "src/plugin-api",
|
||||||
"module": "src/loader",
|
"module": "src/plugin-api",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import * as cordis from 'cordis';
|
import * as cordis from 'cordis';
|
||||||
import Schema from 'schemastery';
|
import Schema from 'schemastery';
|
||||||
|
import { ModuleInterfaces } from './interface';
|
||||||
import { inject } from './lib/ui';
|
import { inject } from './lib/ui';
|
||||||
import { Loader, ModuleInterfaces } from './loader';
|
import { Loader } from './loader';
|
||||||
import { EventMap } from './service/bus';
|
import { EventMap } from './service/bus';
|
||||||
|
|
||||||
export interface Events<C extends Context = Context> extends cordis.Events<C>, EventMap {
|
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),
|
model(pending, fail, ctx),
|
||||||
setting(pending, fail, require('../model/setting')),
|
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);
|
await addon(pending, fail, ctx);
|
||||||
const scriptDir = path.resolve(__dirname, '..', 'script');
|
const scriptDir = path.resolve(__dirname, '..', 'script');
|
||||||
for (const h of await fs.readdir(scriptDir)) {
|
for (const h of await fs.readdir(scriptDir)) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export async function apply(ctx: Context) {
|
|||||||
await modelSystem.runConfig();
|
await modelSystem.runConfig();
|
||||||
const storage = require('../service/storage');
|
const storage = require('../service/storage');
|
||||||
await storage.loadStorageService();
|
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
|
// Make sure everything is ready and then start main entry
|
||||||
if (argv.options.watch) await ctx.loader.reloadPlugin(ctx, 'hydrooj/src/service/watcher', {});
|
if (argv.options.watch) await ctx.loader.reloadPlugin(ctx, 'hydrooj/src/service/watcher', {});
|
||||||
await ctx.root.start();
|
await ctx.root.start();
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import path from 'path';
|
|||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
|
import { Context } from '../context';
|
||||||
import { ValidationError } from '../error';
|
import { ValidationError } from '../error';
|
||||||
import { PluginContext } from '../loader';
|
|
||||||
import { PERM, PRIV } from '../model/builtin';
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
import problem, { ProblemDoc } from '../model/problem';
|
import problem, { ProblemDoc } from '../model/problem';
|
||||||
import { Handler } from '../service/server';
|
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.Route('problem_import_hydro', '/problem/import/hydro', ProblemImportHydroHandler, PERM.PERM_CREATE_PROBLEM);
|
||||||
ctx.inject('ProblemAdd', 'problem_import_hydro', { icon: 'copy', text: 'Import From Hydro' });
|
ctx.inject('ProblemAdd', 'problem_import_hydro', { icon: 'copy', text: 'Import From Hydro' });
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type fs from 'fs';
|
import type fs from 'fs';
|
||||||
import type { Dictionary, NumericDictionary } from 'lodash';
|
import type { Dictionary, NumericDictionary } from 'lodash';
|
||||||
import type { Cursor, ObjectID } from 'mongodb';
|
import type { Cursor, ObjectID } from 'mongodb';
|
||||||
import { Context } from './context';
|
import type { Context } from './context';
|
||||||
import type { ProblemDoc } from './model/problem';
|
import type { ProblemDoc } from './model/problem';
|
||||||
import type { Handler } from './service/server';
|
import type { Handler } from './service/server';
|
||||||
|
|
||||||
@@ -637,7 +637,7 @@ export interface Model {
|
|||||||
blog: typeof import('./model/blog'),
|
blog: typeof import('./model/blog'),
|
||||||
builtin: typeof import('./model/builtin'),
|
builtin: typeof import('./model/builtin'),
|
||||||
contest: typeof import('./model/contest'),
|
contest: typeof import('./model/contest'),
|
||||||
discussion: typeof import('./model/discussion'),
|
DiscussionModel: typeof import('./model/discussion'),
|
||||||
document: typeof import('./model/document'),
|
document: typeof import('./model/document'),
|
||||||
domain: typeof import('./model/domain').default,
|
domain: typeof import('./model/domain').default,
|
||||||
message: typeof import('./model/message').default,
|
message: typeof import('./model/message').default,
|
||||||
@@ -657,7 +657,7 @@ export interface Model {
|
|||||||
rp: typeof import('./script/rating').RpTypes,
|
rp: typeof import('./script/rating').RpTypes,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Service {
|
export interface HydroService {
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
bus: Context,
|
bus: Context,
|
||||||
db: typeof import('./service/db'),
|
db: typeof import('./service/db'),
|
||||||
@@ -683,8 +683,6 @@ export interface ProblemSearchOptions {
|
|||||||
|
|
||||||
export type ProblemSearch = (domainId: string, q: string, options?: ProblemSearchOptions) => Promise<ProblemSearchResponse>;
|
export type ProblemSearch = (domainId: string, q: string, options?: ProblemSearchOptions) => Promise<ProblemSearchResponse>;
|
||||||
|
|
||||||
export { Context as PluginContext } from './context';
|
|
||||||
|
|
||||||
export interface Lib extends Record<string, any> {
|
export interface Lib extends Record<string, any> {
|
||||||
difficulty: typeof import('./lib/difficulty'),
|
difficulty: typeof import('./lib/difficulty'),
|
||||||
buildContent: typeof import('./lib/content').buildContent,
|
buildContent: typeof import('./lib/content').buildContent,
|
||||||
@@ -725,7 +723,7 @@ export interface HydroGlobal {
|
|||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
handler: Record<string, Function>;
|
handler: Record<string, Function>;
|
||||||
script: Record<string, Script>;
|
script: Record<string, Script>;
|
||||||
service: Service;
|
service: HydroService;
|
||||||
lib: Lib;
|
lib: Lib;
|
||||||
module: { [K in keyof ModuleInterfaces]: Record<string, ModuleInterfaces[K]> };
|
module: { [K in keyof ModuleInterfaces]: Record<string, ModuleInterfaces[K]> };
|
||||||
ui: UI;
|
ui: UI;
|
||||||
@@ -742,6 +740,7 @@ declare global {
|
|||||||
addons: string[],
|
addons: string[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var bus: any; // eslint-disable-line
|
||||||
var app: Context; // eslint-disable-line
|
var app: Context; // eslint-disable-line
|
||||||
var Hydro: HydroGlobal; // eslint-disable-line
|
var Hydro: HydroGlobal; // eslint-disable-line
|
||||||
var addons: string[]; // eslint-disable-line
|
var addons: string[]; // eslint-disable-line
|
||||||
|
|||||||
@@ -16,9 +16,6 @@ import { Logger } from './logger';
|
|||||||
import { Context } from './context';
|
import { Context } from './context';
|
||||||
// eslint-disable-next-line import/no-duplicates
|
// eslint-disable-next-line import/no-duplicates
|
||||||
import { unwrapExports } from './utils';
|
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 argv = cac().parse();
|
||||||
const logger = new Logger('loader');
|
const logger = new Logger('loader');
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ bus.on('app/ready', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
global.Hydro.model.discussion = {
|
global.Hydro.model.DiscussionModel = {
|
||||||
typeDisplay,
|
typeDisplay,
|
||||||
PROJECTION_LIST,
|
PROJECTION_LIST,
|
||||||
PROJECTION_PUBLIC,
|
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 { PassThrough } from 'stream';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import * as system from 'hydrooj/src/model/system';
|
import * as system from '../../model/system';
|
||||||
import token from 'hydrooj/src/model/token';
|
import token from '../../model/token';
|
||||||
import type { HydroRequest, HydroResponse } from 'hydrooj/src/service/server';
|
import type { HydroRequest, HydroResponse } from '../server';
|
||||||
|
|
||||||
export interface UiContextBase {
|
export interface UiContextBase {
|
||||||
cdn_prefix: string;
|
cdn_prefix: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import BlackListModel from 'hydrooj/src/model/blacklist';
|
import BlackListModel from '../../model/blacklist';
|
||||||
import DomainModel from 'hydrooj/src/model/domain';
|
import DomainModel from '../../model/domain';
|
||||||
import * as system from 'hydrooj/src/model/system';
|
import * as system from '../../model/system';
|
||||||
import token from 'hydrooj/src/model/token';
|
import token from '../../model/token';
|
||||||
|
|
||||||
export default async (ctx, next) => {
|
export default async (ctx, next) => {
|
||||||
const forceDomain = /^\/d\/([^/]+)\//.exec(ctx.request.path);
|
const forceDomain = /^\/d\/([^/]+)\//.exec(ctx.request.path);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ObjectID } from 'mongodb';
|
import { ObjectID } from 'mongodb';
|
||||||
import avatar from 'hydrooj/src/lib/avatar';
|
import avatar from '../../lib/avatar';
|
||||||
import { PERM } from 'hydrooj/src/model/builtin';
|
import { PERM } from '../../model/builtin';
|
||||||
import * as system from 'hydrooj/src/model/system';
|
import * as system from '../../model/system';
|
||||||
import { User } from 'hydrooj/src/model/user';
|
import { User } from '../../model/user';
|
||||||
import type { KoaContext } from '../server';
|
import type { KoaContext } from '../server';
|
||||||
|
|
||||||
const serializer = (showDisplayName = false) => (k: string, v: any) => {
|
const serializer = (showDisplayName = false) => (k: string, v: any) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { errorMessage } from '@hydrooj/utils/lib/utils';
|
import { errorMessage } from '@hydrooj/utils/lib/utils';
|
||||||
import { SystemError, UserFacingError } from 'hydrooj/src/error';
|
import { SystemError, UserFacingError } from '../../error';
|
||||||
import { PERM } from 'hydrooj/src/model/builtin';
|
import { PERM } from '../../model/builtin';
|
||||||
import { User } from 'hydrooj/src/model/user';
|
import { User } from '../../model/user';
|
||||||
import type { KoaContext } from '../server';
|
import type { KoaContext } from '../server';
|
||||||
|
|
||||||
const serializer = (showDisplayName = false) => (k: string, v: any) => {
|
const serializer = (showDisplayName = false) => (k: string, v: any) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CsrfTokenError, NotFoundError } from 'hydrooj/src/error';
|
import { CsrfTokenError, NotFoundError } from '../../error';
|
||||||
import avatar from 'hydrooj/src/lib/avatar';
|
import avatar from '../../lib/avatar';
|
||||||
import { PERM } from 'hydrooj/src/model/builtin';
|
import { PERM } from '../../model/builtin';
|
||||||
import UserModel from 'hydrooj/src/model/user';
|
import UserModel from '../../model/user';
|
||||||
import type { KoaContext } from '../server';
|
import type { KoaContext } from '../server';
|
||||||
|
|
||||||
export default async (ctx: KoaContext, next) => {
|
export default async (ctx: KoaContext, next) => {
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import AdmZip from 'adm-zip';
|
import {
|
||||||
import fs from 'fs-extra';
|
AdmZip, buildContent, ContentNode, Context, fs, Handler, PERM,
|
||||||
import yaml from 'js-yaml';
|
ProblemConfigFile, ProblemModel, ValidationError, yaml,
|
||||||
import type { ContentNode, PluginContext, ProblemConfigFile } from 'hydrooj';
|
} 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';
|
|
||||||
|
|
||||||
fs.ensureDirSync('/tmp/hydro/import-qduoj');
|
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}`;
|
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;
|
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 = {
|
const config: ProblemConfigFile = {
|
||||||
time: `${pdoc.time_limit}ms`,
|
time: `${pdoc.time_limit}ms`,
|
||||||
memory: `${pdoc.memory_limit}m`,
|
memory: `${pdoc.memory_limit}m`,
|
||||||
@@ -89,11 +84,11 @@ class ImportQduojHandler extends Handler {
|
|||||||
};
|
};
|
||||||
for (const tc of pdoc.test_case_score) {
|
for (const tc of pdoc.test_case_score) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
problem.addTestdata(
|
ProblemModel.addTestdata(
|
||||||
domainId, pid, tc.input_name,
|
domainId, pid, tc.input_name,
|
||||||
path.join(tmp, folder, 'testcase', tc.input_name),
|
path.join(tmp, folder, 'testcase', tc.input_name),
|
||||||
),
|
),
|
||||||
problem.addTestdata(
|
ProblemModel.addTestdata(
|
||||||
domainId, pid, tc.output_name,
|
domainId, pid, tc.output_name,
|
||||||
path.join(tmp, folder, 'testcase', tc.output_name),
|
path.join(tmp, folder, 'testcase', tc.output_name),
|
||||||
),
|
),
|
||||||
@@ -107,8 +102,8 @@ class ImportQduojHandler extends Handler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
problem.addTestdata(domainId, pid, 'config.yaml', Buffer.from(yaml.dump(config))),
|
ProblemModel.addTestdata(domainId, pid, 'config.yaml', Buffer.from(yaml.dump(config))),
|
||||||
problem.edit(domainId, pid, { html: true }),
|
ProblemModel.edit(domainId, pid, { html: true }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} finally {
|
} 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.Route('problem_import_qduoj', '/problem/import/qduoj', ImportQduojHandler, PERM.PERM_CREATE_PROBLEM);
|
||||||
ctx.inject('ProblemAdd', 'problem_import_qduoj', { icon: 'copy', text: 'From QDUOJ Export' });
|
ctx.inject('ProblemAdd', 'problem_import_qduoj', { icon: 'copy', text: 'From QDUOJ Export' });
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,6 @@
|
|||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "0.5.5",
|
"decode-html": "^2.0.0"
|
||||||
"decode-html": "^2.0.0",
|
|
||||||
"fs-extra": "^10.1.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import superagent from 'superagent';
|
import {
|
||||||
import { PluginContext } from 'hydrooj';
|
Context, ForbiddenError, superagent, SystemModel,
|
||||||
import { ForbiddenError, UserFacingError, ValidationError } from 'hydrooj/src/error';
|
TokenModel, UserFacingError, ValidationError,
|
||||||
import * as system from 'hydrooj/src/model/system';
|
} from 'hydrooj';
|
||||||
import token from 'hydrooj/src/model/token';
|
|
||||||
|
|
||||||
declare module 'hydrooj' {
|
declare module 'hydrooj' {
|
||||||
interface SystemKeys {
|
interface SystemKeys {
|
||||||
@@ -14,21 +13,21 @@ declare module 'hydrooj' {
|
|||||||
|
|
||||||
async function get() {
|
async function get() {
|
||||||
const [appid, [state]] = await Promise.all([
|
const [appid, [state]] = await Promise.all([
|
||||||
system.get('login-with-github.id'),
|
SystemModel.get('login-with-github.id'),
|
||||||
token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }),
|
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`;
|
this.response.redirect = `https://github.com/login/oauth/authorize?client_id=${appid}&state=${state}&scope=read:user,user:email`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function callback({ state, code }) {
|
async function callback({ state, code }) {
|
||||||
const [[appid, secret, endpoint, url], s] = await Promise.all([
|
const [[appid, secret, endpoint, url], s] = await Promise.all([
|
||||||
system.getMany([
|
SystemModel.getMany([
|
||||||
'login-with-github.id',
|
'login-with-github.id',
|
||||||
'login-with-github.secret',
|
'login-with-github.secret',
|
||||||
'login-with-github.endpoint',
|
'login-with-github.endpoint',
|
||||||
'server.url',
|
'server.url',
|
||||||
]),
|
]),
|
||||||
token.get(state, token.TYPE_OAUTH),
|
TokenModel.get(state, TokenModel.TYPE_OAUTH),
|
||||||
]);
|
]);
|
||||||
if (!s) throw new ValidationError('token');
|
if (!s) throw new ValidationError('token');
|
||||||
const res = await superagent.post(`${endpoint || 'https://github.com'}/login/oauth/access_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;
|
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.");
|
if (!ret.email) throw new ForbiddenError("You don't have a verified email.");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apply(ctx: PluginContext) {
|
export function apply(ctx: Context) {
|
||||||
ctx.provideModule('oauth', 'github', {
|
ctx.provideModule('oauth', 'github', {
|
||||||
text: 'Login with Github',
|
text: 'Login with Github',
|
||||||
callback,
|
callback,
|
||||||
|
|||||||
@@ -6,13 +6,7 @@
|
|||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"devDependencies": {
|
|
||||||
"@types/superagent": "^4.1.15"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"hydrooj": "*"
|
"hydrooj": "*"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"superagent": "^8.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as superagent from 'superagent';
|
import {
|
||||||
import { PluginContext } from 'hydrooj';
|
Context, superagent, SystemModel, TokenModel, UserFacingError,
|
||||||
|
} from 'hydrooj';
|
||||||
|
|
||||||
declare module 'hydrooj' {
|
declare module 'hydrooj' {
|
||||||
interface SystemKeys {
|
interface SystemKeys {
|
||||||
@@ -9,11 +10,10 @@ declare module 'hydrooj' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function get() {
|
async function get() {
|
||||||
const { system, token } = global.Hydro.model;
|
|
||||||
const [appid, url, [state]] = await Promise.all([
|
const [appid, url, [state]] = await Promise.all([
|
||||||
system.get('login-with-google.id'),
|
SystemModel.get('login-with-google.id'),
|
||||||
system.get('server.url'),
|
SystemModel.get('server.url'),
|
||||||
token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }),
|
TokenModel.add(TokenModel.TYPE_OAUTH, 600, { redirect: this.request.referer }),
|
||||||
]);
|
]);
|
||||||
// eslint-disable-next-line max-len
|
// 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}`;
|
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({
|
async function callback({
|
||||||
state, code, error,
|
state, code, error,
|
||||||
}) {
|
}) {
|
||||||
const { system, token } = global.Hydro.model;
|
|
||||||
const { UserFacingError } = global.Hydro.error;
|
|
||||||
if (error) throw new UserFacingError(error);
|
if (error) throw new UserFacingError(error);
|
||||||
const [
|
const [[appid, secret, url], s] = await Promise.all([
|
||||||
[appid, secret, url],
|
SystemModel.getMany([
|
||||||
s,
|
|
||||||
] = await Promise.all([
|
|
||||||
system.getMany([
|
|
||||||
'login-with-google.id', 'login-with-google.secret', 'server.url',
|
'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')
|
const res = await superagent.post('https://oauth2.googleapis.com/token')
|
||||||
.send({
|
.send({
|
||||||
@@ -66,7 +61,7 @@ async function callback({
|
|||||||
redirect_uri: `${url}oauth/google/callback`,
|
redirect_uri: `${url}oauth/google/callback`,
|
||||||
});
|
});
|
||||||
const payload = decodeJWT(res.body.id_token).payload;
|
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;
|
this.response.redirect = s.redirect;
|
||||||
return {
|
return {
|
||||||
// TODO use openid
|
// TODO use openid
|
||||||
@@ -77,7 +72,7 @@ async function callback({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apply(ctx: PluginContext) {
|
export function apply(ctx: Context) {
|
||||||
ctx.provideModule('oauth', 'google', {
|
ctx.provideModule('oauth', 'google', {
|
||||||
text: 'Login with Google',
|
text: 'Login with Google',
|
||||||
callback,
|
callback,
|
||||||
@@ -6,13 +6,7 @@
|
|||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"devDependencies": {
|
|
||||||
"@types/superagent": "^4.1.15"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"hydrooj": "*"
|
"hydrooj": "*"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"superagent": "^8.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import { md5, sha1 } from '@hydrooj/utils/lib/utils';
|
import { Context, md5, sha1 } from 'hydrooj';
|
||||||
import { PluginContext } from 'hydrooj';
|
|
||||||
import { apply as hustoj } from './scripts/hustoj';
|
import { apply as hustoj } from './scripts/hustoj';
|
||||||
import { apply as syzoj } from './scripts/syzoj';
|
import { apply as syzoj } from './scripts/syzoj';
|
||||||
import { apply as vijos } from './scripts/vijos';
|
import { apply as vijos } from './scripts/vijos';
|
||||||
|
|
||||||
const RE_MD5 = /^[\da-f]{32}$/;
|
const RE_MD5 = /^[\da-f]{32}$/;
|
||||||
|
|
||||||
export function apply(ctx: PluginContext) {
|
export function apply(ctx: Context) {
|
||||||
hustoj(ctx);
|
hustoj(ctx);
|
||||||
vijos(ctx);
|
vijos(ctx);
|
||||||
syzoj(ctx);
|
syzoj(ctx);
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
"@types/turndown": "^5.0.1"
|
"@types/turndown": "^5.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hydrooj/utils": "workspace:*",
|
|
||||||
"js-yaml": "^4.1.0",
|
|
||||||
"mariadb": "^3.0.1",
|
"mariadb": "^3.0.1",
|
||||||
"mongodb": "^3.7.3",
|
"mongodb": "^3.7.3",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
|
|||||||
@@ -1,23 +1,11 @@
|
|||||||
/* eslint-disable no-tabs */
|
/* eslint-disable no-tabs */
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import fs from 'fs-extra';
|
|
||||||
import { ObjectID } from 'mongodb';
|
|
||||||
import mysql from 'mysql';
|
import mysql from 'mysql';
|
||||||
import TurndownService from 'turndown';
|
import TurndownService from 'turndown';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import {
|
||||||
import { noop, Time } from '@hydrooj/utils/lib/utils';
|
buildContent, ContestModel, DomainModel, fs, noop, NotFoundError, ObjectID, postJudge, ProblemModel,
|
||||||
import { Schema } from 'hydrooj';
|
RecordDoc, RecordModel, Schema, SolutionModel, STATUS, SystemModel, Time, UserModel,
|
||||||
import { NotFoundError } from 'hydrooj/src/error';
|
} from 'hydrooj';
|
||||||
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';
|
|
||||||
|
|
||||||
const turndown = new TurndownService({
|
const turndown = new TurndownService({
|
||||||
codeBlockStyle: 'fenced',
|
codeBlockStyle: 'fenced',
|
||||||
@@ -81,7 +69,7 @@ export async function run({
|
|||||||
else res([val, fields]);
|
else res([val, fields]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const target = await domain.get(domainId);
|
const target = await DomainModel.get(domainId);
|
||||||
if (!target) throw new NotFoundError(domainId);
|
if (!target) throw new NotFoundError(domainId);
|
||||||
report({ message: 'Connected to database' });
|
report({ message: 'Connected to database' });
|
||||||
/*
|
/*
|
||||||
@@ -101,22 +89,22 @@ export async function run({
|
|||||||
*/
|
*/
|
||||||
const uidMap: Record<string, number> = {};
|
const uidMap: Record<string, number> = {};
|
||||||
const [udocs] = await query('SELECT * FROM `users`');
|
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(', ')}`);
|
if (precheck.length) throw new Error(`Conflict username: ${precheck.map((u) => u.unameLower).join(', ')}`);
|
||||||
for (const udoc of udocs) {
|
for (const udoc of udocs) {
|
||||||
if (randomMail) delete udoc.email;
|
if (randomMail) delete udoc.email;
|
||||||
let current = await user.getByEmail(domainId, udoc.email || `${udoc.user_id}@hustoj.local`);
|
let current = await UserModel.getByEmail(domainId, udoc.email || `${udoc.user_id}@hustoj.local`);
|
||||||
if (!current) current = await user.getByUname(domainId, udoc.user_id);
|
if (!current) current = await UserModel.getByUname(domainId, udoc.user_id);
|
||||||
if (current) {
|
if (current) {
|
||||||
report({ message: `duplicate user with email ${udoc.email}: ${current.uname},${udoc.user_id}` });
|
report({ message: `duplicate user with email ${udoc.email}: ${current.uname},${udoc.user_id}` });
|
||||||
uidMap[udoc.user_id] = current._id;
|
uidMap[udoc.user_id] = current._id;
|
||||||
} else {
|
} else {
|
||||||
const uid = await user.create(
|
const uid = await UserModel.create(
|
||||||
udoc.email || `${udoc.user_id}@hustoj.local`, udoc.user_id, '',
|
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;
|
uidMap[udoc.user_id] = uid;
|
||||||
await user.setById(uid, {
|
await UserModel.setById(uid, {
|
||||||
loginat: udoc.accesstime,
|
loginat: udoc.accesstime,
|
||||||
regat: udoc.reg_time,
|
regat: udoc.reg_time,
|
||||||
hash: udoc.password,
|
hash: udoc.password,
|
||||||
@@ -124,7 +112,7 @@ export async function run({
|
|||||||
school: udoc.school || '',
|
school: udoc.school || '',
|
||||||
hashType: 'hust',
|
hashType: 'hust',
|
||||||
});
|
});
|
||||||
await domain.setUserInDomain(domainId, uid, {
|
await DomainModel.setUserInDomain(domainId, uid, {
|
||||||
displayName: udoc.nick || '',
|
displayName: udoc.nick || '',
|
||||||
school: udoc.school || '',
|
school: udoc.school || '',
|
||||||
nSubmit: udoc.submit,
|
nSubmit: udoc.submit,
|
||||||
@@ -134,7 +122,7 @@ export async function run({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [admins] = await query("SELECT * FROM `privilege` WHERE `rightstr` = 'administrator'");
|
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]);
|
const adminUids = admins.map((admin) => uidMap[admin.user_id]);
|
||||||
report({ message: 'user finished' });
|
report({ message: 'user finished' });
|
||||||
|
|
||||||
@@ -167,11 +155,11 @@ export async function run({
|
|||||||
const [pdocs] = await query(`SELECT * FROM \`problem\` LIMIT ${pageId * step}, ${step}`);
|
const [pdocs] = await query(`SELECT * FROM \`problem\` LIMIT ${pageId * step}, ${step}`);
|
||||||
for (const pdoc of pdocs) {
|
for (const pdoc of pdocs) {
|
||||||
if (rerun) {
|
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 (opdoc) pidMap[pdoc.problem_id] = opdoc.docId;
|
||||||
}
|
}
|
||||||
if (!pidMap[pdoc.problem_id]) {
|
if (!pidMap[pdoc.problem_id]) {
|
||||||
const pid = await problem.add(
|
const pid = await ProblemModel.add(
|
||||||
domainId, `P${pdoc.problem_id}`,
|
domainId, `P${pdoc.problem_id}`,
|
||||||
pdoc.title, buildContent({
|
pdoc.title, buildContent({
|
||||||
description: pdoc.description,
|
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 [cdoc] = await query(`SELECT * FROM \`privilege\` WHERE \`rightstr\` = 'p${pdoc.problem_id}'`);
|
||||||
const maintainer = [];
|
const maintainer = [];
|
||||||
for (let i = 1; i < cdoc.length; i++) maintainer.push(uidMap[cdoc[i].user_id]);
|
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,
|
nAccept: 0,
|
||||||
nSubmit: pdoc.submit,
|
nSubmit: pdoc.submit,
|
||||||
config: `time: ${pdoc.time_limit}s\nmemory: ${pdoc.memory_limit}m`,
|
config: `time: ${pdoc.time_limit}s\nmemory: ${pdoc.memory_limit}m`,
|
||||||
@@ -221,7 +209,7 @@ export async function run({
|
|||||||
for (const tdoc of tdocs) {
|
for (const tdoc of tdocs) {
|
||||||
const [pdocs] = await query(`SELECT * FROM \`contest_problem\` WHERE \`contest_id\` = ${tdoc.contest_id}`);
|
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 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',
|
domainId, tdoc.title, tdoc.description || 'Description',
|
||||||
adminUids[0], contestType, tdoc.start_time, tdoc.end_time, pids, true,
|
adminUids[0], contestType, tdoc.start_time, tdoc.end_time, pids, true,
|
||||||
{ _code: password },
|
{ _code: password },
|
||||||
@@ -282,9 +270,9 @@ export async function run({
|
|||||||
if (source[0]?.source) data.code = source[0].source;
|
if (source[0]?.source) data.code = source[0].source;
|
||||||
if (rdoc.contest_id) {
|
if (rdoc.contest_id) {
|
||||||
data.contest = new ObjectID(tidMap[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 }));
|
await postJudge(data).catch((err) => report({ message: err.message }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,15 +286,15 @@ export async function run({
|
|||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file.isDirectory()) continue;
|
if (!file.isDirectory()) continue;
|
||||||
const datas = await fs.readdir(`${dataDir}/${file.name}`, { withFileTypes: true });
|
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;
|
if (!pdoc) continue;
|
||||||
report({ message: `Syncing testdata for ${file.name}` });
|
report({ message: `Syncing testdata for ${file.name}` });
|
||||||
for (const data of datas) {
|
for (const data of datas) {
|
||||||
if (data.isDirectory()) continue;
|
if (data.isDirectory()) continue;
|
||||||
const filename = nameMap[data.name] || data;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,10 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import fs from 'fs-extra';
|
|
||||||
import yaml from 'js-yaml';
|
|
||||||
import mariadb from 'mariadb';
|
import mariadb from 'mariadb';
|
||||||
import { ObjectID } from 'mongodb';
|
import {
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
buildContent, ContestModel, DiscussionDoc, DiscussionReplyDoc, DocumentModel, DomainModel,
|
||||||
import { noop, Time } from '@hydrooj/utils/lib/utils';
|
fs, noop, NotFoundError, ObjectID, PERM, postJudge, ProblemModel, RecordDoc, RecordModel,
|
||||||
import { Schema } from 'hydrooj';
|
Schema, STATUS, SystemModel, Time, UserModel, yaml,
|
||||||
import { NotFoundError } from 'hydrooj/src/error';
|
} from 'hydrooj';
|
||||||
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';
|
|
||||||
|
|
||||||
const contentTypeMap = {
|
const contentTypeMap = {
|
||||||
noi: 'oi',
|
noi: 'oi',
|
||||||
@@ -99,7 +86,7 @@ export async function run({
|
|||||||
const query = (q: string) => new Promise<any[]>((res, rej) => {
|
const query = (q: string) => new Promise<any[]>((res, rej) => {
|
||||||
src.query(q).then((r) => res(r)).catch((e) => rej(e));
|
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);
|
if (!target) throw new NotFoundError(domainId);
|
||||||
report({ message: 'Connected to database' });
|
report({ message: 'Connected to database' });
|
||||||
/*
|
/*
|
||||||
@@ -124,24 +111,24 @@ export async function run({
|
|||||||
const superAdmin = [];
|
const superAdmin = [];
|
||||||
const udocs = await query('SELECT * FROM `user`');
|
const udocs = await query('SELECT * FROM `user`');
|
||||||
report({ message: udocs.map((u) => u.username.toLowerCase()) });
|
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(', ')}`);
|
if (precheck.length) throw new Error(`Conflict username: ${precheck.map((u) => u.unameLower).join(', ')}`);
|
||||||
for (const udoc of udocs) {
|
for (const udoc of udocs) {
|
||||||
if (randomMail) delete udoc.email;
|
if (randomMail) delete udoc.email;
|
||||||
let current = await user.getByEmail(domainId, udoc.email || `${udoc.username}@syzoj.local`);
|
let current = await UserModel.getByEmail(domainId, udoc.email || `${udoc.username}@syzoj.local`);
|
||||||
if (!current) current = await user.getByUname(domainId, udoc.username);
|
if (!current) current = await UserModel.getByUname(domainId, udoc.username);
|
||||||
if (current) {
|
if (current) {
|
||||||
report({ message: `duplicate user with email ${udoc.email}: ${current.uname},${udoc.username}` });
|
report({ message: `duplicate user with email ${udoc.email}: ${current.uname},${udoc.username}` });
|
||||||
uidMap[udoc.id] = current._id;
|
uidMap[udoc.id] = current._id;
|
||||||
} else {
|
} else {
|
||||||
const uid = await user.create(
|
const uid = await UserModel.create(
|
||||||
udoc.email || `${udoc.username}@syzoj.local`, udoc.username, '',
|
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);
|
superAdmin.push(uid);
|
||||||
uidMap[udoc.id] = uid;
|
uidMap[udoc.id] = uid;
|
||||||
await user.setById(uid, {
|
await UserModel.setById(uid, {
|
||||||
regat: new Date(udoc.register_time * 1000),
|
regat: new Date(udoc.register_time * 1000),
|
||||||
hash: udoc.password,
|
hash: udoc.password,
|
||||||
salt: udoc.password,
|
salt: udoc.password,
|
||||||
@@ -149,7 +136,7 @@ export async function run({
|
|||||||
bio: udoc.information || '',
|
bio: udoc.information || '',
|
||||||
gender: sexMap[udoc.sex] || 3,
|
gender: sexMap[udoc.sex] || 3,
|
||||||
});
|
});
|
||||||
await domain.setUserInDomain(domainId, uid, {
|
await DomainModel.setUserInDomain(domainId, uid, {
|
||||||
displayName: udoc.nickname || '',
|
displayName: udoc.nickname || '',
|
||||||
nSubmit: udoc.submit_num,
|
nSubmit: udoc.submit_num,
|
||||||
nAccept: udoc.ac_num,
|
nAccept: udoc.ac_num,
|
||||||
@@ -158,19 +145,19 @@ export async function run({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// I think manage_problem_tag is a useless role
|
// 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_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);
|
| 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');
|
const privileges = await query('SELECT user_id,group_concat(privilege) as privilege FROM `user_privilege` group by user_id');
|
||||||
for (const privilege of privileges) {
|
for (const privilege of privileges) {
|
||||||
if (!superAdmin.includes(privilege.user_id)) {
|
if (!superAdmin.includes(privilege.user_id)) {
|
||||||
if (privilege.privilege.split(',').includes('manage_problem') && privilege.privilege.split(',').includes('manage_user')) {
|
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')) {
|
} 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')) {
|
} 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}`);
|
const pdocs = await query(`SELECT * FROM \`problem\` LIMIT ${pageId * step}, ${step}`);
|
||||||
for (const pdoc of pdocs) {
|
for (const pdoc of pdocs) {
|
||||||
if (rerun) {
|
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 (opdoc) pidMap[pdoc.id] = opdoc.docId;
|
||||||
}
|
}
|
||||||
if (!pidMap[pdoc.id]) {
|
if (!pidMap[pdoc.id]) {
|
||||||
@@ -232,13 +219,13 @@ export async function run({
|
|||||||
else problemAdditionalFile[`P${pdoc.id}`].push({ fromPid: pid, filename });
|
else problemAdditionalFile[`P${pdoc.id}`].push({ fromPid: pid, filename });
|
||||||
content = content.replace(origialPath, `file://${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;
|
pidMap[pdoc.id] = pid;
|
||||||
}
|
}
|
||||||
const tags = await query(`SELECT * FROM \`problem_tag_map\` WHERE \`problem_id\` = ${pdoc.id}`);
|
const tags = await query(`SELECT * FROM \`problem_tag_map\` WHERE \`problem_id\` = ${pdoc.id}`);
|
||||||
const tagList = [];
|
const tagList = [];
|
||||||
for (const tag of tags) tagList.push(tagMap[tag.tag_id]);
|
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,
|
nAccept: pdoc.ac_num || 0,
|
||||||
nSubmit: pdoc.submit_num || 0,
|
nSubmit: pdoc.submit_num || 0,
|
||||||
hidden: pdoc.is_public !== 1,
|
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}`);
|
const additionalFile = await query(`SELECT * FROM \`file\` WHERE \`id\` = ${pdoc.additional_file_id}`);
|
||||||
if (additionalFile.length) {
|
if (additionalFile.length) {
|
||||||
const [afdoc] = additionalFile;
|
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}`);
|
`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 pdocs = tdoc.problems.split('|').map((i) => i.trim());
|
||||||
const pids = pdocs.map((i) => pidMap[i]).filter((i) => i);
|
const pids = pdocs.map((i) => pidMap[i]).filter((i) => i);
|
||||||
const admin = uidMap[tdoc.holder_id] || uidMap[tdoc.admins.split('|')[0]];
|
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'}`,
|
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),
|
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]) },
|
pids, ratedTids.includes(tdoc.id), { maintainer: tdoc.admins.split('|').map((i) => uidMap[i]) },
|
||||||
@@ -359,9 +346,9 @@ export async function run({
|
|||||||
}
|
}
|
||||||
if (rdoc.type) {
|
if (rdoc.type) {
|
||||||
data.contest = new ObjectID(tidMap[rdoc.type_info]);
|
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 }));
|
await postJudge(data).catch((err) => report({ message: err.message }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,7 +372,7 @@ export async function run({
|
|||||||
for (const ddoc of ddocs) {
|
for (const ddoc of ddocs) {
|
||||||
const data: Partial<DiscussionDoc> = {
|
const data: Partial<DiscussionDoc> = {
|
||||||
_id: Time.getObjectID(new Date(ddoc.public_time * 1000), false),
|
_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),
|
docId: Time.getObjectID(new Date(ddoc.public_time * 1000), false),
|
||||||
owner: uidMap[ddoc.user_id] || 0,
|
owner: uidMap[ddoc.user_id] || 0,
|
||||||
title: ddoc.title,
|
title: ddoc.title,
|
||||||
@@ -397,11 +384,11 @@ export async function run({
|
|||||||
lock: ddoc.allow_comment === 0,
|
lock: ddoc.allow_comment === 0,
|
||||||
pin: ddoc.is_notice === 1,
|
pin: ddoc.is_notice === 1,
|
||||||
highlight: 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',
|
parentId: pidMap[ddoc.problem_id] || 'Hydro',
|
||||||
ip: '127.0.0.1',
|
ip: '127.0.0.1',
|
||||||
};
|
};
|
||||||
await document.coll.insertOne(data);
|
await DocumentModel.coll.insertOne(data);
|
||||||
didMap[ddoc.id] = data._id;
|
didMap[ddoc.id] = data._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,14 +405,14 @@ export async function run({
|
|||||||
_id: Time.getObjectID(new Date(drdoc.public_time * 1000), false),
|
_id: Time.getObjectID(new Date(drdoc.public_time * 1000), false),
|
||||||
domainId,
|
domainId,
|
||||||
docId: Time.getObjectID(new Date(drdoc.public_time * 1000), false),
|
docId: Time.getObjectID(new Date(drdoc.public_time * 1000), false),
|
||||||
docType: document.TYPE_DISCUSSION_REPLY,
|
docType: DocumentModel.TYPE_DISCUSSION_REPLY,
|
||||||
content: drdoc.content,
|
content: drdoc.content,
|
||||||
owner: uidMap[drdoc.user_id],
|
owner: uidMap[drdoc.user_id],
|
||||||
parentType: document.TYPE_DISCUSSION,
|
parentType: DocumentModel.TYPE_DISCUSSION,
|
||||||
parentId: didMap[drdoc.article_id],
|
parentId: didMap[drdoc.article_id],
|
||||||
ip: '127.0.0.1',
|
ip: '127.0.0.1',
|
||||||
};
|
};
|
||||||
await document.coll.insertOne(data);
|
await DocumentModel.coll.insertOne(data);
|
||||||
}
|
}
|
||||||
report({ message: 'article finished' });
|
report({ message: 'article finished' });
|
||||||
|
|
||||||
@@ -437,21 +424,21 @@ export async function run({
|
|||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file.isDirectory()) continue;
|
if (!file.isDirectory()) continue;
|
||||||
const datas = await fs.readdir(`${dataDir}/testdata/${file.name}`, { withFileTypes: true });
|
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;
|
if (!pdoc) continue;
|
||||||
report({ message: `Syncing testdata for ${file.name}` });
|
report({ message: `Syncing testdata for ${file.name}` });
|
||||||
for (const data of datas) {
|
for (const data of datas) {
|
||||||
if (data.isDirectory()) continue;
|
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_')) {
|
if (data.name.startsWith('spj_')) {
|
||||||
report({ message: `Syncing spj for ${file.name}` });
|
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}`);
|
`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]]}`;
|
pdoc.config += `\nchecker_type: syzoj\nchecker: spj.${langMap[data.name.split('spj_')[1].split('.')[0]]}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(datas.find((i) => i.name === 'data.yml'))) {
|
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 {
|
} else {
|
||||||
report({ message: `Transfering data.yml for ${file.name}` });
|
report({ message: `Transfering data.yml for ${file.name}` });
|
||||||
const config = yaml.load(configMap[`P${file.name}`]) as any;
|
const config = yaml.load(configMap[`P${file.name}`]) as any;
|
||||||
@@ -459,7 +446,7 @@ export async function run({
|
|||||||
if (syzojConfig.specialJudge) {
|
if (syzojConfig.specialJudge) {
|
||||||
report({ message: `Syncing spj config for ${file.name}` });
|
report({ message: `Syncing spj config for ${file.name}` });
|
||||||
config.checker_type = 'syzoj';
|
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}`);
|
`spj.${langMap[syzojConfig.specialJudge.language]}`, `${dataDir}/testdata/${file.name}/${syzojConfig.specialJudge.fileName}`);
|
||||||
config.checker = `spj.${langMap[syzojConfig.specialJudge.language]}`;
|
config.checker = `spj.${langMap[syzojConfig.specialJudge.language]}`;
|
||||||
}
|
}
|
||||||
@@ -476,7 +463,7 @@ export async function run({
|
|||||||
}
|
}
|
||||||
if (syzojConfig.extraSourceFiles?.length === 1) {
|
if (syzojConfig.extraSourceFiles?.length === 1) {
|
||||||
for (const { name: sourceName, dest } of syzojConfig.extraSourceFiles[0].files) {
|
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}`);
|
`${dataDir}/testdata/${file.name}/${sourceName}`);
|
||||||
}
|
}
|
||||||
config.user_extra_files = syzojConfig.extraSourceFiles[0].files.map((x) => x.dest);
|
config.user_extra_files = syzojConfig.extraSourceFiles[0].files.map((x) => x.dest);
|
||||||
@@ -490,17 +477,17 @@ export async function run({
|
|||||||
if (syzojConfig.interactor) {
|
if (syzojConfig.interactor) {
|
||||||
report({ message: `Syncing interactor config for ${file.name}` });
|
report({ message: `Syncing interactor config for ${file.name}` });
|
||||||
config.type = 'interactive';
|
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}`);
|
`spj.${langMap[syzojConfig.interactor.language]}`, `${dataDir}/testdata/${file.name}/${syzojConfig.interactor.fileName}`);
|
||||||
config.interactor = `spj.${langMap[syzojConfig.interactor.language]}`;
|
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}`]) {
|
if (problemAdditionalFile[`P${file.name}`]) {
|
||||||
report({ message: `Syncing additional_file for ${file.name}` });
|
report({ message: `Syncing additional_file for ${file.name}` });
|
||||||
for (const data of problemAdditionalFile[`P${file.name}`]) {
|
for (const data of problemAdditionalFile[`P${file.name}`]) {
|
||||||
if (!fs.existsSync(`${dataDir}/testdata/${data.fromPid}/${decodeURIComponent(data.filename)}`)) continue;
|
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)}`);
|
`${dataDir}/testdata/${data.fromPid}/${decodeURIComponent(data.filename)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import mongodb, { Cursor, Db } from 'mongodb';
|
import mongodb, { Cursor, Db } from 'mongodb';
|
||||||
import {
|
import {
|
||||||
DiscussionTailReplyDoc, MessageDoc,
|
DiscussionModel, DiscussionTailReplyDoc, MessageDoc,
|
||||||
RecordDoc, Schema, TestCase, TrainingNode,
|
RecordDoc, Schema, TestCase, TrainingNode,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
|
||||||
const dst = global.Hydro.service.db;
|
const dst = global.Hydro.service.db;
|
||||||
const { discussion } = global.Hydro.model;
|
|
||||||
const map = {};
|
const map = {};
|
||||||
|
|
||||||
const pid = (id) => {
|
const pid = (id) => {
|
||||||
@@ -300,11 +299,11 @@ async function discussionNode(src: Db, report: Function) {
|
|||||||
const nodes = item[1];
|
const nodes = item[1];
|
||||||
for (const node of nodes || []) {
|
for (const node of nodes || []) {
|
||||||
if (node.pic) {
|
if (node.pic) {
|
||||||
t.push(discussion.addNode(
|
t.push(DiscussionModel.addNode(
|
||||||
doc.domain_id, node.name, category, { pic: node.pic },
|
doc.domain_id, node.name, category, { pic: node.pic },
|
||||||
));
|
));
|
||||||
} else {
|
} 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 { SystemModel, UiContextBase } from 'hydrooj';
|
||||||
import { UiContextBase } from 'hydrooj/src/service/layers/base';
|
|
||||||
|
|
||||||
declare module 'hydrooj/src/service/layers/base' {
|
declare module 'hydrooj' {
|
||||||
interface UiContextBase {
|
interface UiContextBase {
|
||||||
onlyofficeApi?: string;
|
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 { AggregatorRegistry, metric } from 'prom-client';
|
||||||
import * as system from 'hydrooj/src/model/system';
|
import { Context, Handler, SystemModel } from 'hydrooj';
|
||||||
import * as bus from 'hydrooj/src/service/bus';
|
|
||||||
import { Handler } from 'hydrooj/src/service/server';
|
|
||||||
import { registry } from './metrics';
|
import { registry } from './metrics';
|
||||||
|
|
||||||
declare module 'hydrooj/src/service/bus' {
|
declare module 'hydrooj' {
|
||||||
interface EventMap {
|
interface EventMap {
|
||||||
metrics: (id: string, metrics: any) => void;
|
metrics: (id: string, metrics: any) => void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
declare module 'hydrooj/src/interface' {
|
declare module 'hydrooj' {
|
||||||
interface SystemKeys {
|
interface SystemKeys {
|
||||||
'prom-client.name': string;
|
'prom-client.name': string;
|
||||||
'prom-client.password': string;
|
'prom-client.password': string;
|
||||||
@@ -30,7 +28,7 @@ class MetricsHandler extends Handler {
|
|||||||
this.response.addHeader('WWW-Authenticate', 'Basic');
|
this.response.addHeader('WWW-Authenticate', 'Basic');
|
||||||
return;
|
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];
|
const key = this.request.headers.authorization?.split('Basic ')?.[1];
|
||||||
if (!key || key !== Buffer.from(`${name}:${password}`).toString('base64')) {
|
if (!key || key !== Buffer.from(`${name}:${password}`).toString('base64')) {
|
||||||
this.response.status = 403;
|
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.on('metrics', (id, metrics) => { instances[id] = metrics; });
|
||||||
ctx.setInterval(async () => {
|
ctx.setInterval(async () => {
|
||||||
bus.broadcast('metrics', process.env.NODE_APP_INSTANCE!, await registry.getMetricsAsJSON());
|
ctx.broadcast('metrics', process.env.NODE_APP_INSTANCE!, await registry.getMetricsAsJSON());
|
||||||
}, 5000 * (+system.get('prom-client.collect_rate') || 1));
|
}, 5000 * (+SystemModel.get('prom-client.collect_rate') || 1));
|
||||||
ctx.Route('metrics', '/metrics', MetricsHandler);
|
ctx.Route('metrics', '/metrics', MetricsHandler);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
collectDefaultMetrics, Counter, Gauge, Metric, Registry,
|
collectDefaultMetrics, Counter, Gauge, Metric, Registry,
|
||||||
} from 'prom-client';
|
} from 'prom-client';
|
||||||
|
import { db } from 'hydrooj';
|
||||||
import * as bus from 'hydrooj/src/service/bus';
|
import * as bus from 'hydrooj/src/service/bus';
|
||||||
import * as db from 'hydrooj/src/service/db';
|
|
||||||
|
|
||||||
export const registry = new Registry();
|
export const registry = new Registry();
|
||||||
registry.setDefaultLabels({ instanceid: process.env.NODE_APP_INSTANCE });
|
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",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
"author": "undefined",
|
"author": "undefined",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true
|
||||||
"dependencies": {
|
|
||||||
"superagent": "^8.0.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+49
-48
@@ -1,56 +1,57 @@
|
|||||||
import { Schema } from 'hydrooj';
|
import {
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
Context, DomainModel, iterateAllProblem, iterateAllProblemInDomain,
|
||||||
import DomainModel from 'hydrooj/src/model/domain';
|
Logger, Schema, SystemModel,
|
||||||
import * as system from 'hydrooj/src/model/system';
|
} from 'hydrooj';
|
||||||
import { iterateAllProblem, iterateAllProblemInDomain } from 'hydrooj/src/pipelineUtils';
|
import * as SonicService from './service';
|
||||||
import sonic from './service';
|
|
||||||
|
|
||||||
const logger = new Logger('sonic');
|
const logger = new Logger('sonic');
|
||||||
|
|
||||||
global.Hydro.lib.problemSearch = async (domainId, query, opts) => {
|
export function apply(ctx: Context) {
|
||||||
const limit = opts?.limit || system.get('pagination.problem');
|
ctx.plugin(SonicService);
|
||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
async function run({ domainId }, report) {
|
global.Hydro.lib.problemSearch = async (domainId, query, opts) => {
|
||||||
if (domainId) await sonic.flushb('problem', domainId);
|
const limit = opts?.limit || SystemModel.get('pagination.problem');
|
||||||
else await sonic.flushc('problem');
|
const ids = await ctx.sonic.query('problem', `${domainId}@title`, query, { limit });
|
||||||
let i = 0;
|
if (limit - ids.length > 0) ids.push(...await ctx.sonic.query('problem', `${domainId}@content`, query, { limit: limit - ids.length }));
|
||||||
const cb = async (pdoc) => {
|
return {
|
||||||
i++;
|
countRelation: ids.length >= limit ? 'gte' : 'eq',
|
||||||
if (!(i % 1000)) report({ message: `${i} problems indexed` });
|
total: ids.length,
|
||||||
const union = await DomainModel.searchUnion({ union: pdoc.domainId, problem: true });
|
hits: ids,
|
||||||
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 function apply(ctx) {
|
async function run({ domainId }, report) {
|
||||||
|
if (domainId) await ctx.sonic.flushb('problem', domainId);
|
||||||
|
else await ctx.sonic.flushc('problem');
|
||||||
|
let i = 0;
|
||||||
|
const cb = async (pdoc) => {
|
||||||
|
i++;
|
||||||
|
if (!(i % 1000)) report({ message: `${i} problems indexed` });
|
||||||
|
const union = await DomainModel.searchUnion({ union: pdoc.domainId, problem: true });
|
||||||
|
const tasks = [];
|
||||||
|
for (const did of [pdoc.domainId, ...union.map((j) => j._id)]) {
|
||||||
|
tasks.push(
|
||||||
|
pdoc.title && ctx.sonic.push(
|
||||||
|
'problem', `${did}@title`, `${pdoc.domainId}/${pdoc.docId}`,
|
||||||
|
`${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag.join(' ')}`,
|
||||||
|
),
|
||||||
|
pdoc.content.toString() && ctx.sonic.push('problem', `${did}@content`, `${pdoc.domainId}/${pdoc.docId}`, pdoc.content.toString()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await Promise.all(tasks).catch((e) => console.log(`${pdoc.domainId}/${pdoc.docId}`, e));
|
||||||
|
};
|
||||||
|
if (domainId) await iterateAllProblemInDomain(domainId, ['title', 'content'], cb);
|
||||||
|
else await iterateAllProblem(['title', 'content', 'tag'], cb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ctx.on('problem/add', async (doc, docId) => {
|
ctx.on('problem/add', async (doc, docId) => {
|
||||||
const union = await DomainModel.searchUnion({ union: doc.domainId, problem: true });
|
const union = await DomainModel.searchUnion({ union: doc.domainId, problem: true });
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
for (const domainId of [doc.domainId, ...union.map((i) => i._id)]) {
|
for (const domainId of [doc.domainId, ...union.map((i) => i._id)]) {
|
||||||
tasks.push(
|
tasks.push(
|
||||||
sonic.push('problem', `${domainId}@title`, `${doc.domainId}/${docId}`, `${doc.pid || ''} ${doc.title} ${doc.tag?.join(' ')}`),
|
ctx.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}@content`, `${doc.domainId}/${docId}`, doc.content.toString()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Promise.all(tasks).catch((e) => logger.error(e));
|
Promise.all(tasks).catch((e) => logger.error(e));
|
||||||
@@ -62,10 +63,10 @@ export function apply(ctx) {
|
|||||||
const id = `${pdoc.domainId}/${pdoc.docId}`;
|
const id = `${pdoc.domainId}/${pdoc.docId}`;
|
||||||
for (const domainId of [pdoc.domainId, ...union.map((i) => i._id)]) {
|
for (const domainId of [pdoc.domainId, ...union.map((i) => i._id)]) {
|
||||||
tasks.push(
|
tasks.push(
|
||||||
sonic.flusho('problem', `${domainId}@title`, id)
|
ctx.sonic.flusho('problem', `${domainId}@title`, id)
|
||||||
.then(() => sonic.push('problem', `${domainId}@title`, id, `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag?.join(' ')}`)),
|
.then(() => ctx.sonic.push('problem', `${domainId}@title`, id, `${pdoc.pid || ''} ${pdoc.title} ${pdoc.tag?.join(' ')}`)),
|
||||||
sonic.flusho('problem', `${domainId}@content`, id)
|
ctx.sonic.flusho('problem', `${domainId}@content`, id)
|
||||||
.then(() => sonic.push('problem', `${domainId}@content`, id, pdoc.content.toString())),
|
.then(() => ctx.sonic.push('problem', `${domainId}@content`, id, pdoc.content.toString())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Promise.all(tasks).catch((e) => logger.error(e));
|
Promise.all(tasks).catch((e) => logger.error(e));
|
||||||
@@ -77,8 +78,8 @@ export function apply(ctx) {
|
|||||||
const id = `${domainId}/${docId}`;
|
const id = `${domainId}/${docId}`;
|
||||||
for (const domain of [domainId, ...union.map((i) => i._id)]) {
|
for (const domain of [domainId, ...union.map((i) => i._id)]) {
|
||||||
tasks.push(
|
tasks.push(
|
||||||
sonic.flusho('problem', `${domain}@title`, id),
|
ctx.sonic.flusho('problem', `${domain}@title`, id),
|
||||||
sonic.flusho('problem', `${domain}@content`, id),
|
ctx.sonic.flusho('problem', `${domain}@content`, id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await Promise.all(tasks);
|
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,
|
Ingest, PushOptions, QueryOptions,
|
||||||
Search, SuggestOptions,
|
Search, SuggestOptions,
|
||||||
} from 'sonic-channel';
|
} from 'sonic-channel';
|
||||||
import { BaseService } from 'hydrooj';
|
import {
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
Context, Logger, Service, SystemModel,
|
||||||
import * as system from 'hydrooj/src/model/system';
|
} from 'hydrooj';
|
||||||
|
|
||||||
const logger = new Logger('sonic');
|
declare module 'hydrooj' {
|
||||||
|
|
||||||
declare module 'hydrooj/src/interface' {
|
|
||||||
interface SystemKeys {
|
interface SystemKeys {
|
||||||
'sonic.host': string;
|
'sonic.host': string;
|
||||||
'sonic.port': number;
|
'sonic.port': number;
|
||||||
'sonic.auth': string;
|
'sonic.auth': string;
|
||||||
}
|
}
|
||||||
|
interface Context {
|
||||||
|
sonic: SonicService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logger = new Logger('sonic');
|
||||||
|
|
||||||
function getHandler(type: string, that: any) {
|
function getHandler(type: string, that: any) {
|
||||||
return {
|
return {
|
||||||
connected() {
|
connected() {
|
||||||
that.started = true;
|
|
||||||
logger.info(`Sonic Channel succeeded to connect to host (${type}).`);
|
logger.info(`Sonic Channel succeeded to connect to host (${type}).`);
|
||||||
},
|
},
|
||||||
disconnected() {
|
disconnected() {
|
||||||
that.started = false;
|
|
||||||
logger.error(`Sonic Channel is now disconnected (${type}).`);
|
logger.error(`Sonic Channel is now disconnected (${type}).`);
|
||||||
},
|
},
|
||||||
timeout() {
|
timeout() {
|
||||||
@@ -39,14 +40,13 @@ function getHandler(type: string, that: any) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class SonicService implements BaseService {
|
class SonicService extends Service {
|
||||||
public search: Search;
|
public search: Search;
|
||||||
public ingest: Ingest;
|
public ingest: Ingest;
|
||||||
public started = false;
|
|
||||||
public error = '';
|
public error = '';
|
||||||
|
|
||||||
async start() {
|
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 = {
|
const cfg = {
|
||||||
host: host || '::1',
|
host: host || '::1',
|
||||||
port: port || 1491,
|
port: port || 1491,
|
||||||
@@ -108,11 +108,6 @@ class SonicService implements BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const service = new SonicService();
|
export function apply() {
|
||||||
global.Hydro.service.sonic = service;
|
Context.service('sonic', SonicService);
|
||||||
export = service;
|
|
||||||
declare module 'hydrooj/src/interface' {
|
|
||||||
interface Service {
|
|
||||||
sonic: typeof service
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,26 +2,18 @@
|
|||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import fs from 'fs-extra';
|
import {
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
ContestModel, fs, Handler, Logger, ObjectID, PERM, PRIV, ProblemModel, Schema,
|
||||||
import { PERM, PRIV } from 'hydrooj/src/model/builtin';
|
SettingModel, SystemModel, SystemSettings, UiContextBase,
|
||||||
import * as contest from 'hydrooj/src/model/contest';
|
UserModel,
|
||||||
import problem from 'hydrooj/src/model/problem';
|
} from 'hydrooj';
|
||||||
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 { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { ObjectID } from 'mongodb';
|
|
||||||
import { tmpdir } from 'os';
|
import { tmpdir } from 'os';
|
||||||
import { join, resolve } from 'path';
|
import { join, resolve } from 'path';
|
||||||
import Schema from 'schemastery';
|
|
||||||
import convert from 'schemastery-jsonschema';
|
import convert from 'schemastery-jsonschema';
|
||||||
import markdown from './backendlib/markdown';
|
import markdown from './backendlib/markdown';
|
||||||
|
|
||||||
declare module 'hydrooj/src/interface' {
|
declare module 'hydrooj' {
|
||||||
interface UI {
|
interface UI {
|
||||||
esbuildPlugins?: esbuild.Plugin[]
|
esbuildPlugins?: esbuild.Plugin[]
|
||||||
}
|
}
|
||||||
@@ -29,8 +21,6 @@ declare module 'hydrooj/src/interface' {
|
|||||||
'ui-default.nav_logo_dark': string;
|
'ui-default.nav_logo_dark': string;
|
||||||
'ui-default.nav_logo_dark_2x': string;
|
'ui-default.nav_logo_dark_2x': string;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
declare module 'hydrooj/src/service/layers/base' {
|
|
||||||
interface UiContextBase {
|
interface UiContextBase {
|
||||||
nav_logo_dark: string;
|
nav_logo_dark: string;
|
||||||
nav_logo_dark_2x: string;
|
nav_logo_dark_2x: string;
|
||||||
@@ -70,7 +60,7 @@ export async function buildUI() {
|
|||||||
if (build.errors.length) console.error(build.errors);
|
if (build.errors.length) console.error(build.errors);
|
||||||
if (build.warnings.length) console.warn(build.warnings);
|
if (build.warnings.length) console.warn(build.warnings);
|
||||||
const pages = build.outputFiles.map((i) => i.text);
|
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');
|
const c = crypto.createHash('sha1');
|
||||||
c.update(JSON.stringify(payload));
|
c.update(JSON.stringify(payload));
|
||||||
@@ -80,7 +70,7 @@ export async function buildUI() {
|
|||||||
logger.success('UI addons built in %d ms', Date.now() - start);
|
logger.success('UI addons built in %d ms', Date.now() - start);
|
||||||
}
|
}
|
||||||
function updateLogo() {
|
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',
|
'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -89,7 +79,7 @@ class WikiHelpHandler extends Handler {
|
|||||||
noCheckPermView = true;
|
noCheckPermView = true;
|
||||||
|
|
||||||
async get({ domainId }) {
|
async get({ domainId }) {
|
||||||
const LANGS = setting.langs;
|
const LANGS = SettingModel.langs;
|
||||||
const languages = {};
|
const languages = {};
|
||||||
for (const key in LANGS) {
|
for (const key in LANGS) {
|
||||||
if (LANGS[key].domain && !LANGS[key].domain.includes(domainId)) continue;
|
if (LANGS[key].domain && !LANGS[key].domain.includes(domainId)) continue;
|
||||||
@@ -105,9 +95,9 @@ class WikiAboutHandler extends Handler {
|
|||||||
noCheckPermView = true;
|
noCheckPermView = true;
|
||||||
|
|
||||||
async get() {
|
async get() {
|
||||||
let raw = system.get('ui-default.about') || '';
|
let raw = SystemModel.get('ui-default.about') || '';
|
||||||
// TODO template engine
|
// 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 lines = raw.split('\n');
|
||||||
const sections = [];
|
const sections = [];
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
@@ -130,7 +120,7 @@ class SetThemeHandler extends Handler {
|
|||||||
|
|
||||||
async get({ theme }) {
|
async get({ theme }) {
|
||||||
this.checkPriv(PRIV.PRIV_USER_PROFILE);
|
this.checkPriv(PRIV.PRIV_USER_PROFILE);
|
||||||
await user.setById(this.user._id, { theme });
|
await UserModel.setById(this.user._id, { theme });
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,7 +155,7 @@ class UiConstantsHandler extends ResourceHandler {
|
|||||||
|
|
||||||
class LanguageHandler extends ResourceHandler {
|
class LanguageHandler extends ResourceHandler {
|
||||||
async all({ lang }) {
|
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.body = `window.LOCALES=${JSON.stringify(global.Hydro.locales[lang])};`;
|
||||||
this.response.type = 'application/javascript';
|
this.response.type = 'application/javascript';
|
||||||
}
|
}
|
||||||
@@ -181,34 +171,34 @@ class SystemConfigSchemaHandler extends Handler {
|
|||||||
class RichMediaHandler extends Handler {
|
class RichMediaHandler extends Handler {
|
||||||
async renderUser(domainId, payload) {
|
async renderUser(domainId, payload) {
|
||||||
let d = payload.domainId || domainId;
|
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;
|
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 });
|
return await this.renderHTML('partials/user.html', { udoc });
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderProblem(domainId, payload) {
|
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)
|
let pdoc = cur.hasPerm(PERM.PERM_VIEW | PERM.PERM_VIEW_PROBLEM)
|
||||||
? await problem.get(payload.domainId || domainId, payload.id) || problem.default
|
? await ProblemModel.get(payload.domainId || domainId, payload.id) || ProblemModel.default
|
||||||
: problem.default;
|
: ProblemModel.default;
|
||||||
if (pdoc.hidden && !cur.own(pdoc) && !cur.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) pdoc = problem.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 });
|
return await this.renderHTML('partials/problem.html', { pdoc });
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderContest(domainId, payload) {
|
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)
|
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;
|
: null;
|
||||||
if (tdoc) return await this.renderHTML('partials/contest.html', { tdoc });
|
if (tdoc) return await this.renderHTML('partials/contest.html', { tdoc });
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderHomework(domainId, payload) {
|
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)
|
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;
|
: null;
|
||||||
if (tdoc) return await this.renderHTML('partials/homework.html', { tdoc });
|
if (tdoc) return await this.renderHTML('partials/homework.html', { tdoc });
|
||||||
return '';
|
return '';
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface RemoteAccount {
|
|||||||
problemLists?: string[];
|
problemLists?: string[];
|
||||||
enableOn?: string[];
|
enableOn?: string[];
|
||||||
}
|
}
|
||||||
declare module 'hydrooj/src/interface' {
|
declare module 'hydrooj' {
|
||||||
interface Collections {
|
interface Collections {
|
||||||
vjudge: RemoteAccount;
|
vjudge: RemoteAccount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { sleep } from '@hydrooj/utils/lib/utils';
|
import {
|
||||||
import * as Judge from 'hydrooj/src/handler/judge';
|
Context, db, DomainModel, JudgeHandler, Logger,
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
ProblemModel, sleep, STATUS, TaskModel,
|
||||||
import { STATUS } from 'hydrooj/src/model/builtin';
|
} from 'hydrooj';
|
||||||
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 { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
|
import { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
|
||||||
import { getDifficulty } from './providers/codeforces';
|
import { getDifficulty } from './providers/codeforces';
|
||||||
import providers from './providers/index';
|
import providers from './providers/index';
|
||||||
@@ -40,8 +35,8 @@ class Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async judge(task) {
|
async judge(task) {
|
||||||
const next = (payload) => Judge.next({ ...payload, rid: task.rid });
|
const next = (payload) => JudgeHandler.next({ ...payload, rid: task.rid });
|
||||||
const end = (payload) => Judge.end({ ...payload, rid: task.rid });
|
const end = (payload) => JudgeHandler.end({ ...payload, rid: task.rid });
|
||||||
await next({ status: STATUS.STATUS_FETCHED });
|
await next({ status: STATUS.STATUS_FETCHED });
|
||||||
try {
|
try {
|
||||||
const rid = await this.api.submitProblem(task.target, task.lang, task.code, task, next, end);
|
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 {
|
interface Model {
|
||||||
vjudge: VJudgeModel;
|
vjudge: VJudgeModel;
|
||||||
}
|
}
|
||||||
@@ -155,4 +150,6 @@ class VJudgeModel {
|
|||||||
|
|
||||||
global.Hydro.model.vjudge = 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 StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
||||||
import superagent from 'superagent';
|
import superagent from 'superagent';
|
||||||
import proxy from 'superagent-proxy';
|
import proxy from 'superagent-proxy';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import {
|
||||||
import { sleep } from '@hydrooj/utils/lib/utils';
|
buildContent, Logger, SettingModel, sleep, STATUS,
|
||||||
import { buildContent } from 'hydrooj/src/lib/content';
|
} from 'hydrooj';
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
|
||||||
import * as setting from 'hydrooj/src/model/setting';
|
|
||||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
import { VERDICT } from '../verdict';
|
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) {
|
async submitProblem(id: string, lang: string, code: string, info, next, end) {
|
||||||
const programTypeId = lang.includes('codeforces.') ? lang.split('codeforces.')[1] : '54';
|
const programTypeId = lang.includes('codeforces.') ? lang.split('codeforces.')[1] : '54';
|
||||||
const comment = setting.langs[lang].comment;
|
const comment = SettingModel.langs[lang].comment;
|
||||||
if (comment) {
|
if (comment) {
|
||||||
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
||||||
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { PassThrough } from 'stream';
|
|||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import * as superagent from 'superagent';
|
import * as superagent from 'superagent';
|
||||||
import proxy from 'superagent-proxy';
|
import proxy from 'superagent-proxy';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { Logger, sleep, STATUS } from 'hydrooj';
|
||||||
import { sleep } from '@hydrooj/utils/lib/utils';
|
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
|
||||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
|
|
||||||
proxy(superagent as any);
|
proxy(superagent as any);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import poj from './poj';
|
|||||||
import spoj from './spoj';
|
import spoj from './spoj';
|
||||||
import uoj from './uoj';
|
import uoj from './uoj';
|
||||||
|
|
||||||
declare module 'hydrooj/src/interface' {
|
declare module 'hydrooj' {
|
||||||
interface HydroGlobal {
|
interface HydroGlobal {
|
||||||
vjudge: typeof vjudge;
|
vjudge: typeof vjudge;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { JSDOM } from 'jsdom';
|
|||||||
import { flattenDeep } from 'lodash';
|
import { flattenDeep } from 'lodash';
|
||||||
import superagent from 'superagent';
|
import superagent from 'superagent';
|
||||||
import proxy from 'superagent-proxy';
|
import proxy from 'superagent-proxy';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import {
|
||||||
import { sleep } from '@hydrooj/utils/lib/utils';
|
Logger, SettingModel, sleep, STATUS,
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
} from 'hydrooj';
|
||||||
import * as setting from 'hydrooj/src/model/setting';
|
|
||||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
|
|
||||||
proxy(superagent);
|
proxy(superagent);
|
||||||
@@ -104,7 +103,7 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
|
|
||||||
async submitProblem(id: string, lang: string, code: string, info, next, end) {
|
async submitProblem(id: string, lang: string, code: string, info, next, end) {
|
||||||
let enableO2 = 0;
|
let enableO2 = 0;
|
||||||
const comment = setting.langs[lang]?.comment;
|
const comment = SettingModel.langs[lang]?.comment;
|
||||||
if (code.length < 10) {
|
if (code.length < 10) {
|
||||||
end({ status: STATUS.STATUS_COMPILE_ERROR, message: 'Code too short' });
|
end({ status: STATUS.STATUS_COMPILE_ERROR, message: 'Code too short' });
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -3,12 +3,9 @@ import { PassThrough } from 'stream';
|
|||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import * as superagent from 'superagent';
|
import * as superagent from 'superagent';
|
||||||
import proxy from 'superagent-proxy';
|
import proxy from 'superagent-proxy';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
|
||||||
import {
|
import {
|
||||||
htmlEncode, parseMemoryMB, parseTimeMS, sleep,
|
htmlEncode, Logger, parseMemoryMB, parseTimeMS, SettingModel, sleep, STATUS,
|
||||||
} from '@hydrooj/utils/lib/utils';
|
} from 'hydrooj';
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
|
||||||
import * as setting from 'hydrooj/src/model/setting';
|
|
||||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
import { VERDICT } from '../verdict';
|
import { VERDICT } from '../verdict';
|
||||||
|
|
||||||
@@ -221,7 +218,7 @@ export default class POJProvider implements IBasicProvider {
|
|||||||
async submitProblem(id: string, lang: string, code: string, info) {
|
async submitProblem(id: string, lang: string, code: string, info) {
|
||||||
await this.ensureLogin();
|
await this.ensureLogin();
|
||||||
const language = lang.includes('poj.') ? lang.split('poj.')[1] : '0';
|
const language = lang.includes('poj.') ? lang.split('poj.')[1] : '0';
|
||||||
const comment = setting.langs[lang].comment;
|
const comment = SettingModel.langs[lang].comment;
|
||||||
if (comment) {
|
if (comment) {
|
||||||
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
||||||
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { PassThrough } from 'stream';
|
|||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import superagent from 'superagent';
|
import superagent from 'superagent';
|
||||||
import proxy from 'superagent-proxy';
|
import proxy from 'superagent-proxy';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import {
|
||||||
import { parseMemoryMB, sleep } from '@hydrooj/utils/lib/utils';
|
Logger, parseMemoryMB, SettingModel, sleep, STATUS,
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
} from 'hydrooj';
|
||||||
import * as setting from 'hydrooj/src/model/setting';
|
|
||||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
|
|
||||||
proxy(superagent);
|
proxy(superagent);
|
||||||
@@ -118,7 +117,7 @@ langs: ${JSON.stringify(langs)}`),
|
|||||||
}
|
}
|
||||||
|
|
||||||
async submitProblem(problemcode: string, lang: string, code: string, info, next, end) {
|
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) {
|
if (comment) {
|
||||||
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
||||||
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { PassThrough } from 'stream';
|
|||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import * as superagent from 'superagent';
|
import * as superagent from 'superagent';
|
||||||
import proxy from 'superagent-proxy';
|
import proxy from 'superagent-proxy';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import {
|
||||||
import { parseMemoryMB, parseTimeMS, sleep } from '@hydrooj/utils/lib/utils';
|
Logger, parseMemoryMB, parseTimeMS, SettingModel, sleep, STATUS,
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
} from 'hydrooj';
|
||||||
import * as setting from 'hydrooj/src/model/setting';
|
|
||||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
import { VERDICT } from '../verdict';
|
import { VERDICT } from '../verdict';
|
||||||
|
|
||||||
@@ -165,7 +164,7 @@ export default class UOJProvider implements IBasicProvider {
|
|||||||
async submitProblem(id: string, lang: string, code: string, info) {
|
async submitProblem(id: string, lang: string, code: string, info) {
|
||||||
let programTypeId = lang.includes('uoj.') ? lang.split('uoj.')[1] : 'C++11';
|
let programTypeId = lang.includes('uoj.') ? lang.split('uoj.')[1] : 'C++11';
|
||||||
if (programTypeId === 'Python27') programTypeId = 'Python2.7';
|
if (programTypeId === 'Python27') programTypeId = 'Python2.7';
|
||||||
const comment = setting.langs[lang].comment;
|
const comment = SettingModel.langs[lang].comment;
|
||||||
if (comment) {
|
if (comment) {
|
||||||
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
const msg = `Hydro submission #${info.rid}@${new Date().getTime()}`;
|
||||||
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
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