mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
Hello Typescript!
This commit is contained in:
1 parent
c9b6412527
commit
ba03d7a7bf
98 files changed
+3186
-2616
No files matched your search
+26
-13
@@ -1,7 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
commonjs: true,
|
|
||||||
node: true,
|
node: true,
|
||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
@@ -11,23 +10,37 @@ module.exports = {
|
|||||||
Atomics: 'readonly',
|
Atomics: 'readonly',
|
||||||
SharedArrayBuffer: 'readonly',
|
SharedArrayBuffer: 'readonly',
|
||||||
},
|
},
|
||||||
parserOptions: {
|
parser: '@typescript-eslint/parser',
|
||||||
ecmaVersion: 2018,
|
plugins: ['@typescript-eslint'],
|
||||||
},
|
|
||||||
rules: {
|
rules: {
|
||||||
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
|
'global-require': 'off',
|
||||||
|
'guard-for-in': 'off',
|
||||||
|
'implicit-arrow-linebreak': 'off',
|
||||||
|
'import/extensions': 'off',
|
||||||
|
'import/prefer-default-export': 'off',
|
||||||
indent: ['warn', 4],
|
indent: ['warn', 4],
|
||||||
'no-plusplus': 'off',
|
'max-classes-per-file': 'off',
|
||||||
|
'no-bitwise': 'off',
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'no-extend-native': 'off',
|
'no-extend-native': 'off',
|
||||||
'no-underscore-dangle': 'off',
|
|
||||||
'no-restricted-syntax': 'off',
|
|
||||||
'max-classes-per-file': 'off',
|
|
||||||
'guard-for-in': 'off',
|
|
||||||
'no-param-reassign': 'off',
|
|
||||||
'global-require': 'off',
|
|
||||||
'no-nested-ternary': 'off',
|
|
||||||
'no-multi-assign': 'off',
|
'no-multi-assign': 'off',
|
||||||
|
'no-nested-ternary': 'off',
|
||||||
|
'no-param-reassign': 'off',
|
||||||
|
'no-plusplus': 'off',
|
||||||
|
'no-restricted-syntax': 'off',
|
||||||
'no-return-await': 'off',
|
'no-return-await': 'off',
|
||||||
'no-bitwise': 'off',
|
'no-underscore-dangle': 'off',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts'],
|
||||||
|
},
|
||||||
|
'import/resolver': {
|
||||||
|
typescript: {
|
||||||
|
alwaysTryTypes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -19,7 +19,8 @@ jobs:
|
|||||||
path: .uibuild
|
path: .uibuild
|
||||||
- run: |
|
- run: |
|
||||||
yarn
|
yarn
|
||||||
yarn build:production
|
yarn build
|
||||||
|
yarn pack:production
|
||||||
- uses: actions/upload-artifact@v1
|
- uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: app.js
|
name: app.js
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ __*
|
|||||||
ui/misc/.iconfont
|
ui/misc/.iconfont
|
||||||
ui/static/locale
|
ui/static/locale
|
||||||
*.log
|
*.log
|
||||||
|
*.hydro
|
||||||
!.gitkeep
|
!.gitkeep
|
||||||
+1
-1
@@ -7,7 +7,7 @@ const build = async (type) => {
|
|||||||
const config = {
|
const config = {
|
||||||
mode: type,
|
mode: type,
|
||||||
entry: {
|
entry: {
|
||||||
app: root('hydro/loader.js'),
|
app: root('dist/loader.js'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// TODO check more
|
// TODO check more
|
||||||
|
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const superagent = require('superagent');
|
import superagent from 'superagent';
|
||||||
const system = require('./model/system');
|
import * as system from './model/system';
|
||||||
const db = require('./service/db');
|
import * as db from './service/db';
|
||||||
|
|
||||||
const c = {};
|
const c = {};
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ async function checkDb(log, warn, error) {
|
|||||||
if (!d) await coll.insertOne({ _id: 'check', value: 'check' });
|
if (!d) await coll.insertOne({ _id: 'check', value: 'check' });
|
||||||
await coll.createIndex('check');
|
await coll.createIndex('check');
|
||||||
await coll.deleteOne({ _id: 'check' });
|
await coll.deleteOne({ _id: 'check' });
|
||||||
|
await coll.drop();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error(`Mongo Error: Database read-write failed.\n${e.message}`);
|
error(`Mongo Error: Database read-write failed.\n${e.message}`);
|
||||||
}
|
}
|
||||||
@@ -49,7 +50,7 @@ const checks = [
|
|||||||
checkProxy,
|
checkProxy,
|
||||||
];
|
];
|
||||||
|
|
||||||
async function start(log, warn, error, cb) {
|
export async function start(log, warn, error, cb) {
|
||||||
const id = String.random(6);
|
const id = String.random(6);
|
||||||
cb(id);
|
cb(id);
|
||||||
for (const check of checks) {
|
for (const check of checks) {
|
||||||
@@ -62,8 +63,6 @@ async function start(log, warn, error, cb) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cancel(id) {
|
export async function cancel(id) {
|
||||||
c[id] = true;
|
c[id] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { start, cancel };
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
declare global {
|
||||||
|
namespace NodeJS {
|
||||||
|
interface Global {
|
||||||
|
Hydro: {
|
||||||
|
model: any
|
||||||
|
handler: any
|
||||||
|
script: any
|
||||||
|
service: any
|
||||||
|
lib: any
|
||||||
|
stat: any
|
||||||
|
wiki: any
|
||||||
|
template: any
|
||||||
|
ui: any
|
||||||
|
error: any
|
||||||
|
locales: any
|
||||||
|
}
|
||||||
|
nodeModules: any
|
||||||
|
onDestory: Function[]
|
||||||
|
isFirstWorker: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'cluster' {
|
||||||
|
var isFirstWorker: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { };
|
||||||
@@ -1,35 +1,35 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const yaml = require('js-yaml');
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
const builtinLib = [
|
export const builtinLib = [
|
||||||
'jwt', 'download', 'i18n', 'mail', 'markdown',
|
'jwt', 'download', 'i18n', 'mail', 'markdown',
|
||||||
'md5', 'misc', 'paginate', 'hash.hydro', 'rank',
|
'md5', 'misc', 'paginate', 'hash.hydro', 'rank',
|
||||||
'template', 'validator', 'nav', 'sysinfo', 'testdata.convert.ini',
|
'template', 'validator', 'nav', 'sysinfo', 'testdata.convert.ini',
|
||||||
'readConfig', 'logger', 'useragent',
|
'readConfig', 'logger', 'useragent',
|
||||||
];
|
];
|
||||||
|
|
||||||
const builtinModel = [
|
export const builtinModel = [
|
||||||
'builtin', 'document', 'domain', 'blacklist', 'opcount',
|
'builtin', 'document', 'domain', 'blacklist', 'opcount',
|
||||||
'setting', 'token', 'user', 'problem', 'record',
|
'setting', 'token', 'user', 'problem', 'record',
|
||||||
'contest', 'message', 'solution', 'training', 'file',
|
'contest', 'message', 'solution', 'training', 'file',
|
||||||
'discussion', 'system',
|
'discussion', 'system',
|
||||||
];
|
];
|
||||||
|
|
||||||
const builtinHandler = [
|
export const builtinHandler = [
|
||||||
'home', 'problem', 'record', 'judge', 'user',
|
'home', 'problem', 'record', 'judge', 'user',
|
||||||
'contest', 'training', 'discussion', 'manage', 'import',
|
'contest', 'training', 'discussion', 'manage', 'import',
|
||||||
'misc', 'homework', 'domain', 'wiki',
|
'misc', 'homework', 'domain', 'wiki',
|
||||||
];
|
];
|
||||||
|
|
||||||
const builtinScript = [
|
export const builtinScript = [
|
||||||
'install', 'rating', 'recalcRating', 'register', 'problemStat',
|
'install', 'rating', 'recalcRating', 'register', 'problemStat',
|
||||||
'blacklist', 'setSuperadmin', 'deleteUser',
|
'blacklist', 'setSuperadmin', 'deleteUser',
|
||||||
];
|
];
|
||||||
|
|
||||||
async function handler(pending, fail) {
|
export async function handler(pending, fail) {
|
||||||
for (const i of pending) {
|
for (const i of pending) {
|
||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/handler.js`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/handler.js`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
@@ -46,7 +46,7 @@ async function handler(pending, fail) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function locale(pending, fail) {
|
export async function locale(pending, fail) {
|
||||||
for (const i of pending) {
|
for (const i of pending) {
|
||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/locale.json`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/locale.json`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
@@ -61,7 +61,7 @@ async function locale(pending, fail) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setting(pending, fail, modelSetting, modelSystem) {
|
export async function setting(pending, fail, modelSetting, modelSystem = null) {
|
||||||
const map = {
|
const map = {
|
||||||
system: modelSetting.SystemSetting,
|
system: modelSetting.SystemSetting,
|
||||||
account: modelSetting.AccountSetting,
|
account: modelSetting.AccountSetting,
|
||||||
@@ -71,7 +71,7 @@ async function setting(pending, fail, modelSetting, modelSystem) {
|
|||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/setting.yaml`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/setting.yaml`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
try {
|
try {
|
||||||
const cfg = yaml.safeLoad(fs.readFileSync(p).toString());
|
const cfg: any = yaml.safeLoad(fs.readFileSync(p).toString());
|
||||||
for (const key in cfg) {
|
for (const key in cfg) {
|
||||||
if (cfg[key].default && modelSystem) {
|
if (cfg[key].default && modelSystem) {
|
||||||
const current = await modelSystem.get(`${i}.${key}`);
|
const current = await modelSystem.get(`${i}.${key}`);
|
||||||
@@ -91,7 +91,7 @@ async function setting(pending, fail, modelSetting, modelSystem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function template(pending, fail) {
|
export async function template(pending, fail) {
|
||||||
for (const i of pending) {
|
for (const i of pending) {
|
||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/template.json`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/template.json`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
@@ -106,7 +106,7 @@ async function template(pending, fail) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function model(pending, fail) {
|
export async function model(pending, fail) {
|
||||||
for (const i of pending) {
|
for (const i of pending) {
|
||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/model.js`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/model.js`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
@@ -123,7 +123,7 @@ async function model(pending, fail) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lib(pending, fail) {
|
export async function lib(pending, fail) {
|
||||||
for (const i of pending) {
|
for (const i of pending) {
|
||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/lib.js`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/lib.js`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
@@ -141,7 +141,7 @@ async function lib(pending, fail) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function service(pending, fail) {
|
export async function service(pending, fail) {
|
||||||
for (const i of pending) {
|
for (const i of pending) {
|
||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/service.js`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/service.js`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
@@ -159,7 +159,7 @@ async function service(pending, fail) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function script(pending, fail, active) {
|
export async function script(pending, fail, active) {
|
||||||
for (const i of pending) {
|
for (const i of pending) {
|
||||||
const p = `${os.tmpdir()}/hydro/tmp/${i}/script.js`;
|
const p = `${os.tmpdir()}/hydro/tmp/${i}/script.js`;
|
||||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||||
@@ -176,18 +176,3 @@ async function script(pending, fail, active) {
|
|||||||
active.push(i);
|
active.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
builtinLib,
|
|
||||||
builtinModel,
|
|
||||||
builtinHandler,
|
|
||||||
builtinScript,
|
|
||||||
handler,
|
|
||||||
lib,
|
|
||||||
locale,
|
|
||||||
model,
|
|
||||||
script,
|
|
||||||
service,
|
|
||||||
template,
|
|
||||||
setting,
|
|
||||||
};
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
/* eslint-disable import/no-dynamic-require */
|
/* eslint-disable import/no-dynamic-require */
|
||||||
const {
|
import {
|
||||||
lib, service, model, setting,
|
lib, service, model, setting,
|
||||||
builtinLib, builtinHandler, builtinModel,
|
builtinLib, builtinHandler, builtinModel,
|
||||||
} = require('./common');
|
} from './common';
|
||||||
|
|
||||||
async function load(call) {
|
export async function load(call) {
|
||||||
let pending = await require('../lib/hpm').getInstalled();
|
let pending = await require('../lib/hpm').getInstalled();
|
||||||
const fail = [];
|
const fail = [];
|
||||||
require('../lib/i18n');
|
require('../lib/i18n');
|
||||||
@@ -14,10 +14,10 @@ async function load(call) {
|
|||||||
try {
|
try {
|
||||||
require('../options');
|
require('../options');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await call({ entry: 'install' });
|
await call({ entry: 'setup' });
|
||||||
require('../options');
|
require('../options');
|
||||||
}
|
}
|
||||||
const bus = require('../service/bus');
|
const bus = require('../service/bus').default;
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
const h = () => {
|
const h = () => {
|
||||||
console.log('Database connected');
|
console.log('Database connected');
|
||||||
@@ -32,7 +32,7 @@ async function load(call) {
|
|||||||
require('../service/gridfs');
|
require('../service/gridfs');
|
||||||
require('../service/monitor');
|
require('../service/monitor');
|
||||||
const server = require('../service/server');
|
const server = require('../service/server');
|
||||||
await server.prepare();
|
await server.default.prepare();
|
||||||
await service(pending, fail);
|
await service(pending, fail);
|
||||||
for (const i of builtinModel) require(`../model/${i}`);
|
for (const i of builtinModel) require(`../model/${i}`);
|
||||||
for (const i of builtinHandler) require(`../handler/${i}`);
|
for (const i of builtinHandler) require(`../handler/${i}`);
|
||||||
@@ -62,5 +62,3 @@ async function load(call) {
|
|||||||
pending = [];
|
pending = [];
|
||||||
return await modelSystem.get('server.worker');
|
return await modelSystem.get('server.worker');
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = load;
|
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const http = require('http');
|
import http from 'http';
|
||||||
const Koa = require('koa');
|
import Koa from 'koa';
|
||||||
const Body = require('koa-body');
|
import Body from 'koa-body';
|
||||||
const Router = require('koa-router');
|
import Router from 'koa-router';
|
||||||
const cache = require('koa-static-cache');
|
import cache from 'koa-static-cache';
|
||||||
const nunjucks = require('nunjucks');
|
import nunjucks from 'nunjucks';
|
||||||
const mongodb = require('mongodb');
|
import mongodb from 'mongodb';
|
||||||
|
|
||||||
class Loader {
|
class Loader {
|
||||||
getSource(name) { // eslint-disable-line class-methods-use-this
|
getSource(name: string) { // eslint-disable-line class-methods-use-this
|
||||||
return {
|
return {
|
||||||
src: `
|
src: `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -76,6 +76,7 @@ class Loader {
|
|||||||
</html>
|
</html>
|
||||||
`,
|
`,
|
||||||
path: name,
|
path: name,
|
||||||
|
noCache: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +94,7 @@ function render(name) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load() {
|
export async function load() {
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
const server = http.createServer(app.callback());
|
const server = http.createServer(app.callback());
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
@@ -158,5 +159,3 @@ async function load() {
|
|||||||
});
|
});
|
||||||
return 'Done! Restarting...';
|
return 'Done! Restarting...';
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = load;
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const zlib = require('zlib');
|
import zlib from 'zlib';
|
||||||
const yaml = require('js-yaml');
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
function ensureDir(dir) {
|
function ensureDir(dir) {
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
@@ -30,7 +30,8 @@ for (const i of moduleRoots) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function load() {
|
|
||||||
|
export async function load() {
|
||||||
process.on('unhandledRejection', (e) => {
|
process.on('unhandledRejection', (e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -45,7 +46,7 @@ async function load() {
|
|||||||
try {
|
try {
|
||||||
const f = fs.readFileSync(root(`${moduleRoot}/${file}`));
|
const f = fs.readFileSync(root(`${moduleRoot}/${file}`));
|
||||||
const s = fs.statSync(root(`${moduleRoot}/${file}`));
|
const s = fs.statSync(root(`${moduleRoot}/${file}`));
|
||||||
const m = yaml.safeLoad(zlib.gunzipSync(f));
|
const m: any = yaml.safeLoad(zlib.gunzipSync(f).toString());
|
||||||
const { id } = m;
|
const { id } = m;
|
||||||
if (m.os) {
|
if (m.os) {
|
||||||
if (!m.os.includes(os.platform().toLowerCase())) {
|
if (!m.os.includes(os.platform().toLowerCase())) {
|
||||||
@@ -123,5 +124,3 @@ async function load() {
|
|||||||
console.log('Unzip done.');
|
console.log('Unzip done.');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = load;
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
/* eslint-disable import/no-dynamic-require */
|
/* eslint-disable import/no-dynamic-require */
|
||||||
const {
|
import {
|
||||||
locale, template, lib, service, model, handler, script, setting,
|
locale, template, lib, service, model, handler, script, setting,
|
||||||
builtinLib, builtinScript, builtinHandler, builtinModel,
|
builtinLib, builtinScript, builtinHandler, builtinModel,
|
||||||
} = require('./common');
|
} from './common';
|
||||||
|
|
||||||
async function load() {
|
export async function load() {
|
||||||
let pending = await require('../lib/hpm').getInstalled();
|
let pending = await require('../lib/hpm').getInstalled();
|
||||||
const fail = [];
|
const fail = [];
|
||||||
const active = [];
|
const active = [];
|
||||||
@@ -27,7 +27,7 @@ async function load() {
|
|||||||
for (const i of builtinLib) require(`../lib/${i}`);
|
for (const i of builtinLib) require(`../lib/${i}`);
|
||||||
await lib(pending, fail);
|
await lib(pending, fail);
|
||||||
require('../service/gridfs');
|
require('../service/gridfs');
|
||||||
const server = require('../service/server');
|
const server = require('../service/server').default;
|
||||||
await server.prepare();
|
await server.prepare();
|
||||||
await service(pending, fail);
|
await service(pending, fail);
|
||||||
for (const i of builtinModel) require(`../model/${i}`);
|
for (const i of builtinModel) require(`../model/${i}`);
|
||||||
@@ -40,7 +40,7 @@ async function load() {
|
|||||||
await global.Hydro.handler[i]();
|
await global.Hydro.handler[i]();
|
||||||
}
|
}
|
||||||
const notfound = require('../handler/notfound');
|
const notfound = require('../handler/notfound');
|
||||||
await notfound();
|
await notfound.apply();
|
||||||
for (const i in global.Hydro.service) {
|
for (const i in global.Hydro.service) {
|
||||||
if (global.Hydro.service[i].postInit) {
|
if (global.Hydro.service[i].postInit) {
|
||||||
try {
|
try {
|
||||||
@@ -60,5 +60,3 @@ async function load() {
|
|||||||
}, 30 * 1000);
|
}, 30 * 1000);
|
||||||
return { active, fail };
|
return { active, fail };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = load;
|
|
||||||
-179
@@ -1,179 +0,0 @@
|
|||||||
/* eslint-disable func-names */
|
|
||||||
const { isClass } = require('./utils');
|
|
||||||
|
|
||||||
const Err = (name, ...classMessageCode) => {
|
|
||||||
let Class;
|
|
||||||
let msg;
|
|
||||||
let code;
|
|
||||||
for (const item of classMessageCode) {
|
|
||||||
if (typeof item === 'number') {
|
|
||||||
code = item;
|
|
||||||
} else if (typeof item === 'string') {
|
|
||||||
msg = function () { return item; };
|
|
||||||
} else if (isClass(item)) {
|
|
||||||
Class = item;
|
|
||||||
} else if (typeof item === 'function') {
|
|
||||||
msg = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const HydroError = class extends Class { };
|
|
||||||
HydroError.prototype.name = name;
|
|
||||||
if (msg) HydroError.prototype.msg = msg;
|
|
||||||
if (code) HydroError.prototype.code = code;
|
|
||||||
return HydroError;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HydroError extends Error {
|
|
||||||
constructor(...params) {
|
|
||||||
super();
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserFacingError = Err('UserFacingError', HydroError, 'UserFacingError', 400);
|
|
||||||
const SystemError = Err('SystemError', HydroError, 'SystemError', 500);
|
|
||||||
|
|
||||||
const BadRequestError = Err('BadRequestError', UserFacingError, 'BadRequestError', 400);
|
|
||||||
const ForbiddenError = Err('ForbiddenError', UserFacingError, 'ForbiddenError', 403);
|
|
||||||
const NotFoundError = Err('NotFoundError', UserFacingError, 'NotFoundError', 404);
|
|
||||||
const MethodNotAllowedError = Err('MethodNotAllowedError', UserFacingError, 'MethodNotAllowedError', 405);
|
|
||||||
const RemoteOnlineJudgeError = Err('RemoteOnlineJudgeError', UserFacingError, 'RemoteOnlineJudgeError', 500);
|
|
||||||
|
|
||||||
const AlreadyVotedError = Err('AlreadyVotedError', ForbiddenError, "You've already voted.");
|
|
||||||
const LoginError = Err('LoginError', ForbiddenError, 'Invalid password for user {0}.');
|
|
||||||
const UserAlreadyExistError = Err('UserAlreadyExistError', ForbiddenError, 'User {0} already exists.');
|
|
||||||
const InvalidTokenError = Err('InvalidTokenError', ForbiddenError);
|
|
||||||
const BlacklistedError = Err('BlacklistedError', ForbiddenError, 'Address or user {0} is blacklisted.');
|
|
||||||
const VerifyPasswordError = Err('VerifyPasswordError', ForbiddenError, "Passwords don't match.");
|
|
||||||
const OpcountExceededError = Err('OpcountExceededError', ForbiddenError, 'Too frequent operations of {0} (limit: {2} operations in {1} seconds).');
|
|
||||||
const PermissionError = Err('PermissionError', ForbiddenError, "You don't have the required permission ({0}) in this domain.");
|
|
||||||
const PrivilegeError = Err('PrivilegeError', ForbiddenError, function () {
|
|
||||||
if (this.params.includes(global.Hydro.model.builtin.PRIV.PRIV_USER_PROFILE)) {
|
|
||||||
return "You're not logged in.";
|
|
||||||
}
|
|
||||||
return "You don't have the required privilege.";
|
|
||||||
});
|
|
||||||
const ValidationError = Err('ValidationError', ForbiddenError, function () {
|
|
||||||
if (this.params.length === 1) return 'Field {0} validation failed.';
|
|
||||||
return 'Field {0} or {1} validation failed.';
|
|
||||||
});
|
|
||||||
const ContestNotAttendedError = Err('ContestNotAttendedError', ForbiddenError, "You haven't attended this contest yet.");
|
|
||||||
const ContestAlreadyAttendedError = Err('ContestAlreadyAttendedError', ForbiddenError, "You've already attended this contest.");
|
|
||||||
const ContestNotLiveError = Err('ContestNotLiveError', ForbiddenError, 'This contest is not live.');
|
|
||||||
const ContestScoreboardHiddenError = Err('ContestScoreboardHiddenError', ForbiddenError, 'Contest scoreboard is not visible.');
|
|
||||||
const TrainingAlreadyEnrollError = Err('TrainingAlreadyEnrollError', ForbiddenError, "You've already enrolled this training.");
|
|
||||||
const RoleAlreadyExistError = Err('RoleAlreadyExistError', ForbiddenError, 'This role already exists.');
|
|
||||||
const CsrfTokenError = Err('CsrfTokenError', ForbiddenError, 'CsrfTokenError');
|
|
||||||
|
|
||||||
const UserNotFoundError = Err('UserNotFoundError', NotFoundError, 'User {0} not found.');
|
|
||||||
const NoProblemError = Err('NoProblemError', NotFoundError, 'No problem.');
|
|
||||||
const RecordNotFoundError = Err('RecordNotFoundError', NotFoundError, 'Record {0} not found.');
|
|
||||||
const ProblemDataNotFoundError = Err('ProblemDataNotFoundError', NotFoundError, 'Data of problem {1} not found.');
|
|
||||||
const MessageNotFoundError = Err('MessageNotFoundError', NotFoundError, 'Message {0} not found.');
|
|
||||||
const DocumentNotFoundError = Err('DocumentNotFoundError', NotFoundError, 'Document {2} not found.');
|
|
||||||
|
|
||||||
const ProblemNotFoundError = Err('ProblemNotFountError', DocumentNotFoundError, 'Problem {0} not found.');
|
|
||||||
const SolutionNotFoundError = Err('SolutionNotFoundError', DocumentNotFoundError, 'Solution {0} not found.');
|
|
||||||
const TrainingNotFoundError = Err('TrainingNotFoundError', DocumentNotFoundError, 'Training {0} not found.');
|
|
||||||
const ContestNotFoundError = Err('ContestNotFoundError', DocumentNotFoundError, 'Contest {0} not found.');
|
|
||||||
const DiscussionNotFoundError = Err('DiscussionNotFoundError', DocumentNotFoundError, 'Discussion {0} not found.');
|
|
||||||
const DiscussionNodeNotFoundError = Err('DiscussionNodeNotFoundError', DocumentNotFoundError, 'Discussion node {1} not found.');
|
|
||||||
|
|
||||||
const InvalidOperationError = Err('InvalidOperationError', MethodNotAllowedError);
|
|
||||||
|
|
||||||
global.Hydro.error = module.exports = {
|
|
||||||
Err,
|
|
||||||
HydroError,
|
|
||||||
BadRequestError,
|
|
||||||
BlacklistedError,
|
|
||||||
ForbiddenError,
|
|
||||||
NotFoundError,
|
|
||||||
LoginError,
|
|
||||||
CsrfTokenError,
|
|
||||||
MethodNotAllowedError,
|
|
||||||
InvalidOperationError,
|
|
||||||
UserAlreadyExistError,
|
|
||||||
InvalidTokenError,
|
|
||||||
UserNotFoundError,
|
|
||||||
VerifyPasswordError,
|
|
||||||
ProblemDataNotFoundError,
|
|
||||||
OpcountExceededError,
|
|
||||||
PermissionError,
|
|
||||||
PrivilegeError,
|
|
||||||
NoProblemError,
|
|
||||||
ValidationError,
|
|
||||||
ProblemNotFoundError,
|
|
||||||
TrainingNotFoundError,
|
|
||||||
ContestNotFoundError,
|
|
||||||
RecordNotFoundError,
|
|
||||||
SolutionNotFoundError,
|
|
||||||
AlreadyVotedError,
|
|
||||||
ContestNotAttendedError,
|
|
||||||
ContestNotLiveError,
|
|
||||||
ContestScoreboardHiddenError,
|
|
||||||
ContestAlreadyAttendedError,
|
|
||||||
UserFacingError,
|
|
||||||
SystemError,
|
|
||||||
TrainingAlreadyEnrollError,
|
|
||||||
RemoteOnlineJudgeError,
|
|
||||||
DiscussionNodeNotFoundError,
|
|
||||||
DocumentNotFoundError,
|
|
||||||
DiscussionNotFoundError,
|
|
||||||
RoleAlreadyExistError,
|
|
||||||
MessageNotFoundError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
class FileTooLongError(ValidationError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'The uploaded file is too long.'
|
|
||||||
|
|
||||||
class FileTypeNotAllowedError(ValidationError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'This type of files are not allowed to be uploaded.'
|
|
||||||
|
|
||||||
class InvalidTokenDigestError(ForbiddenError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CurrentPasswordError(ForbiddenError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return "Current password doesn't match."
|
|
||||||
|
|
||||||
class DiscussionCategoryAlreadyExistError(ForbiddenError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'Discussion category {1} already exists.'
|
|
||||||
|
|
||||||
class DiscussionCategoryNotFoundError(NotFoundError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'Discussion category {1} not found.'
|
|
||||||
|
|
||||||
class DiscussionNodeAlreadyExistError(ForbiddenError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'Discussion node {1} already exists.'
|
|
||||||
|
|
||||||
class TrainingRequirementNotSatisfiedError(ForbiddenError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'Training requirement is not satisfied.'
|
|
||||||
|
|
||||||
class UsageExceededError(ForbiddenError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'Usage exceeded.'
|
|
||||||
|
|
||||||
class InvalidArgumentError(BadRequestError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'Argument {0} is invalid.'
|
|
||||||
|
|
||||||
class SendMailError(UserFacingError):
|
|
||||||
@property
|
|
||||||
def message(self):
|
|
||||||
return 'Failed to send mail to {0}.'
|
|
||||||
*/
|
|
||||||
+200
@@ -0,0 +1,200 @@
|
|||||||
|
/* eslint-disable func-names */
|
||||||
|
const { isClass } = require('./utils');
|
||||||
|
|
||||||
|
interface IHydroError {
|
||||||
|
new(...args: any[]): HydroError
|
||||||
|
}
|
||||||
|
|
||||||
|
const Err = (name: string, ...info: Array<IHydroError | Function | string | number>) => {
|
||||||
|
let Class: IHydroError;
|
||||||
|
let msg;
|
||||||
|
let code;
|
||||||
|
for (const item of info) {
|
||||||
|
if (typeof item === 'number') {
|
||||||
|
code = item;
|
||||||
|
} else if (typeof item === 'string') {
|
||||||
|
msg = function () { return item; };
|
||||||
|
} else if (isClass(item)) {
|
||||||
|
// @ts-ignore
|
||||||
|
Class = item;
|
||||||
|
} else if (typeof item === 'function') {
|
||||||
|
msg = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const HydroError = class extends Class { };
|
||||||
|
HydroError.prototype.name = name;
|
||||||
|
if (msg) HydroError.prototype.msg = msg;
|
||||||
|
if (code) HydroError.prototype.code = code;
|
||||||
|
return HydroError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class HydroError extends Error {
|
||||||
|
params: any[];
|
||||||
|
|
||||||
|
constructor(...params: any[]) {
|
||||||
|
super();
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
msg() {
|
||||||
|
return 'HydroError';
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
code() {
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserFacingError = Err('UserFacingError', HydroError, 'UserFacingError', 400);
|
||||||
|
export const SystemError = Err('SystemError', HydroError, 'SystemError', 500);
|
||||||
|
|
||||||
|
export const BadRequestError = Err('BadRequestError', UserFacingError, 'BadRequestError', 400);
|
||||||
|
export const ForbiddenError = Err('ForbiddenError', UserFacingError, 'ForbiddenError', 403);
|
||||||
|
export const NotFoundError = Err('NotFoundError', UserFacingError, 'NotFoundError', 404);
|
||||||
|
export const MethodNotAllowedError = Err('MethodNotAllowedError', UserFacingError, 'MethodNotAllowedError', 405);
|
||||||
|
export const RemoteOnlineJudgeError = Err('RemoteOnlineJudgeError', UserFacingError, 'RemoteOnlineJudgeError', 500);
|
||||||
|
|
||||||
|
export const AlreadyVotedError = Err('AlreadyVotedError', ForbiddenError, "You've already voted.");
|
||||||
|
export const LoginError = Err('LoginError', ForbiddenError, 'Invalid password for user {0}.');
|
||||||
|
export const UserAlreadyExistError = Err('UserAlreadyExistError', ForbiddenError, 'User {0} already exists.');
|
||||||
|
export const InvalidTokenError = Err('InvalidTokenError', ForbiddenError);
|
||||||
|
export const BlacklistedError = Err('BlacklistedError', ForbiddenError, 'Address or user {0} is blacklisted.');
|
||||||
|
export const VerifyPasswordError = Err('VerifyPasswordError', ForbiddenError, "Passwords don't match.");
|
||||||
|
export const OpcountExceededError = Err('OpcountExceededError', ForbiddenError, 'Too frequent operations of {0} (limit: {2} operations in {1} seconds).');
|
||||||
|
export const PermissionError = Err('PermissionError', ForbiddenError, "You don't have the required permission ({0}) in this domain.");
|
||||||
|
export const PrivilegeError = Err('PrivilegeError', ForbiddenError, function () {
|
||||||
|
if (this.params.includes(global.Hydro.model.builtin.PRIV.PRIV_USER_PROFILE)) {
|
||||||
|
return "You're not logged in.";
|
||||||
|
}
|
||||||
|
return "You don't have the required privilege.";
|
||||||
|
});
|
||||||
|
export const ValidationError = Err('ValidationError', ForbiddenError, function () {
|
||||||
|
if (this.params.length === 1) return 'Field {0} validation failed.';
|
||||||
|
return 'Field {0} or {1} validation failed.';
|
||||||
|
});
|
||||||
|
export const ContestNotAttendedError = Err('ContestNotAttendedError', ForbiddenError, "You haven't attended this contest yet.");
|
||||||
|
export const ContestAlreadyAttendedError = Err('ContestAlreadyAttendedError', ForbiddenError, "You've already attended this contest.");
|
||||||
|
export const ContestNotLiveError = Err('ContestNotLiveError', ForbiddenError, 'This contest is not live.');
|
||||||
|
export const ContestScoreboardHiddenError = Err('ContestScoreboardHiddenError', ForbiddenError, 'Contest scoreboard is not visible.');
|
||||||
|
export const TrainingAlreadyEnrollError = Err('TrainingAlreadyEnrollError', ForbiddenError, "You've already enrolled this training.");
|
||||||
|
export const HomeworkNotLiveError = Err('HomeworkNotLiveError', ForbiddenError, 'This homework is not open.');
|
||||||
|
export const HomeworkNotAttendedError = Err('HomeworkNotAttendedError', ForbiddenError, "You haven't claimed this homework yet.");
|
||||||
|
export const RoleAlreadyExistError = Err('RoleAlreadyExistError', ForbiddenError, 'This role already exists.');
|
||||||
|
export const CsrfTokenError = Err('CsrfTokenError', ForbiddenError, 'CsrfTokenError');
|
||||||
|
|
||||||
|
export const UserNotFoundError = Err('UserNotFoundError', NotFoundError, 'User {0} not found.');
|
||||||
|
export const NoProblemError = Err('NoProblemError', NotFoundError, 'No problem.');
|
||||||
|
export const RecordNotFoundError = Err('RecordNotFoundError', NotFoundError, 'Record {0} not found.');
|
||||||
|
export const ProblemDataNotFoundError = Err('ProblemDataNotFoundError', NotFoundError, 'Data of problem {1} not found.');
|
||||||
|
export const MessageNotFoundError = Err('MessageNotFoundError', NotFoundError, 'Message {0} not found.');
|
||||||
|
export const DocumentNotFoundError = Err('DocumentNotFoundError', NotFoundError, 'Document {2} not found.');
|
||||||
|
|
||||||
|
export const ProblemNotFoundError = Err('ProblemNotFountError', DocumentNotFoundError, 'Problem {0} not found.');
|
||||||
|
export const SolutionNotFoundError = Err('SolutionNotFoundError', DocumentNotFoundError, 'Solution {0} not found.');
|
||||||
|
export const TrainingNotFoundError = Err('TrainingNotFoundError', DocumentNotFoundError, 'Training {0} not found.');
|
||||||
|
export const ContestNotFoundError = Err('ContestNotFoundError', DocumentNotFoundError, 'Contest {0} not found.');
|
||||||
|
export const DiscussionNotFoundError = Err('DiscussionNotFoundError', DocumentNotFoundError, 'Discussion {0} not found.');
|
||||||
|
export const DiscussionNodeNotFoundError = Err('DiscussionNodeNotFoundError', DocumentNotFoundError, 'Discussion node {1} not found.');
|
||||||
|
|
||||||
|
export const InvalidOperationError = Err('InvalidOperationError', MethodNotAllowedError);
|
||||||
|
|
||||||
|
global.Hydro.error = {
|
||||||
|
Err,
|
||||||
|
HydroError,
|
||||||
|
BadRequestError,
|
||||||
|
BlacklistedError,
|
||||||
|
ForbiddenError,
|
||||||
|
NotFoundError,
|
||||||
|
LoginError,
|
||||||
|
CsrfTokenError,
|
||||||
|
MethodNotAllowedError,
|
||||||
|
InvalidOperationError,
|
||||||
|
UserAlreadyExistError,
|
||||||
|
InvalidTokenError,
|
||||||
|
UserNotFoundError,
|
||||||
|
VerifyPasswordError,
|
||||||
|
ProblemDataNotFoundError,
|
||||||
|
OpcountExceededError,
|
||||||
|
PermissionError,
|
||||||
|
PrivilegeError,
|
||||||
|
NoProblemError,
|
||||||
|
ValidationError,
|
||||||
|
ProblemNotFoundError,
|
||||||
|
TrainingNotFoundError,
|
||||||
|
ContestNotFoundError,
|
||||||
|
RecordNotFoundError,
|
||||||
|
SolutionNotFoundError,
|
||||||
|
AlreadyVotedError,
|
||||||
|
ContestNotAttendedError,
|
||||||
|
ContestNotLiveError,
|
||||||
|
ContestScoreboardHiddenError,
|
||||||
|
ContestAlreadyAttendedError,
|
||||||
|
UserFacingError,
|
||||||
|
SystemError,
|
||||||
|
TrainingAlreadyEnrollError,
|
||||||
|
HomeworkNotLiveError,
|
||||||
|
HomeworkNotAttendedError,
|
||||||
|
RemoteOnlineJudgeError,
|
||||||
|
DiscussionNodeNotFoundError,
|
||||||
|
DocumentNotFoundError,
|
||||||
|
DiscussionNotFoundError,
|
||||||
|
RoleAlreadyExistError,
|
||||||
|
MessageNotFoundError,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
class FileTooLongError(ValidationError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'The uploaded file is too long.'
|
||||||
|
|
||||||
|
class FileTypeNotAllowedError(ValidationError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'This type of files are not allowed to be uploaded.'
|
||||||
|
|
||||||
|
class InvalidTokenDigestError(ForbiddenError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CurrentPasswordError(ForbiddenError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return "Current password doesn't match."
|
||||||
|
|
||||||
|
class DiscussionCategoryAlreadyExistError(ForbiddenError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'Discussion category {1} already exists.'
|
||||||
|
|
||||||
|
class DiscussionCategoryNotFoundError(NotFoundError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'Discussion category {1} not found.'
|
||||||
|
|
||||||
|
class DiscussionNodeAlreadyExistError(ForbiddenError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'Discussion node {1} already exists.'
|
||||||
|
|
||||||
|
class TrainingRequirementNotSatisfiedError(ForbiddenError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'Training requirement is not satisfied.'
|
||||||
|
|
||||||
|
class UsageExceededError(ForbiddenError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'Usage exceeded.'
|
||||||
|
|
||||||
|
class InvalidArgumentError(BadRequestError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'Argument {0} is invalid.'
|
||||||
|
|
||||||
|
class SendMailError(UserFacingError):
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return 'Failed to send mail to {0}.'
|
||||||
|
*/
|
||||||
@@ -1,27 +1,19 @@
|
|||||||
const moment = require('moment-timezone');
|
import moment from 'moment-timezone';
|
||||||
const { ObjectID } = require('mongodb');
|
import { ObjectID } from 'mongodb';
|
||||||
const AdmZip = require('adm-zip');
|
import AdmZip from 'adm-zip';
|
||||||
const {
|
import {
|
||||||
ContestNotLiveError, ValidationError, ProblemNotFoundError,
|
ContestNotLiveError, ValidationError, ProblemNotFoundError,
|
||||||
ContestNotAttendedError,
|
ContestNotAttendedError,
|
||||||
} = require('../error');
|
} from '../error';
|
||||||
const paginate = require('../lib/paginate');
|
import paginate from '../lib/paginate';
|
||||||
const {
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
PERM: {
|
import * as contest from '../model/contest';
|
||||||
PERM_CREATE_CONTEST, PERM_EDIT_CONTEST, PERM_READ_RECORD_CODE,
|
import * as document from '../model/document';
|
||||||
PERM_VIEW_CONTEST, PERM_EDIT_CONTEST_SELF,
|
import * as problem from '../model/problem';
|
||||||
},
|
import * as record from '../model/record';
|
||||||
PRIV: {
|
import * as user from '../model/user';
|
||||||
PRIV_READ_RECORD_CODE,
|
import * as system from '../model/system';
|
||||||
},
|
import { Route, Handler } from '../service/server';
|
||||||
} = require('../model/builtin');
|
|
||||||
const contest = require('../model/contest');
|
|
||||||
const document = require('../model/document');
|
|
||||||
const problem = require('../model/problem');
|
|
||||||
const record = require('../model/record');
|
|
||||||
const user = require('../model/user');
|
|
||||||
const system = require('../model/system');
|
|
||||||
const { Route, Handler } = require('../service/server');
|
|
||||||
|
|
||||||
const ContestHandler = contest.ContestHandlerMixin(Handler);
|
const ContestHandler = contest.ContestHandlerMixin(Handler);
|
||||||
|
|
||||||
@@ -35,7 +27,7 @@ class ContestListHandler extends ContestHandler {
|
|||||||
tdocs = contest.getMulti(domainId).sort({ beginAt: -1 });
|
tdocs = contest.getMulti(domainId).sort({ beginAt: -1 });
|
||||||
qs = '';
|
qs = '';
|
||||||
} else {
|
} else {
|
||||||
if (!contest.CONTEST_RULES.includes(rule)) throw new ValidationError('rule');
|
if (!contest.RULES[rule]) throw new ValidationError('rule');
|
||||||
tdocs = contest.getMulti(domainId, { rule }).sort({ beginAt: -1 });
|
tdocs = contest.getMulti(domainId, { rule }).sort({ beginAt: -1 });
|
||||||
qs = 'rule={0}'.format(rule);
|
qs = 'rule={0}'.format(rule);
|
||||||
}
|
}
|
||||||
@@ -127,8 +119,8 @@ class ContestScoreboardDownloadHandler extends ContestDetailHandler {
|
|||||||
|
|
||||||
class ContestEditHandler extends ContestDetailHandler {
|
class ContestEditHandler extends ContestDetailHandler {
|
||||||
async prepare() {
|
async prepare() {
|
||||||
if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_CONTEST);
|
if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_CONTEST);
|
||||||
else this.checkPerm(PERM_EDIT_CONTEST_SELF);
|
else this.checkPerm(PERM.PERM_EDIT_CONTEST_SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get() {
|
async get() {
|
||||||
@@ -160,20 +152,20 @@ class ContestEditHandler extends ContestDetailHandler {
|
|||||||
async post({
|
async post({
|
||||||
domainId, beginAtDate, beginAtTime, duration, title, content, rule, pids, rated = false,
|
domainId, beginAtDate, beginAtTime, duration, title, content, rule, pids, rated = false,
|
||||||
}) {
|
}) {
|
||||||
let beginAt = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
const beginAtMoment = moment.tz(`${beginAtDate} ${beginAtTime}`, this.user.timeZone);
|
||||||
if (!beginAt.isValid()) {
|
if (!beginAtMoment.isValid()) {
|
||||||
throw new ValidationError('beginAtDate', 'beginAtTime');
|
throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||||
}
|
}
|
||||||
const endAt = beginAt.clone().add(duration, 'hours').toDate();
|
const endAt = beginAtMoment.clone().add(duration, 'hours').toDate();
|
||||||
if (beginAt.isSameOrAfter(endAt)) throw new ValidationError('duration');
|
if (beginAtMoment.isSameOrAfter(endAt)) throw new ValidationError('duration');
|
||||||
beginAt = beginAt.toDate();
|
const beginAt = beginAtMoment.toDate();
|
||||||
pids = await this.verifyProblems(domainId, pids);
|
pids = await this.verifyProblems(domainId, pids);
|
||||||
await contest.edit(domainId, this.tdoc.docId, {
|
await contest.edit(domainId, this.tdoc.docId, {
|
||||||
title, content, rule, beginAt, endAt, pids, rated,
|
title, content, rule, beginAt, endAt, pids, rated,
|
||||||
});
|
});
|
||||||
if (this.tdoc.beginAt !== beginAt || this.tdoc.endAt !== endAt
|
if (this.tdoc.beginAt !== beginAt || this.tdoc.endAt !== endAt
|
||||||
|| Array.isDiff(this.tdoc.pids, pids) || this.tdoc.rule !== rule) {
|
|| Array.isDiff(this.tdoc.pids, pids) || this.tdoc.rule !== rule) {
|
||||||
await contest.recalcStatus(this.tdoc.docId);
|
await contest.recalcStatus(domainId, this.tdoc.docId);
|
||||||
}
|
}
|
||||||
this.response.body = { tid: this.tdoc.docId };
|
this.response.body = { tid: this.tdoc.docId };
|
||||||
this.response.redirect = this.url('contest_detail', { tid: this.tdoc.docId });
|
this.response.redirect = this.url('contest_detail', { tid: this.tdoc.docId });
|
||||||
@@ -201,6 +193,7 @@ class ContestProblemHandler extends ContestDetailHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
async get({ tid }) {
|
async get({ tid }) {
|
||||||
const path = [
|
const path = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
@@ -272,7 +265,9 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
|
|||||||
|
|
||||||
class ContestCodeHandler extends ContestDetailHandler {
|
class ContestCodeHandler extends ContestDetailHandler {
|
||||||
async get({ domainId, tid }) {
|
async get({ domainId, tid }) {
|
||||||
if (!this.user.hasPriv(PRIV_READ_RECORD_CODE)) this.checkPerm(PERM_READ_RECORD_CODE);
|
if (!this.user.hasPriv(PRIV.PRIV_READ_RECORD_CODE)) {
|
||||||
|
this.checkPerm(PERM.PERM_READ_RECORD_CODE);
|
||||||
|
}
|
||||||
this.limitRate('homework_code', 3600, 60);
|
this.limitRate('homework_code', 3600, 60);
|
||||||
const [tdoc, tsdocs] = await contest.getAndListStatus(domainId, tid);
|
const [tdoc, tsdocs] = await contest.getAndListStatus(domainId, tid);
|
||||||
const rnames = {};
|
const rnames = {};
|
||||||
@@ -329,23 +324,23 @@ class ContestCreateHandler extends ContestHandler {
|
|||||||
pids = await this.verifyProblems(domainId, pids);
|
pids = await this.verifyProblems(domainId, pids);
|
||||||
const tid = await contest.add(
|
const tid = await contest.add(
|
||||||
domainId, title, content,
|
domainId, title, content,
|
||||||
this.user._id, rule, beginAt, endAt, pids, rated,
|
this.user._id, rule, beginAt.toDate(), endAt.toDate(), pids, rated,
|
||||||
);
|
);
|
||||||
this.response.body = { tid };
|
this.response.body = { tid };
|
||||||
this.response.redirect = this.url('contest_detail', { tid });
|
this.response.redirect = this.url('contest_detail', { tid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('contest_main', '/contest', ContestListHandler, PERM_VIEW_CONTEST);
|
Route('contest_main', '/contest', ContestListHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_detail', '/contest/:tid', ContestDetailHandler, PERM_VIEW_CONTEST);
|
Route('contest_detail', '/contest/:tid', ContestDetailHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_edit', '/contest/:tid/edit', ContestEditHandler, PERM_VIEW_CONTEST);
|
Route('contest_edit', '/contest/:tid/edit', ContestEditHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_scoreboard', '/contest/:tid/scoreboard', ContestScoreboardHandler, PERM_VIEW_CONTEST);
|
Route('contest_scoreboard', '/contest/:tid/scoreboard', ContestScoreboardHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_scoreboard_download', '/contest/:tid/export/:ext', ContestScoreboardDownloadHandler, PERM_VIEW_CONTEST);
|
Route('contest_scoreboard_download', '/contest/:tid/export/:ext', ContestScoreboardDownloadHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_detail_problem', '/contest/:tid/p/:pid', ContestProblemHandler, PERM_VIEW_CONTEST);
|
Route('contest_detail_problem', '/contest/:tid/p/:pid', ContestProblemHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_detail_problem_submit', '/contest/:tid/p/:pid/submit', ContestProblemSubmitHandler, PERM_VIEW_CONTEST);
|
Route('contest_detail_problem_submit', '/contest/:tid/p/:pid/submit', ContestProblemSubmitHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_code', '/contest/:tid/code', ContestCodeHandler, PERM_VIEW_CONTEST);
|
Route('contest_code', '/contest/:tid/code', ContestCodeHandler, PERM.PERM_VIEW_CONTEST);
|
||||||
Route('contest_create', '/contest/create', ContestCreateHandler, PERM_CREATE_CONTEST);
|
Route('contest_create', '/contest/create', ContestCreateHandler, PERM.PERM_CREATE_CONTEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.contest = module.exports = apply;
|
global.Hydro.handler.contest = apply;
|
||||||
@@ -1,23 +1,14 @@
|
|||||||
const { ObjectID } = require('mongodb');
|
import { ObjectID } from 'mongodb';
|
||||||
const paginate = require('../lib/paginate');
|
import { DiscussionNotFoundError, DocumentNotFoundError } from '../error';
|
||||||
const system = require('../model/system');
|
import paginate from '../lib/paginate';
|
||||||
const user = require('../model/user');
|
import * as system from '../model/system';
|
||||||
const discussion = require('../model/discussion');
|
import * as user from '../model/user';
|
||||||
const document = require('../model/document');
|
import * as discussion from '../model/discussion';
|
||||||
const {
|
import * as document from '../model/document';
|
||||||
PERM: {
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
PERM_VIEW_DISCUSSION, PERM_EDIT_DISCUSSION, PERM_EDIT_DISCUSSION_REPLY,
|
import { Route, Handler } from '../service/server';
|
||||||
PERM_DELETE_DISCUSSION, PERM_DELETE_DISCUSSION_REPLY, PERM_HIGHLIGHT_DISCUSSION,
|
|
||||||
PERM_CREATE_DISCUSSION, PERM_REPLY_DISCUSSION,
|
|
||||||
},
|
|
||||||
PRIV: {
|
|
||||||
PRIV_USER_PROFILE,
|
|
||||||
},
|
|
||||||
} = require('../model/builtin');
|
|
||||||
const { Route, Handler } = require('../service/server');
|
|
||||||
const { DiscussionNotFoundError, DocumentNotFoundError } = require('../error');
|
|
||||||
|
|
||||||
const typeMapper = {
|
export const typeMapper = {
|
||||||
problem: document.TYPE_PROBLEM,
|
problem: document.TYPE_PROBLEM,
|
||||||
contest: document.TYPE_CONTEST,
|
contest: document.TYPE_CONTEST,
|
||||||
node: document.TYPE_DISCUSSION_NODE,
|
node: document.TYPE_DISCUSSION_NODE,
|
||||||
@@ -29,7 +20,7 @@ class DiscussionHandler extends Handler {
|
|||||||
async _prepare({
|
async _prepare({
|
||||||
domainId, type, name, did, drid, drrid,
|
domainId, type, name, did, drid, drrid,
|
||||||
}) {
|
}) {
|
||||||
this.checkPerm(PERM_VIEW_DISCUSSION);
|
this.checkPerm(PERM.PERM_VIEW_DISCUSSION);
|
||||||
if (did) {
|
if (did) {
|
||||||
this.ddoc = await discussion.get(domainId, did);
|
this.ddoc = await discussion.get(domainId, did);
|
||||||
if (!this.ddoc) throw new DiscussionNotFoundError(did);
|
if (!this.ddoc) throw new DiscussionNotFoundError(did);
|
||||||
@@ -124,8 +115,8 @@ class DiscussionNodeHandler extends DiscussionHandler {
|
|||||||
|
|
||||||
class DiscussionCreateHandler extends DiscussionHandler {
|
class DiscussionCreateHandler extends DiscussionHandler {
|
||||||
async prepare() {
|
async prepare() {
|
||||||
this.checkPriv(PRIV_USER_PROFILE);
|
this.checkPriv(PRIV.PRIV_USER_PROFILE);
|
||||||
this.checkPerm(PERM_CREATE_DISCUSSION);
|
this.checkPerm(PERM.PERM_CREATE_DISCUSSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get({ type, name }) {
|
async get({ type, name }) {
|
||||||
@@ -144,7 +135,7 @@ class DiscussionCreateHandler extends DiscussionHandler {
|
|||||||
}) {
|
}) {
|
||||||
this.limitRate('add_discussion', 3600, 30);
|
this.limitRate('add_discussion', 3600, 30);
|
||||||
if (ObjectID.isValid(name)) name = new ObjectID(name);
|
if (ObjectID.isValid(name)) name = new ObjectID(name);
|
||||||
if (highlight) this.checkPerm(PERM_HIGHLIGHT_DISCUSSION);
|
if (highlight) this.checkPerm(PERM.PERM_HIGHLIGHT_DISCUSSION);
|
||||||
const did = await discussion.add(
|
const did = await discussion.add(
|
||||||
domainId, typeMapper[type], name, this.user._id,
|
domainId, typeMapper[type], name, this.user._id,
|
||||||
title, content, this.request.ip, highlight,
|
title, content, this.request.ip, highlight,
|
||||||
@@ -156,7 +147,7 @@ class DiscussionCreateHandler extends DiscussionHandler {
|
|||||||
|
|
||||||
class DiscussionDetailHandler extends DiscussionHandler {
|
class DiscussionDetailHandler extends DiscussionHandler {
|
||||||
async get({ domainId, did, page = 1 }) {
|
async get({ domainId, did, page = 1 }) {
|
||||||
const dsdoc = this.user.hasPriv(PRIV_USER_PROFILE)
|
const dsdoc = this.user.hasPriv(PRIV.PRIV_USER_PROFILE)
|
||||||
? await discussion.getStatus(domainId, did, this.user._id)
|
? await discussion.getStatus(domainId, did, this.user._id)
|
||||||
: null;
|
: null;
|
||||||
const [drdocs, pcount, drcount] = await paginate(
|
const [drdocs, pcount, drcount] = await paginate(
|
||||||
@@ -187,31 +178,31 @@ class DiscussionDetailHandler extends DiscussionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async post() {
|
async post() {
|
||||||
this.checkPriv(PRIV_USER_PROFILE);
|
this.checkPriv(PRIV.PRIV_USER_PROFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
async postReply({ domainId, did, content }) {
|
async postReply({ domainId, did, content }) {
|
||||||
this.checkPerm(PERM_REPLY_DISCUSSION);
|
this.checkPerm(PERM.PERM_REPLY_DISCUSSION);
|
||||||
this.limitRate('add_discussion', 3600, 30);
|
this.limitRate('add_discussion', 3600, 30);
|
||||||
await discussion.addReply(domainId, did, this.user._id, content, this.request.ip);
|
await discussion.addReply(domainId, did, this.user._id, content, this.request.ip);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postTailReply({ domainId, drid, content }) {
|
async postTailReply({ domainId, drid, content }) {
|
||||||
this.checkPerm(PERM_REPLY_DISCUSSION);
|
this.checkPerm(PERM.PERM_REPLY_DISCUSSION);
|
||||||
this.limitRate('add_discussion', 3600, 30);
|
this.limitRate('add_discussion', 3600, 30);
|
||||||
await discussion.addTailReply(domainId, drid, this.user._id, content, this.request.ip);
|
await discussion.addTailReply(domainId, drid, this.user._id, content, this.request.ip);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postEditReply({ domainId, drid, content }) {
|
async postEditReply({ domainId, drid, content }) {
|
||||||
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION_REPLY);
|
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY);
|
||||||
await discussion.editReply(domainId, drid, content);
|
await discussion.editReply(domainId, drid, content);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postDeleteReply({ domainId, drid }) {
|
async postDeleteReply({ domainId, drid }) {
|
||||||
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY);
|
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY);
|
||||||
await discussion.delReply(domainId, drid);
|
await discussion.delReply(domainId, drid);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
@@ -219,13 +210,13 @@ class DiscussionDetailHandler extends DiscussionHandler {
|
|||||||
async postEditTailReply({
|
async postEditTailReply({
|
||||||
domainId, drid, drrid, content,
|
domainId, drid, drrid, content,
|
||||||
}) {
|
}) {
|
||||||
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION_REPLY);
|
if (this.drdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION_REPLY);
|
||||||
await discussion.editTailReply(domainId, drid, drrid, content);
|
await discussion.editTailReply(domainId, drid, drrid, content);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postDeleteTailReply({ domainId, drid, drrid }) {
|
async postDeleteTailReply({ domainId, drid, drrid }) {
|
||||||
if (this.drrdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION_REPLY);
|
if (this.drrdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_DISCUSSION_REPLY);
|
||||||
await discussion.delTailReply(domainId, drid, drrid);
|
await discussion.delTailReply(domainId, drid, drrid);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
@@ -278,15 +269,15 @@ class DiscussionEditHandler extends DiscussionHandler {
|
|||||||
async postUpdate({
|
async postUpdate({
|
||||||
domainId, did, title, content, highlight,
|
domainId, did, title, content, highlight,
|
||||||
}) {
|
}) {
|
||||||
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION);
|
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_DISCUSSION);
|
||||||
if (highlight && !this.ddoc.highlight) this.checkPerm(PERM_HIGHLIGHT_DISCUSSION);
|
if (highlight && !this.ddoc.highlight) this.checkPerm(PERM.PERM_HIGHLIGHT_DISCUSSION);
|
||||||
await discussion.edit(domainId, did, title, content, highlight);
|
await discussion.edit(domainId, did, title, content, highlight);
|
||||||
this.response.body = { did };
|
this.response.body = { did };
|
||||||
this.response.redirect = this.url('discussion_detail', { did });
|
this.response.redirect = this.url('discussion_detail', { did });
|
||||||
}
|
}
|
||||||
|
|
||||||
async postDelete({ domainId, did }) {
|
async postDelete({ domainId, did }) {
|
||||||
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_DISCUSSION);
|
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_DISCUSSION);
|
||||||
await discussion.del(domainId, did);
|
await discussion.del(domainId, did);
|
||||||
this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId };
|
this.response.body = { type: this.ddoc.parentType, parent: this.ddoc.parentId };
|
||||||
this.response.redirect = this.url('discussion_node', {
|
this.response.redirect = this.url('discussion_node', {
|
||||||
@@ -296,7 +287,7 @@ class DiscussionEditHandler extends DiscussionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('discussion_main', '/discuss', DiscussionMainHandler);
|
Route('discussion_main', '/discuss', DiscussionMainHandler);
|
||||||
Route('discussion_detail', '/discuss/:did', DiscussionDetailHandler);
|
Route('discussion_detail', '/discuss/:did', DiscussionDetailHandler);
|
||||||
Route('discussion_edit', '/discuss/:did/edit', DiscussionEditHandler);
|
Route('discussion_edit', '/discuss/:did/edit', DiscussionEditHandler);
|
||||||
@@ -307,4 +298,4 @@ async function apply() {
|
|||||||
Route('discussion_create', '/discuss/:type/:name/create', DiscussionCreateHandler);
|
Route('discussion_create', '/discuss/:type/:name/create', DiscussionCreateHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.discussion = module.exports = apply;
|
global.Hydro.handler.discussion = apply;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
const user = require('../model/user');
|
import { RoleAlreadyExistError, ValidationError, PermissionError } from '../error';
|
||||||
const domain = require('../model/domain');
|
import * as user from '../model/user';
|
||||||
const system = require('../model/system');
|
import * as domain from '../model/domain';
|
||||||
const { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } = require('../model/setting');
|
import * as system from '../model/system';
|
||||||
const { PERM: { PERM_EDIT_DOMAIN }, PERMS_BY_FAMILY } = require('../model/builtin');
|
import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
|
||||||
const paginate = require('../lib/paginate');
|
import { PERM, PERMS_BY_FAMILY } from '../model/builtin';
|
||||||
const { Route, Handler } = require('../service/server');
|
import paginate from '../lib/paginate';
|
||||||
const { RoleAlreadyExistError, ValidationError, PermissionError } = require('../error');
|
import { Route, Handler } from '../service/server';
|
||||||
|
|
||||||
class DomainRankHandler extends Handler {
|
class DomainRankHandler extends Handler {
|
||||||
async get({ domainId, page = 1 }) {
|
async get({ domainId, page = 1 }) {
|
||||||
@@ -55,7 +55,7 @@ class DomainCreateHandler extends Handler {
|
|||||||
|
|
||||||
class ManageHandler extends Handler {
|
class ManageHandler extends Handler {
|
||||||
async prepare({ domainId }) {
|
async prepare({ domainId }) {
|
||||||
this.checkPerm(PERM_EDIT_DOMAIN);
|
this.checkPerm(PERM.PERM_EDIT_DOMAIN);
|
||||||
this.domain = await domain.get(domainId);
|
this.domain = await domain.get(domainId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,18 +178,18 @@ class DomainRoleHandler extends ManageHandler {
|
|||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postDelete({ roles }) {
|
async postDelete({ domainId, roles }) {
|
||||||
for (const role of roles) {
|
for (const role of roles) {
|
||||||
if (['root', 'default', 'guest'].includes(role)) {
|
if (['root', 'default', 'guest'].includes(role)) {
|
||||||
throw new ValidationError('role');
|
throw new ValidationError('role');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await user.deleteRoles(roles);
|
await user.deleteRoles(domainId, roles);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('ranking', '/ranking', DomainRankHandler);
|
Route('ranking', '/ranking', DomainRankHandler);
|
||||||
Route('domain_create', '/domain/create', DomainCreateHandler);
|
Route('domain_create', '/domain/create', DomainCreateHandler);
|
||||||
Route('domain_dashboard', '/domain/dashboard', DomainDashboardHandler);
|
Route('domain_dashboard', '/domain/dashboard', DomainDashboardHandler);
|
||||||
@@ -199,4 +199,4 @@ async function apply() {
|
|||||||
Route('domain_role', '/domain/role', DomainRoleHandler);
|
Route('domain_role', '/domain/role', DomainRoleHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.domain = module.exports = apply;
|
global.Hydro.handler.domain = apply;
|
||||||
@@ -1,40 +1,32 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const {
|
import {
|
||||||
VerifyPasswordError, UserAlreadyExistError, InvalidTokenError,
|
VerifyPasswordError, UserAlreadyExistError, InvalidTokenError,
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
} = require('../error');
|
} from '../error';
|
||||||
const bus = require('../service/bus');
|
import * as bus from '../service/bus';
|
||||||
const {
|
import {
|
||||||
Route, Connection, Handler, ConnectionHandler,
|
Route, Connection, Handler, ConnectionHandler,
|
||||||
} = require('../service/server');
|
} from '../service/server';
|
||||||
const misc = require('../lib/misc');
|
import * as misc from '../lib/misc';
|
||||||
const md5 = require('../lib/md5');
|
import md5 from '../lib/md5';
|
||||||
const contest = require('../model/contest');
|
import * as contest from '../model/contest';
|
||||||
const message = require('../model/message');
|
import * as message from '../model/message';
|
||||||
const document = require('../model/document');
|
import * as document from '../model/document';
|
||||||
const system = require('../model/system');
|
import * as system from '../model/system';
|
||||||
const user = require('../model/user');
|
import * as user from '../model/user';
|
||||||
const file = require('../model/file');
|
import * as file from '../model/file';
|
||||||
const setting = require('../model/setting');
|
import * as setting from '../model/setting';
|
||||||
const domain = require('../model/domain');
|
import * as domain from '../model/domain';
|
||||||
const discussion = require('../model/discussion');
|
import * as discussion from '../model/discussion';
|
||||||
const token = require('../model/token');
|
import * as token from '../model/token';
|
||||||
const training = require('../model/training');
|
import * as training from '../model/training';
|
||||||
const {
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
PERM: {
|
|
||||||
PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION,
|
|
||||||
PERM_VIEW_HOMEWORK,
|
|
||||||
},
|
|
||||||
PRIV: {
|
|
||||||
PRIV_USER_PROFILE, PRIV_DELETE_FILE, PRIV_DELETE_FILE_SELF,
|
|
||||||
},
|
|
||||||
} = require('../model/builtin');
|
|
||||||
|
|
||||||
const { geoip, useragent } = global.Hydro.lib;
|
const { geoip, useragent } = global.Hydro.lib;
|
||||||
|
|
||||||
class HomeHandler extends Handler {
|
class HomeHandler extends Handler {
|
||||||
async homework(domainId) {
|
async homework(domainId) {
|
||||||
if (this.user.hasPerm(PERM_VIEW_HOMEWORK)) {
|
if (this.user.hasPerm(PERM.PERM_VIEW_HOMEWORK)) {
|
||||||
const tdocs = await contest.getMulti(domainId, {}, document.TYPE_HOMEWORK)
|
const tdocs = await contest.getMulti(domainId, {}, document.TYPE_HOMEWORK)
|
||||||
.sort('beginAt', -1)
|
.sort('beginAt', -1)
|
||||||
.limit(await system.get('HOMEWORK_ON_MAIN'))
|
.limit(await system.get('HOMEWORK_ON_MAIN'))
|
||||||
@@ -49,7 +41,7 @@ class HomeHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async contest(domainId) {
|
async contest(domainId) {
|
||||||
if (this.user.hasPerm(PERM_VIEW_CONTEST)) {
|
if (this.user.hasPerm(PERM.PERM_VIEW_CONTEST)) {
|
||||||
const tdocs = await contest.getMulti(domainId)
|
const tdocs = await contest.getMulti(domainId)
|
||||||
.sort('beginAt', -1)
|
.sort('beginAt', -1)
|
||||||
.limit(await system.get('CONTESTS_ON_MAIN'))
|
.limit(await system.get('CONTESTS_ON_MAIN'))
|
||||||
@@ -63,7 +55,7 @@ class HomeHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async training(domainId) {
|
async training(domainId) {
|
||||||
if (this.user.hasPerm(PERM_VIEW_TRAINING)) {
|
if (this.user.hasPerm(PERM.PERM_VIEW_TRAINING)) {
|
||||||
const tdocs = await training.getMulti(domainId)
|
const tdocs = await training.getMulti(domainId)
|
||||||
.sort('_id', 1)
|
.sort('_id', 1)
|
||||||
.limit(await system.get('TRAININGS_ON_MAIN'))
|
.limit(await system.get('TRAININGS_ON_MAIN'))
|
||||||
@@ -76,8 +68,8 @@ class HomeHandler extends Handler {
|
|||||||
return [[], {}];
|
return [[], {}];
|
||||||
}
|
}
|
||||||
|
|
||||||
async discussion(domainId) {
|
async discussion(domainId): Promise<[any[], any]> {
|
||||||
if (this.user.hasPerm(PERM_VIEW_DISCUSSION)) {
|
if (this.user.hasPerm(PERM.PERM_VIEW_DISCUSSION)) {
|
||||||
const ddocs = await discussion.getMulti(domainId)
|
const ddocs = await discussion.getMulti(domainId)
|
||||||
.limit(await system.get('DISCUSSIONS_ON_MAIN'))
|
.limit(await system.get('DISCUSSIONS_ON_MAIN'))
|
||||||
.toArray();
|
.toArray();
|
||||||
@@ -147,10 +139,10 @@ class HomeSecurityHandler extends Handler {
|
|||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postChangeMail({ currentPassword, mail }) {
|
async postChangeMail({ domainId, currentPassword, mail }) {
|
||||||
this.limitRate('send_mail', 3600, 30);
|
this.limitRate('send_mail', 3600, 30);
|
||||||
this.user.checkPassword(currentPassword);
|
this.user.checkPassword(currentPassword);
|
||||||
const udoc = await user.getByMail(mail);
|
const udoc = await user.getByEmail(domainId, mail, true);
|
||||||
if (udoc) throw new UserAlreadyExistError(mail);
|
if (udoc) throw new UserAlreadyExistError(mail);
|
||||||
const [code] = await token.add(
|
const [code] = await token.add(
|
||||||
token.TYPE_CHANGEMAIL,
|
token.TYPE_CHANGEMAIL,
|
||||||
@@ -216,12 +208,12 @@ class HomeSettingsHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UserChangemailWithCodeHandler extends Handler {
|
class UserChangemailWithCodeHandler extends Handler {
|
||||||
async get({ code }) {
|
async get({ domainId, code }) {
|
||||||
const tdoc = await token.get(code, token.TYPE_CHANGEMAIL);
|
const tdoc = await token.get(code, token.TYPE_CHANGEMAIL);
|
||||||
if (!tdoc || tdoc.uid !== this.user._id) {
|
if (!tdoc || tdoc.uid !== this.user._id) {
|
||||||
throw new InvalidTokenError(code);
|
throw new InvalidTokenError(code);
|
||||||
}
|
}
|
||||||
const udoc = await user.getByEmail(tdoc.mail);
|
const udoc = await user.getByEmail(domainId, tdoc.mail, true);
|
||||||
if (udoc) throw new UserAlreadyExistError(tdoc.mail);
|
if (udoc) throw new UserAlreadyExistError(tdoc.mail);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
user.setEmail(this.user._id, tdoc.mail),
|
user.setEmail(this.user._id, tdoc.mail),
|
||||||
@@ -271,7 +263,7 @@ class HomeMessagesHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async postDeleteMessage({ messageId }) {
|
async postDeleteMessage({ messageId }) {
|
||||||
await message.delete(messageId, this.user._id);
|
await message.del(messageId);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,29 +285,29 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
|
|||||||
class HomeFileHandler extends Handler {
|
class HomeFileHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
const ufdocs = await file.getMulti({ owner: this.user._id }).toArray();
|
const ufdocs = await file.getMulti({ owner: this.user._id }).toArray();
|
||||||
const fdict = await fs.getMetaDict(ufdocs.map((ufdoc) => ufdoc._id));
|
const fdict = await file.getMetaDict(ufdocs.map((ufdoc) => ufdoc._id));
|
||||||
this.response.template = 'home_file.html';
|
this.response.template = 'home_file.html';
|
||||||
this.response.body = { ufdocs, fdict };
|
this.response.body = { ufdocs, fdict };
|
||||||
}
|
}
|
||||||
|
|
||||||
async postDelete(ufid) {
|
async postDelete(ufid) {
|
||||||
const ufdoc = await file.get(ufid);
|
const ufdoc = await file.getMeta(ufid);
|
||||||
if (ufdoc.owner !== this.user._id) this.checkPriv(PRIV_DELETE_FILE);
|
if (ufdoc.owner !== this.user._id) this.checkPriv(PRIV.PRIV_DELETE_FILE);
|
||||||
else this.checkPriv(PRIV_DELETE_FILE_SELF);
|
else this.checkPriv(PRIV.PRIV_DELETE_FILE_SELF);
|
||||||
const result = await file.del(ufdoc._id);
|
const result = await file.del(ufdoc._id);
|
||||||
if (result) await file.decUsage(this.user._id, ufdoc.length);
|
if (result) await user.inc(this.user._id, 'usage', ufdoc.length);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
async function apply() {
|
||||||
Route('homepage', '/', HomeHandler);
|
Route('homepage', '/', HomeHandler);
|
||||||
Route('home_security', '/home/security', HomeSecurityHandler, PRIV_USER_PROFILE);
|
Route('home_security', '/home/security', HomeSecurityHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PRIV_USER_PROFILE);
|
Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PRIV_USER_PROFILE);
|
Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Route('home_messages', '/home/messages', HomeMessagesHandler, PRIV_USER_PROFILE);
|
Route('home_messages', '/home/messages', HomeMessagesHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Route('home_file', '/home/file', HomeFileHandler, PRIV_USER_PROFILE);
|
Route('home_file', '/home/file', HomeFileHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler, PRIV_USER_PROFILE);
|
Connection('home_messages_conn', '/home/messages-conn', HomeMessagesConnectionHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.home = module.exports = apply;
|
global.Hydro.handler.home = module.exports = apply;
|
||||||
@@ -1,27 +1,21 @@
|
|||||||
const yaml = require('js-yaml');
|
import yaml from 'js-yaml';
|
||||||
const moment = require('moment-timezone');
|
import moment from 'moment-timezone';
|
||||||
const { ObjectID } = require('mongodb');
|
import { ObjectID } from 'mongodb';
|
||||||
const AdmZip = require('adm-zip');
|
import AdmZip from 'adm-zip';
|
||||||
const {
|
import {
|
||||||
ValidationError, HomeworkNotLiveError, ProblemNotFoundError,
|
ValidationError, HomeworkNotLiveError, ProblemNotFoundError,
|
||||||
HomeworkNotAttendedError,
|
HomeworkNotAttendedError,
|
||||||
} = require('../error');
|
} from '../error';
|
||||||
const { Route, Handler } = require('../service/server');
|
import { Route, Handler } from '../service/server';
|
||||||
const {
|
import { PERM } from '../model/builtin';
|
||||||
PERM_VIEW_HOMEWORK, PERM_ATTEND_HOMEWORK, PERM_VIEW_PROBLEM,
|
import * as system from '../model/system';
|
||||||
PERM_SUBMIT_PROBLEM, PERM_CREATE_HOMEWORK, PERM_EDIT_HOMEWORK,
|
import * as user from '../model/user';
|
||||||
PERM_VIEW_HOMEWORK_SCOREBOARD, PERM_READ_RECORD_CODE,
|
import * as contest from '../model/contest';
|
||||||
} = require('../model/builtin').PERM;
|
import * as discussion from '../model/discussion';
|
||||||
const system = require('../model/system');
|
import * as problem from '../model/problem';
|
||||||
const user = require('../model/user');
|
import * as record from '../model/record';
|
||||||
const contest = require('../model/contest');
|
import * as document from '../model/document';
|
||||||
const discussion = require('../model/discussion');
|
import paginate from '../lib/paginate';
|
||||||
const problem = require('../model/problem');
|
|
||||||
const record = require('../model/record');
|
|
||||||
const document = require('../model/document');
|
|
||||||
const paginate = require('../lib/paginate');
|
|
||||||
const misc = require('../lib/misc');
|
|
||||||
const ranked = require('../lib/rank');
|
|
||||||
|
|
||||||
const HomeworkHandler = contest.ContestHandlerMixin(Handler);
|
const HomeworkHandler = contest.ContestHandlerMixin(Handler);
|
||||||
|
|
||||||
@@ -31,7 +25,7 @@ class HomeworkMainHandler extends HomeworkHandler {
|
|||||||
const calendar = [];
|
const calendar = [];
|
||||||
for (const tdoc of tdocs) {
|
for (const tdoc of tdocs) {
|
||||||
const cal = { ...tdoc, url: `/homework/${tdoc.docId}` };
|
const cal = { ...tdoc, url: `/homework/${tdoc.docId}` };
|
||||||
if (contest.isHomeworkExtended(tdoc) || contest.isDone(tdoc)) {
|
if (contest.isExtended(tdoc) || contest.isDone(tdoc)) {
|
||||||
cal.endAt = tdoc.endAt;
|
cal.endAt = tdoc.endAt;
|
||||||
cal.penaltySince = tdoc.penaltySince;
|
cal.penaltySince = tdoc.penaltySince;
|
||||||
} else {
|
} else {
|
||||||
@@ -93,7 +87,7 @@ class HomeworkDetailHandler extends HomeworkHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async postAttend({ domainId, tid }) {
|
async postAttend({ domainId, tid }) {
|
||||||
this.checkPerm(PERM_ATTEND_HOMEWORK);
|
this.checkPerm(PERM.PERM_ATTEND_HOMEWORK);
|
||||||
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
||||||
if (contest.isDone(tdoc)) throw new HomeworkNotLiveError(tdoc.docId);
|
if (contest.isDone(tdoc)) throw new HomeworkNotLiveError(tdoc.docId);
|
||||||
await contest.attend(domainId, tdoc.docId, this.user._id, document.TYPE_HOMEWORK);
|
await contest.attend(domainId, tdoc.docId, this.user._id, document.TYPE_HOMEWORK);
|
||||||
@@ -103,7 +97,7 @@ class HomeworkDetailHandler extends HomeworkHandler {
|
|||||||
|
|
||||||
class HomeworkDetailProblemHandler extends HomeworkHandler {
|
class HomeworkDetailProblemHandler extends HomeworkHandler {
|
||||||
async prepare({ domainId, tid, pid }) {
|
async prepare({ domainId, tid, pid }) {
|
||||||
this.checkPerm(PERM_VIEW_PROBLEM);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM);
|
||||||
[this.tdoc, this.pdoc, this.tsdoc] = await Promise.all([
|
[this.tdoc, this.pdoc, this.tsdoc] = await Promise.all([
|
||||||
contest.get(domainId, tid, document.TYPE_HOMEWORK),
|
contest.get(domainId, tid, document.TYPE_HOMEWORK),
|
||||||
problem.get(domainId, pid, this.user._id),
|
problem.get(domainId, pid, this.user._id),
|
||||||
@@ -121,7 +115,7 @@ class HomeworkDetailProblemHandler extends HomeworkHandler {
|
|||||||
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid, tid);
|
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid, tid);
|
||||||
const path = [
|
const path = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['homework_main', '/homework'],
|
['homework_main', 'homework_main'],
|
||||||
[this.tdoc.title, 'homework_detail', { tid }, true],
|
[this.tdoc.title, 'homework_detail', { tid }, true],
|
||||||
[this.pdoc.title, null, null, true],
|
[this.pdoc.title, null, null, true],
|
||||||
];
|
];
|
||||||
@@ -170,8 +164,8 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
|
|||||||
await contest.updateStatus(domainId, tid, this.user._id,
|
await contest.updateStatus(domainId, tid, this.user._id,
|
||||||
rid, pid, false, 0, document.TYPE_HOMEWORK);
|
rid, pid, false, 0, document.TYPE_HOMEWORK);
|
||||||
this.response.body = { tid, rid };
|
this.response.body = { tid, rid };
|
||||||
if (this.canShowRecord(this.tdoc)) this.response.redirect = `/record/${rid}`;
|
if (this.canShowRecord(this.tdoc)) this.response.redirect = this.url('record_detail', { rid });
|
||||||
else this.response.redirect = `/homework/${tid}`;
|
else this.response.redirect = this.url('homework_detail', { tid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +224,8 @@ class HomeworkCreateHandler extends HomeworkHandler {
|
|||||||
class HomeworkEditHandler extends HomeworkHandler {
|
class HomeworkEditHandler extends HomeworkHandler {
|
||||||
async get({ domainId, tid }) {
|
async get({ domainId, tid }) {
|
||||||
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
||||||
if (tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_HOMEWORK);
|
if (tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_HOMEWORK);
|
||||||
|
else this.checkPerm(PERM.PERM_EDIT_HOMEWORK_SELF);
|
||||||
const extensionDays = Math.round(
|
const extensionDays = Math.round(
|
||||||
(tdoc.endAt.getTime() - tdoc.penaltySince.getTime()) / 36000 / 24,
|
(tdoc.endAt.getTime() - tdoc.penaltySince.getTime()) / 36000 / 24,
|
||||||
) / 100;
|
) / 100;
|
||||||
@@ -262,7 +257,8 @@ class HomeworkEditHandler extends HomeworkHandler {
|
|||||||
penaltyRules, pids, tid,
|
penaltyRules, pids, tid,
|
||||||
}) {
|
}) {
|
||||||
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
const tdoc = await contest.get(domainId, tid, document.TYPE_HOMEWORK);
|
||||||
if (tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_HOMEWORK);
|
if (tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_HOMEWORK);
|
||||||
|
else this.checkPerm(PERM.PERM_EDIT_HOMEWORK_SELF);
|
||||||
let beginAt;
|
let beginAt;
|
||||||
let penaltySince;
|
let penaltySince;
|
||||||
try {
|
try {
|
||||||
@@ -288,7 +284,7 @@ class HomeworkEditHandler extends HomeworkHandler {
|
|||||||
|| tdoc.endAt !== endAt
|
|| tdoc.endAt !== endAt
|
||||||
|| tdoc.penaltySince !== penaltySince
|
|| tdoc.penaltySince !== penaltySince
|
||||||
|| new Set(tdoc.pids) !== new Set(pids)) {
|
|| new Set(tdoc.pids) !== new Set(pids)) {
|
||||||
await contest.recalcStatus(domainId, document.TYPE_HOMEWORK, tdoc.docId);
|
await contest.recalcStatus(domainId, tdoc.docId, document.TYPE_HOMEWORK);
|
||||||
}
|
}
|
||||||
this.response.body = { tid };
|
this.response.body = { tid };
|
||||||
this.response.redirect = this.url('homework_detail', { tid });
|
this.response.redirect = this.url('homework_detail', { tid });
|
||||||
@@ -327,7 +323,7 @@ class HomeworkScoreboardDownloadHandler extends HomeworkHandler {
|
|||||||
|
|
||||||
class HomeworkCodeHandler extends HomeworkHandler {
|
class HomeworkCodeHandler extends HomeworkHandler {
|
||||||
async get({ domainId, tid }) {
|
async get({ domainId, tid }) {
|
||||||
this.checkPerm(PERM_READ_RECORD_CODE);
|
this.checkPerm(PERM.PERM_READ_RECORD_CODE);
|
||||||
this.limitRate('homework_code', 3600, 60);
|
this.limitRate('homework_code', 3600, 60);
|
||||||
const [tdoc, tsdocs] = await contest.getAndListStatus(
|
const [tdoc, tsdocs] = await contest.getAndListStatus(
|
||||||
domainId, tid, document.TYPE_HOMEWORK,
|
domainId, tid, document.TYPE_HOMEWORK,
|
||||||
@@ -352,155 +348,15 @@ class HomeworkCodeHandler extends HomeworkHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
async function apply() {
|
||||||
contest.isHomeworkExtended = (tdoc) => {
|
Route('homework_main', '/homework', HomeworkMainHandler, PERM.PERM_VIEW_HOMEWORK);
|
||||||
const now = new Date().getTime();
|
Route('homework_create', '/homework/create', HomeworkCreateHandler, PERM.PERM_CREATE_HOMEWORK);
|
||||||
return tdoc.penaltySince.getTime() <= now && now < tdoc.endAt.getTime();
|
Route('homework_detail', '/homework/:tid', HomeworkDetailHandler, PERM.PERM_VIEW_HOMEWORK);
|
||||||
};
|
Route('homework_scoreboard', '/homework/:tid/scoreboard', HomeworkScoreboardHandler, PERM.PERM_VIEW_HOMEWORK_SCOREBOARD);
|
||||||
contest.RULES.homework = {
|
Route('homework_scoreboard', '/homework/:tid/scoreboard/download/:ext', HomeworkScoreboardDownloadHandler, PERM.PERM_VIEW_HOMEWORK_SCOREBOARD);
|
||||||
TEXT: 'Assignment',
|
Route('homework_detail_problem', '/homework/:tid/p/:pid', HomeworkDetailProblemHandler, PERM.PERM_VIEW_HOMEWORK);
|
||||||
check: () => { },
|
Route('homework_detail_problem_submit', '/homework/:tid/p/:pid/submit', HomeworkDetailProblemSubmitHandler, PERM.PERM_SUBMIT_PROBLEM);
|
||||||
stat: (tdoc, journal) => {
|
Route('homework_code', '/homework/:tid/code', HomeworkCodeHandler, PERM.PERM_VIEW_HOMEWORK);
|
||||||
const effective = {};
|
|
||||||
for (const j of journal) {
|
|
||||||
if (tdoc.pids.includes(j.pid)
|
|
||||||
&& !effective.includes(j.pid)
|
|
||||||
&& effective[j.pid].accept) {
|
|
||||||
effective[j.pid] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function time(jdoc) {
|
|
||||||
const real = jdoc.rid.generationTime - tdoc.beginAt.getTime() / 1000;
|
|
||||||
return Math.floor(real);
|
|
||||||
}
|
|
||||||
function penaltyScore(jdoc) {
|
|
||||||
const { score } = jdoc;
|
|
||||||
const exceedSeconds = Math.floor(
|
|
||||||
jdoc.rid.generationTime - tdoc.penaltySince.getTime() / 1000,
|
|
||||||
);
|
|
||||||
if (exceedSeconds < 0) return score;
|
|
||||||
let coefficient = 1;
|
|
||||||
const keys = Object.keys(tdoc.penaltyRules)
|
|
||||||
.map((k) => parseFloat(k)).sort((a, b) => a - b);
|
|
||||||
for (const i of keys) {
|
|
||||||
if (i * 3600 <= exceedSeconds) coefficient = tdoc.penaltyRules[i];
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
return score * coefficient;
|
|
||||||
}
|
|
||||||
const detail = [];
|
|
||||||
for (const j of effective) {
|
|
||||||
detail.push({ ...j, penaltyScore: penaltyScore(j), time: time(j) });
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
score: Math.sum(detail.map((d) => d.score)),
|
|
||||||
penaltyScore: Math.sum(detail.map((d) => d.penaltySince)),
|
|
||||||
time: Math.sum(detail.map((d) => d.time)),
|
|
||||||
detail,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
showScoreboard: () => true,
|
|
||||||
showRecord: () => true,
|
|
||||||
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
|
||||||
const columns = [
|
|
||||||
{ type: 'rank', value: _('Rank') },
|
|
||||||
{ type: 'user', value: _('User') },
|
|
||||||
{ type: 'total_score', value: _('Score') },
|
|
||||||
];
|
|
||||||
if (isExport) {
|
|
||||||
columns.push(
|
|
||||||
{ type: 'total_original_score', value: _('Original Score') },
|
|
||||||
{ type: 'total_time', value: _('Total Time (Seconds)') },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
columns.push({ type: 'total_time_str', value: _('Total Time') });
|
|
||||||
for (const index in tdoc.pids) {
|
|
||||||
const pid = tdoc.pids[index];
|
|
||||||
if (isExport) {
|
|
||||||
columns.push(
|
|
||||||
{
|
|
||||||
type: 'problem_score',
|
|
||||||
value: '#{0} {1}'.format(index + 1, pdict[pid].title),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'problem_original_score',
|
|
||||||
value: '#{0} {1}'.format(index + 1, _('Original Score')),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'problem_time',
|
|
||||||
value: '#{0} {1}'.format(index + 1, _('Time (Seconds)')),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'problem_time_str',
|
|
||||||
value: '#{0} {1}'.format(index + 1, _('Time')),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
columns.push({
|
|
||||||
type: 'problem_detail',
|
|
||||||
value: '#{0}'.format(index + 1),
|
|
||||||
raw: pdict[pid],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const rows = [columns];
|
|
||||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
|
||||||
const tsddict = {};
|
|
||||||
for (const item of tsdoc.journal || []) {
|
|
||||||
tsddict[item.pid] = item;
|
|
||||||
}
|
|
||||||
const row = [
|
|
||||||
{ type: 'string', value: rank },
|
|
||||||
{
|
|
||||||
type: 'user',
|
|
||||||
value: udict[tsdoc.uid].uname,
|
|
||||||
raw: udict[tsdoc.uid],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
value: tsdoc.penaltyScore || 0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
if (isExport) {
|
|
||||||
row.push({ type: 'string', value: tsdoc.score || 0 });
|
|
||||||
row.push({ type: 'string', value: tsdoc.time || 0.0 });
|
|
||||||
}
|
|
||||||
row.push({ type: 'string', value: misc.formatSeconds(tsdoc.time || 0) });
|
|
||||||
for (const pid of tdoc.pids) {
|
|
||||||
const rdoc = (tsddict[pid] || {}).rid;
|
|
||||||
const colScore = (tsddict[pid] || {}).penaltyScore || '-';
|
|
||||||
const colOriginalScore = (tsddict[pid] || {}).score || '-';
|
|
||||||
const colTime = (tsddict[pid] || {}).time || '-';
|
|
||||||
const colTimeStr = colTime !== '-' ? misc.formatSeconds(colTime || 0) : '-';
|
|
||||||
if (isExport) {
|
|
||||||
row.push(
|
|
||||||
{ type: 'string', value: colScore },
|
|
||||||
{ type: 'string', value: colOriginalScore },
|
|
||||||
{ type: 'string', value: colTime },
|
|
||||||
{ type: 'string', value: colTimeStr },
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
row.push({
|
|
||||||
type: 'record',
|
|
||||||
value: '{0} / {1}\n{2}'.format(colScore, colOriginalScore, colTimeStr),
|
|
||||||
raw: rdoc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
return rows;
|
|
||||||
},
|
|
||||||
rank: (tsdocs) => ranked(tsdocs, (a, b) => a.score === b.score),
|
|
||||||
};
|
|
||||||
Route('homework_main', '/homework', HomeworkMainHandler, PERM_VIEW_HOMEWORK);
|
|
||||||
Route('homework_create', '/homework/create', HomeworkCreateHandler, PERM_CREATE_HOMEWORK);
|
|
||||||
Route('homework_detail', '/homework/:tid', HomeworkDetailHandler, PERM_VIEW_HOMEWORK);
|
|
||||||
Route('homework_scoreboard', '/homework/:tid/scoreboard', HomeworkScoreboardHandler, PERM_VIEW_HOMEWORK_SCOREBOARD);
|
|
||||||
Route('homework_scoreboard', '/homework/:tid/scoreboard/download/:ext', HomeworkScoreboardDownloadHandler, PERM_VIEW_HOMEWORK_SCOREBOARD);
|
|
||||||
Route('homework_detail_problem', '/homework/:tid/p/:pid', HomeworkDetailProblemHandler, PERM_VIEW_HOMEWORK);
|
|
||||||
Route('homework_detail_problem_submit', '/homework/:tid/p/:pid/submit', HomeworkDetailProblemSubmitHandler, PERM_SUBMIT_PROBLEM);
|
|
||||||
Route('homework_code', '/homework/:tid/code', HomeworkCodeHandler, PERM_VIEW_HOMEWORK);
|
|
||||||
Route('homework_edit', '/homework/:tid/edit', HomeworkEditHandler);
|
Route('homework_edit', '/homework/:tid/edit', HomeworkEditHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.homework = module.exports = apply;
|
global.Hydro.handler.homework = apply;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const problem = require('../model/problem');
|
import { ValidationError } from '../error'
|
||||||
const { PERM_CREATE_PROBLEM } = require('../model/builtin').PERM;
|
import * as problem from '../model/problem';
|
||||||
const { Route, Handler } = require('../service/server');
|
import { PERM } from '../model/builtin';
|
||||||
const { ValidationError } = require('../error');
|
import { Route, Handler } from '../service/server'
|
||||||
|
|
||||||
class ProblemImportHandler extends Handler {
|
class ProblemImportHandler extends Handler {
|
||||||
async prepare() {
|
async prepare() {
|
||||||
this.checkPerm(PERM_CREATE_PROBLEM);
|
this.checkPerm(PERM.PERM_CREATE_PROBLEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get() {
|
async get() {
|
||||||
@@ -47,8 +47,8 @@ class ProblemImportHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('problem_import', '/problem/import', ProblemImportHandler);
|
Route('problem_import', '/problem/import', ProblemImportHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.import = module.exports = apply;
|
global.Hydro.handler.import = apply;
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
const yaml = require('js-yaml');
|
import yaml from 'js-yaml';
|
||||||
const { parseTimeMS, parseMemoryMB } = require('../utils');
|
import { parseTimeMS, parseMemoryMB } from '../utils';
|
||||||
const record = require('../model/record');
|
import * as record from '../model/record';
|
||||||
const problem = require('../model/problem');
|
import * as problem from '../model/problem';
|
||||||
const builtin = require('../model/builtin');
|
import * as builtin from '../model/builtin';
|
||||||
const contest = require('../model/contest');
|
import * as contest from '../model/contest';
|
||||||
const user = require('../model/user');
|
import * as user from '../model/user';
|
||||||
const task = require('../model/task');
|
import * as task from '../model/task';
|
||||||
const bus = require('../service/bus');
|
import * as bus from '../service/bus';
|
||||||
const {
|
import {
|
||||||
Route, Handler, Connection, ConnectionHandler,
|
Route, Handler, Connection, ConnectionHandler,
|
||||||
} = require('../service/server');
|
} from '../service/server';
|
||||||
|
|
||||||
async function _postJudge(rdoc) {
|
async function _postJudge(rdoc) {
|
||||||
const accept = rdoc.status === builtin.STATUS_ACCEPTED;
|
const accept = rdoc.status === builtin.STATUS.STATUS_ACCEPTED;
|
||||||
bus.publish('record_change', rdoc);
|
bus.publish('record_change', rdoc);
|
||||||
if (rdoc.type === 'run') return;
|
if (rdoc.type === 'run') return;
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
@@ -24,7 +24,7 @@ async function _postJudge(rdoc) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (await problem.updateStatus(rdoc.pid, rdoc.uid, rdoc._id, rdoc.status)) {
|
if (await problem.updateStatus(rdoc.domainId, rdoc.pid, rdoc.uid, rdoc._id, rdoc.status)) {
|
||||||
if (accept && !rdoc.rejudged) {
|
if (accept && !rdoc.rejudged) {
|
||||||
tasks.push(
|
tasks.push(
|
||||||
problem.inc(rdoc.domainId, rdoc.pid, 'nAccept', 1),
|
problem.inc(rdoc.domainId, rdoc.pid, 'nAccept', 1),
|
||||||
@@ -35,12 +35,12 @@ async function _postJudge(rdoc) {
|
|||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function next(body) {
|
export async function next(body) {
|
||||||
let rdoc = await record.get(body.domainId, body.rid);
|
let rdoc = await record.get(body.domainId, body.rid);
|
||||||
const $set = {};
|
const $set: any = {};
|
||||||
const $push = {};
|
const $push: any = {};
|
||||||
if (body.case) {
|
if (body.case) {
|
||||||
const c = {};
|
const c: any = {};
|
||||||
c.memory = body.case.memory_kb || body.case.memory;
|
c.memory = body.case.memory_kb || body.case.memory;
|
||||||
c.time = body.case.time_ms || body.case.time;
|
c.time = body.case.time_ms || body.case.time;
|
||||||
c.judgeText = body.case.judge_text || body.case.judgeText || body.case.message;
|
c.judgeText = body.case.judge_text || body.case.judgeText || body.case.message;
|
||||||
@@ -65,10 +65,10 @@ async function next(body) {
|
|||||||
bus.publish('record_change', rdoc);
|
bus.publish('record_change', rdoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function end(body) {
|
export async function end(body) {
|
||||||
let rdoc = await record.get(body.domainId, body.rid);
|
let rdoc = await record.get(body.domainId, body.rid);
|
||||||
const $set = {};
|
const $set: any = {};
|
||||||
const $push = {};
|
const $push: any = {};
|
||||||
const $unset = { progress: '' };
|
const $unset = { progress: '' };
|
||||||
if (body.judge_text) {
|
if (body.judge_text) {
|
||||||
rdoc.judgeTexts.push(body.judge_text);
|
rdoc.judgeTexts.push(body.judge_text);
|
||||||
@@ -98,7 +98,7 @@ class PretestHandler extends Handler {
|
|||||||
if (pid) {
|
if (pid) {
|
||||||
const pdoc = await problem.get(domainId, pid);
|
const pdoc = await problem.get(domainId, pid);
|
||||||
if (pdoc.config) {
|
if (pdoc.config) {
|
||||||
const config = yaml.safeLoad(pdoc.config);
|
const config: any = yaml.safeLoad(pdoc.config);
|
||||||
if (config.time) time = config.time;
|
if (config.time) time = config.time;
|
||||||
if (config.memory) memory = config.memory;
|
if (config.memory) memory = config.memory;
|
||||||
}
|
}
|
||||||
@@ -155,19 +155,19 @@ class JudgeConnectionHandler extends ConnectionHandler {
|
|||||||
|
|
||||||
async cleanup() {
|
async cleanup() {
|
||||||
if (this.processing) {
|
if (this.processing) {
|
||||||
await record.reset(this.processing);
|
await record.reset(this.processing.domainId, this.processing.rid);
|
||||||
task.add(this.processing);
|
task.add(this.processing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('pretest', '/pretest', PretestHandler);
|
Route('pretest', '/pretest', PretestHandler);
|
||||||
Route('judge', '/judge', JudgeHandler, builtin.PERM.PERM_JUDGE);
|
Route('judge', '/judge', JudgeHandler, builtin.PRIV.PRIV_JUDGE);
|
||||||
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PERM.PERM_JUDGE);
|
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PRIV.PRIV_JUDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
apply.next = next;
|
apply.next = next;
|
||||||
apply.end = end;
|
apply.end = end;
|
||||||
|
|
||||||
global.Hydro.handler.judge = module.exports = apply;
|
global.Hydro.handler.judge = apply;
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
const setting = require('../model/setting');
|
import * as judge from './judge';
|
||||||
const system = require('../model/system');
|
import { ValidationError } from '../error';
|
||||||
const { STATUS, PRIV } = require('../model/builtin');
|
import * as check from '../check';
|
||||||
const record = require('../model/record');
|
import * as setting from '../model/setting';
|
||||||
const judge = require('./judge');
|
import * as system from '../model/system';
|
||||||
const {
|
import { STATUS, PRIV } from '../model/builtin';
|
||||||
|
import * as record from '../model/record';
|
||||||
|
import {
|
||||||
Route, Connection, Handler, ConnectionHandler,
|
Route, Connection, Handler, ConnectionHandler,
|
||||||
} = require('../service/server');
|
} from '../service/server';
|
||||||
const hpm = require('../lib/hpm');
|
import * as hpm from '../lib/hpm';
|
||||||
const loader = require('../loader');
|
|
||||||
const check = require('../check');
|
|
||||||
|
|
||||||
function set(key, value) {
|
function set(key, value) {
|
||||||
if (setting.SYSTEM_SETTINGS_BY_KEY[key]) {
|
if (setting.SYSTEM_SETTINGS_BY_KEY[key]) {
|
||||||
@@ -81,7 +81,6 @@ class SystemModuleHandler extends SystemHandler {
|
|||||||
async get() {
|
async get() {
|
||||||
this.response.body.path.push(['manage_module', null]);
|
this.response.body.path.push(['manage_module', null]);
|
||||||
this.response.body.installed = await hpm.getDetail();
|
this.response.body.installed = await hpm.getDetail();
|
||||||
this.response.active = loader.active;
|
|
||||||
this.response.template = 'manage_module.html';
|
this.response.template = 'manage_module.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
const { PRIV_JUDGE, PRIV_USER_PROFILE } = require('../model/builtin').PRIV;
|
import { PRIV } from '../model/builtin';
|
||||||
const file = require('../model/file');
|
import * as file from '../model/file';
|
||||||
const user = require('../model/user');
|
import * as user from '../model/user';
|
||||||
const markdown = require('../lib/markdown');
|
import * as markdown from '../lib/markdown';
|
||||||
const db = require('../service/db');
|
import * as db from '../service/db';
|
||||||
const { Route, Handler } = require('../service/server');
|
import { Route, Handler } from '../service/server';
|
||||||
|
|
||||||
const coll = db.collection('status');
|
const coll = db.collection('status');
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ class FileDownloadHandler extends Handler {
|
|||||||
async get({ docId, secret, name }) {
|
async get({ docId, secret, name }) {
|
||||||
if (name) name = Buffer.from(name, 'base64').toString();
|
if (name) name = Buffer.from(name, 'base64').toString();
|
||||||
this.response.attachment(name || docId);
|
this.response.attachment(name || docId);
|
||||||
this.response.body = await file.get(docId, secret);
|
this.response.body = await file.getWithSecret(docId, secret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class StatusHandler extends Handler {
|
|||||||
|
|
||||||
class StatusUpdateHandler extends Handler {
|
class StatusUpdateHandler extends Handler {
|
||||||
async post(args) {
|
async post(args) {
|
||||||
this.checkPerm(PRIV_JUDGE);
|
this.checkPerm(PRIV.PRIV_JUDGE);
|
||||||
args.type = 'judger';
|
args.type = 'judger';
|
||||||
return coll.updateOne(
|
return coll.updateOne(
|
||||||
{ mid: args.mid, type: 'judger' },
|
{ mid: args.mid, type: 'judger' },
|
||||||
@@ -49,7 +49,7 @@ class StatusUpdateHandler extends Handler {
|
|||||||
|
|
||||||
class SwitchLanguageHandler extends Handler {
|
class SwitchLanguageHandler extends Handler {
|
||||||
async get({ lang }) {
|
async get({ lang }) {
|
||||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
|
if (this.user.hasPriv(PRIV.PRIV_USER_PROFILE)) {
|
||||||
await user.setById(this.user._id, { viewLang: lang });
|
await user.setById(this.user._id, { viewLang: lang });
|
||||||
} else this.session.viewLang = lang;
|
} else this.session.viewLang = lang;
|
||||||
this.back();
|
this.back();
|
||||||
@@ -70,7 +70,7 @@ class SockToken extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('file_download', '/fs/:docId/:secret', FileDownloadHandler);
|
Route('file_download', '/fs/:docId/:secret', FileDownloadHandler);
|
||||||
Route('file_download_with_name', '/fs/:docId/:name/:secret', FileDownloadHandler);
|
Route('file_download_with_name', '/fs/:docId/:name/:secret', FileDownloadHandler);
|
||||||
Route('status', '/status', StatusHandler);
|
Route('status', '/status', StatusHandler);
|
||||||
@@ -89,4 +89,4 @@ apply.updateStatus = function updateStatus(args) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
global.Hydro.handler.misc = module.exports = apply;
|
global.Hydro.handler.misc = apply;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable no-empty-function */
|
/* eslint-disable no-empty-function */
|
||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
const { Route, Handler } = require('../service/server');
|
import { NotFoundError } from '../error';
|
||||||
const { NotFoundError } = require('../error');
|
import { Route, Handler } from '../service/server';
|
||||||
|
|
||||||
class NotFoundHandler extends Handler {
|
class NotFoundHandler extends Handler {
|
||||||
async prepare() {
|
async prepare() {
|
||||||
@@ -13,8 +13,8 @@ class NotFoundHandler extends Handler {
|
|||||||
async post() { }
|
async post() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('notfound', '*', NotFoundHandler);
|
Route('notfound', '*', NotFoundHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.notfound = module.exports = apply;
|
global.Hydro.handler.notfound = apply;
|
||||||
@@ -1,58 +1,47 @@
|
|||||||
const paginate = require('../lib/paginate');
|
import paginate from '../lib/paginate';
|
||||||
const validator = require('../lib/validator');
|
import * as validator from '../lib/validator';
|
||||||
const file = require('../model/file');
|
import * as file from '../model/file';
|
||||||
const problem = require('../model/problem');
|
import * as problem from '../model/problem';
|
||||||
const record = require('../model/record');
|
import * as record from '../model/record';
|
||||||
const user = require('../model/user');
|
import * as user from '../model/user';
|
||||||
const solution = require('../model/solution');
|
import * as solution from '../model/solution';
|
||||||
const system = require('../model/system');
|
import * as system from '../model/system';
|
||||||
const {
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
PERM: {
|
import * as bus from '../service/bus';
|
||||||
PERM_VIEW_PROBLEM, PERM_VIEW_PROBLEM_HIDDEN, PERM_SUBMIT_PROBLEM,
|
import {
|
||||||
PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM,
|
|
||||||
PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION,
|
|
||||||
PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY,
|
|
||||||
PERM_REPLY_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SELF,
|
|
||||||
},
|
|
||||||
PRIV: {
|
|
||||||
PRIV_USER_PROFILE, PRIV_JUDGE,
|
|
||||||
},
|
|
||||||
} = require('../model/builtin');
|
|
||||||
const bus = require('../service/bus');
|
|
||||||
const {
|
|
||||||
Route, Connection, Handler, ConnectionHandler,
|
Route, Connection, Handler, ConnectionHandler,
|
||||||
} = require('../service/server');
|
} from '../service/server';
|
||||||
const {
|
import {
|
||||||
NoProblemError, ProblemDataNotFoundError, BadRequestError,
|
NoProblemError, ProblemDataNotFoundError, BadRequestError,
|
||||||
SolutionNotFoundError,
|
SolutionNotFoundError,
|
||||||
} = require('../error');
|
} from '../error';
|
||||||
|
|
||||||
class ProblemHandler extends Handler {
|
class ProblemHandler extends Handler {
|
||||||
async _prepare() {
|
async __prepare() {
|
||||||
this.checkPerm(PERM_VIEW_PROBLEM);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get({ domainId, page = 1, q = '' }) {
|
async get({ domainId, page = 1, q = '' }) {
|
||||||
this.response.template = 'problem_main.html';
|
this.response.template = 'problem_main.html';
|
||||||
const query = {};
|
const query: any = {};
|
||||||
let psdict = {};
|
let psdict = {};
|
||||||
const path = [
|
const path: any = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['problem_main', null],
|
['problem_main', null],
|
||||||
];
|
];
|
||||||
if (q) {
|
if (q) {
|
||||||
q = q.toLowerCase();
|
q = q.toLowerCase();
|
||||||
const $regex = new RegExp(`\\A\\Q${q.replace(/\\E/gmi, /\\E\\E\\Q/gmi)}\\E`, 'gmi');
|
const $regex = new RegExp(`\\A\\Q${q}\\E`, 'gmi');
|
||||||
query.title = { $regex };
|
query.title = { $regex };
|
||||||
path.push([q, null, null, true]);
|
path.push([q, null, null, true]);
|
||||||
}
|
}
|
||||||
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) query.hidden = false;
|
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) query.hidden = false;
|
||||||
const [pdocs, ppcount, pcount] = await paginate(
|
const [pdocs, ppcount, pcount] = await paginate(
|
||||||
problem.getMulti(domainId, query).sort({ pid: 1 }),
|
problem.getMulti(domainId, query).sort({ pid: 1 }),
|
||||||
page,
|
page,
|
||||||
await system.get('PROBLEM_PER_PAGE'),
|
await system.get('PROBLEM_PER_PAGE'),
|
||||||
);
|
);
|
||||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
|
if (this.user.hasPriv(PRIV.PRIV_USER_PROFILE)) {
|
||||||
psdict = await problem.getListStatus(
|
psdict = await problem.getListStatus(
|
||||||
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
||||||
);
|
);
|
||||||
@@ -73,7 +62,7 @@ class ProblemHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async cleanup() {
|
async cleanup() {
|
||||||
if (this.response.template === 'problem_main.html' && this.preferJson) {
|
if (this.response.template === 'problem_main.html' && this.request.json) {
|
||||||
const {
|
const {
|
||||||
path, page, pcount, ppcount, pdocs, psdict, category,
|
path, page, pcount, ppcount, pdocs, psdict, category,
|
||||||
} = this.response.body;
|
} = this.response.body;
|
||||||
@@ -98,7 +87,7 @@ class ProblemHandler extends Handler {
|
|||||||
class ProblemCategoryHandler extends ProblemHandler {
|
class ProblemCategoryHandler extends ProblemHandler {
|
||||||
async get({ domainId, page = 1, category }) {
|
async get({ domainId, page = 1, category }) {
|
||||||
this.response.template = 'problem_main.html';
|
this.response.template = 'problem_main.html';
|
||||||
const q = { $and: [] };
|
const q: any = { $and: [] };
|
||||||
for (const name of category) {
|
for (const name of category) {
|
||||||
q.$and.push({
|
q.$and.push({
|
||||||
$or: [
|
$or: [
|
||||||
@@ -108,13 +97,13 @@ class ProblemCategoryHandler extends ProblemHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
let psdict = {};
|
let psdict = {};
|
||||||
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||||
const [pdocs, ppcount, pcount] = await paginate(
|
const [pdocs, ppcount, pcount] = await paginate(
|
||||||
problem.getMulti(domainId, q).sort({ pid: 1 }),
|
problem.getMulti(domainId, q).sort({ pid: 1 }),
|
||||||
page,
|
page,
|
||||||
await system.get('PROBLEM_PER_PAGE'),
|
await system.get('PROBLEM_PER_PAGE'),
|
||||||
);
|
);
|
||||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
|
if (this.user.hasPriv(PRIV.PRIV_USER_PROFILE)) {
|
||||||
psdict = await problem.getListStatus(
|
psdict = await problem.getListStatus(
|
||||||
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
||||||
);
|
);
|
||||||
@@ -132,7 +121,7 @@ class ProblemCategoryHandler extends ProblemHandler {
|
|||||||
|
|
||||||
class ProblemRandomHandler extends ProblemHandler {
|
class ProblemRandomHandler extends ProblemHandler {
|
||||||
async get({ domainId, category }) {
|
async get({ domainId, category }) {
|
||||||
const q = category[0] ? { $and: [] } : {};
|
const q: any = category[0] ? { $and: [] } : {};
|
||||||
for (const name of category) {
|
for (const name of category) {
|
||||||
if (name) {
|
if (name) {
|
||||||
q.$and.push({
|
q.$and.push({
|
||||||
@@ -143,7 +132,7 @@ class ProblemRandomHandler extends ProblemHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||||
const pid = await problem.random(domainId, q);
|
const pid = await problem.random(domainId, q);
|
||||||
if (!pid) throw new NoProblemError();
|
if (!pid) throw new NoProblemError();
|
||||||
this.response.body = { pid };
|
this.response.body = { pid };
|
||||||
@@ -157,7 +146,7 @@ class ProblemDetailHandler extends ProblemHandler {
|
|||||||
if (pid) {
|
if (pid) {
|
||||||
this.pdoc = await problem.get(domainId, pid, this.user._id);
|
this.pdoc = await problem.get(domainId, pid, this.user._id);
|
||||||
if (this.pdoc.hidden && this.pdoc.owner !== this.user._id) {
|
if (this.pdoc.hidden && this.pdoc.owner !== this.user._id) {
|
||||||
this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
}
|
}
|
||||||
if (this.pdoc) this.udoc = await user.getById(domainId, this.pdoc.owner);
|
if (this.pdoc) this.udoc = await user.getById(domainId, this.pdoc.owner);
|
||||||
}
|
}
|
||||||
@@ -168,7 +157,8 @@ class ProblemDetailHandler extends ProblemHandler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async get() {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
async get(args: any) {
|
||||||
this.response.body.path = [
|
this.response.body.path = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['problem_main', 'problem_main'],
|
['problem_main', 'problem_main'],
|
||||||
@@ -260,8 +250,8 @@ class ProblemStatisticsHandler extends ProblemDetailHandler {
|
|||||||
|
|
||||||
class ProblemManageHandler extends ProblemDetailHandler {
|
class ProblemManageHandler extends ProblemDetailHandler {
|
||||||
async prepare() {
|
async prepare() {
|
||||||
if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_PROBLEM);
|
if (this.pdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
||||||
else this.checkPerm(PERM_EDIT_PROBLEM_SELF);
|
else this.checkPerm(PERM.PERM_EDIT_PROBLEM_SELF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,8 +325,10 @@ class ProblemDataUploadHandler extends ProblemManageHandler {
|
|||||||
|
|
||||||
class ProblemDataDownloadHandler extends ProblemDetailHandler {
|
class ProblemDataDownloadHandler extends ProblemDetailHandler {
|
||||||
async get({ pid }) {
|
async get({ pid }) {
|
||||||
if (this.user._id !== this.pdoc.owner) {
|
if (!this.user.hasPriv(PRIV.PRIV_JUDGE)) {
|
||||||
if (!this.user.hasPerm(PERM_READ_PROBLEM_DATA)) this.checkPriv(PRIV_JUDGE);
|
if (this.user._id !== this.pdoc.owner) {
|
||||||
|
this.checkPerm(PERM.PERM_READ_PROBLEM_DATA);
|
||||||
|
} else this.checkPerm(PERM.PERM_READ_PROBLEM_DATA_SELF);
|
||||||
}
|
}
|
||||||
if (!this.pdoc.data) throw new ProblemDataNotFoundError(pid);
|
if (!this.pdoc.data) throw new ProblemDataNotFoundError(pid);
|
||||||
else if (typeof this.pdoc.data === 'string') [, this.response.redirect] = this.pdoc.data.split('from:');
|
else if (typeof this.pdoc.data === 'string') [, this.response.redirect] = this.pdoc.data.split('from:');
|
||||||
@@ -347,7 +339,7 @@ class ProblemDataDownloadHandler extends ProblemDetailHandler {
|
|||||||
class ProblemSolutionHandler extends ProblemDetailHandler {
|
class ProblemSolutionHandler extends ProblemDetailHandler {
|
||||||
async get({ domainId, page = 1 }) {
|
async get({ domainId, page = 1 }) {
|
||||||
this.response.template = 'problem_solution.html';
|
this.response.template = 'problem_solution.html';
|
||||||
this.checkPerm(PERM_VIEW_PROBLEM_SOLUTION);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION);
|
||||||
const [psdocs, pcount, pscount] = await paginate(
|
const [psdocs, pcount, pscount] = await paginate(
|
||||||
solution.getMulti(domainId, this.pdoc.docId),
|
solution.getMulti(domainId, this.pdoc.docId),
|
||||||
page,
|
page,
|
||||||
@@ -362,7 +354,7 @@ class ProblemSolutionHandler extends ProblemDetailHandler {
|
|||||||
for (const psrdoc of psdoc.reply) uids.push(psrdoc.owner);
|
for (const psrdoc of psdoc.reply) uids.push(psrdoc.owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const udict = await user.getList(uids);
|
const udict = await user.getList(domainId, uids);
|
||||||
const pssdict = solution.getListStatus(domainId, docids, this.user._id);
|
const pssdict = solution.getListStatus(domainId, docids, this.user._id);
|
||||||
const path = [
|
const path = [
|
||||||
['problem_main', 'problem_main'],
|
['problem_main', 'problem_main'],
|
||||||
@@ -379,28 +371,31 @@ class ProblemSolutionHandler extends ProblemDetailHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async postSubmit({ domainId, content }) {
|
async postSubmit({ domainId, content }) {
|
||||||
this.checkPerm(PERM_CREATE_PROBLEM_SOLUTION);
|
this.checkPerm(PERM.PERM_CREATE_PROBLEM_SOLUTION);
|
||||||
await solution.add(domainId, this.pdoc.docId, this.user._id, content);
|
await solution.add(domainId, this.pdoc.docId, this.user._id, content);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postEditSolution({ domainId, content }) {
|
async postEditSolution({ domainId, content }) {
|
||||||
if (this.psdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_PROBLEM_SOLUTION);
|
if (this.psdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION);
|
||||||
|
else this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_SELF);
|
||||||
this.psdoc = await solution.edit(domainId, this.psdoc.docId, content);
|
this.psdoc = await solution.edit(domainId, this.psdoc.docId, content);
|
||||||
this.ctx.body.psdoc = this.psdoc;
|
this.ctx.body.psdoc = this.psdoc;
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postDeleteSolution({ domainId }) {
|
async postDeleteSolution({ domainId }) {
|
||||||
if (this.psdoc.owner !== this.user._id) this.checkPerm(PERM_DELETE_PROBLEM_SOLUTION);
|
if (this.psdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION);
|
||||||
|
else this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF);
|
||||||
await solution.del(domainId, this.psdoc.docId);
|
await solution.del(domainId, this.psdoc.docId);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postReply({ domainId, psid, content }) {
|
async postReply({ domainId, psid, content }) {
|
||||||
this.checkPerm(PERM_REPLY_PROBLEM_SOLUTION);
|
this.checkPerm(PERM.PERM_REPLY_PROBLEM_SOLUTION);
|
||||||
const psdoc = await solution.get(domainId, psid);
|
const psdoc = await solution.get(domainId, psid);
|
||||||
await solution.reply(domainId, psdoc.docId, this.user._id, content);
|
await solution.reply(domainId, psdoc.docId, this.user._id, content);
|
||||||
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postEditReply({
|
async postEditReply({
|
||||||
@@ -408,14 +403,27 @@ class ProblemSolutionHandler extends ProblemDetailHandler {
|
|||||||
}) {
|
}) {
|
||||||
const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid);
|
const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid);
|
||||||
if ((!psdoc) || psdoc.pid !== this.pdoc.docId) throw new SolutionNotFoundError(psid);
|
if ((!psdoc) || psdoc.pid !== this.pdoc.docId) throw new SolutionNotFoundError(psid);
|
||||||
if (psrdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_PROBLEM_SOLUTION_REPLY);
|
if (!(psdoc.owner === this.user._id
|
||||||
|
&& this.user.hasPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF_SOLUTION))) {
|
||||||
|
if (!(psrdoc.owner === this.user._id
|
||||||
|
&& this.user.hasPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF))) {
|
||||||
|
this.checkPerm(PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY);
|
||||||
|
}
|
||||||
|
}
|
||||||
await solution.editReply(domainId, psid, psrid, content);
|
await solution.editReply(domainId, psid, psrid, content);
|
||||||
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
async postDeleteReply({ domainId, psid, psrid }) {
|
async postDeleteReply({ domainId, psid, psrid }) {
|
||||||
const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid);
|
const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid);
|
||||||
if ((!psdoc) || psdoc.pid !== this.pdoc.docId) throw new SolutionNotFoundError(psid);
|
if ((!psdoc) || psdoc.pid !== this.pdoc.docId) throw new SolutionNotFoundError(psid);
|
||||||
if (psrdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_PROBLEM_SOLUTION_REPLY);
|
if (!(psdoc.owner === this.user._id
|
||||||
|
&& this.user.hasPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF_SOLUTION))) {
|
||||||
|
if (!(psrdoc.owner === this.user._id
|
||||||
|
&& this.user.hasPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF))) {
|
||||||
|
this.checkPerm(PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY);
|
||||||
|
}
|
||||||
|
}
|
||||||
await solution.delReply(domainId, psid, psrid);
|
await solution.delReply(domainId, psid, psrid);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
@@ -435,7 +443,7 @@ class ProblemSolutionHandler extends ProblemDetailHandler {
|
|||||||
|
|
||||||
class ProblemSolutionRawHandler extends ProblemDetailHandler {
|
class ProblemSolutionRawHandler extends ProblemDetailHandler {
|
||||||
async get({ domainId, psid }) {
|
async get({ domainId, psid }) {
|
||||||
this.checkPerm(PERM_VIEW_PROBLEM_SOLUTION);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION);
|
||||||
const psdoc = await solution.get(domainId, psid);
|
const psdoc = await solution.get(domainId, psid);
|
||||||
this.response.type = 'text/markdown';
|
this.response.type = 'text/markdown';
|
||||||
this.response.body = psdoc.content;
|
this.response.body = psdoc.content;
|
||||||
@@ -443,10 +451,10 @@ class ProblemSolutionRawHandler extends ProblemDetailHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ProblemSolutionReplyRawHandler extends ProblemDetailHandler {
|
class ProblemSolutionReplyRawHandler extends ProblemDetailHandler {
|
||||||
async get({ domainId, psid }) {
|
async get({ domainId, psid, psrid }) {
|
||||||
this.checkPerm(PERM_VIEW_PROBLEM_SOLUTION);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION);
|
||||||
const [psdoc, psrdoc] = await solution.getReply(domainId, psid);
|
const [psdoc, psrdoc] = await solution.getReply(domainId, psid, psrid);
|
||||||
if ((!psdoc) || psdoc.pid !== this.pdoc.docId) throw new SolutionNotFoundError(psid);
|
if ((!psdoc) || psdoc.pid !== this.pdoc.docId) throw new SolutionNotFoundError(psid, psrid);
|
||||||
this.response.type = 'text/markdown';
|
this.response.type = 'text/markdown';
|
||||||
this.response.body = psrdoc.content;
|
this.response.body = psrdoc.content;
|
||||||
}
|
}
|
||||||
@@ -455,7 +463,7 @@ class ProblemSolutionReplyRawHandler extends ProblemDetailHandler {
|
|||||||
class ProblemCreateHandler extends Handler {
|
class ProblemCreateHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
this.response.template = 'problem_edit.html';
|
this.response.template = 'problem_edit.html';
|
||||||
this.checkPerm(PERM_CREATE_PROBLEM);
|
this.checkPerm(PERM.PERM_CREATE_PROBLEM);
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
path: [
|
path: [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
@@ -477,12 +485,12 @@ class ProblemCreateHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('problem_main', '/p', ProblemHandler);
|
Route('problem_main', '/p', ProblemHandler);
|
||||||
Route('problem_category', '/p/category/:category', ProblemCategoryHandler);
|
Route('problem_category', '/p/category/:category', ProblemCategoryHandler);
|
||||||
Route('problem_random', '/problem/random', ProblemRandomHandler);
|
Route('problem_random', '/problem/random', ProblemRandomHandler);
|
||||||
Route('problem_detail', '/p/:pid', ProblemDetailHandler);
|
Route('problem_detail', '/p/:pid', ProblemDetailHandler);
|
||||||
Route('problem_submit', '/p/:pid/submit', ProblemSubmitHandler, PERM_SUBMIT_PROBLEM);
|
Route('problem_submit', '/p/:pid/submit', ProblemSubmitHandler, PERM.PERM_SUBMIT_PROBLEM);
|
||||||
Route('problem_pretest', '/p/:pid/pretest', ProblemPretestHandler);
|
Route('problem_pretest', '/p/:pid/pretest', ProblemPretestHandler);
|
||||||
Route('problem_settings', '/p/:pid/settings', ProblemSettingsHandler);
|
Route('problem_settings', '/p/:pid/settings', ProblemSettingsHandler);
|
||||||
Route('problem_statistics', '/p/:pid/statistics', ProblemStatisticsHandler);
|
Route('problem_statistics', '/p/:pid/statistics', ProblemStatisticsHandler);
|
||||||
@@ -496,4 +504,4 @@ async function apply() {
|
|||||||
Connection('problem_pretest_conn', '/p/:pid/pretest-conn', ProblemPretestConnectionHandler);
|
Connection('problem_pretest_conn', '/p/:pid/pretest-conn', ProblemPretestConnectionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.problem = module.exports = apply;
|
global.Hydro.handler.problem = apply;
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
const { ObjectID } = require('mongodb');
|
import { ObjectID } from 'mongodb';
|
||||||
const { PermissionError } = require('../error');
|
import { PermissionError } from '../error';
|
||||||
const { PERM_READ_RECORD_CODE, PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../model/builtin').PERM;
|
import { PERM } from '../model/builtin';
|
||||||
const problem = require('../model/problem');
|
import * as problem from '../model/problem';
|
||||||
const record = require('../model/record');
|
import * as record from '../model/record';
|
||||||
const contest = require('../model/contest');
|
import * as contest from '../model/contest';
|
||||||
const system = require('../model/system');
|
import * as system from '../model/system';
|
||||||
const user = require('../model/user');
|
import * as user from '../model/user';
|
||||||
const paginate = require('../lib/paginate');
|
import paginate from '../lib/paginate';
|
||||||
const bus = require('../service/bus');
|
import * as bus from '../service/bus';
|
||||||
const {
|
import {
|
||||||
Route, Handler, Connection, ConnectionHandler,
|
Route, Handler, Connection, ConnectionHandler,
|
||||||
} = require('../service/server');
|
} from '../service/server';
|
||||||
|
|
||||||
const RecordHandler = contest.ContestHandlerMixin(Handler);
|
const RecordHandler = contest.ContestHandlerMixin(Handler);
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ class RecordListHandler extends RecordHandler {
|
|||||||
domainId, page = 1, pid, tid, uidOrName,
|
domainId, page = 1, pid, tid, uidOrName,
|
||||||
}) {
|
}) {
|
||||||
this.response.template = 'record_main.html';
|
this.response.template = 'record_main.html';
|
||||||
const q = { tid, hidden: false };
|
const q: any = { tid, hidden: false };
|
||||||
if (uidOrName) {
|
if (uidOrName) {
|
||||||
q.$or = [
|
q.$or = [
|
||||||
{ unameLower: uidOrName.toLowerCase() },
|
{ unameLower: uidOrName.toLowerCase() },
|
||||||
@@ -61,7 +61,7 @@ class RecordDetailHandler extends RecordHandler {
|
|||||||
const tdoc = await contest.get(domainId, rdoc.tid, rdoc.ttype);
|
const tdoc = await contest.get(domainId, rdoc.tid, rdoc.ttype);
|
||||||
if (!this.canShowRecord(tdoc, true)) throw new PermissionError(rid);
|
if (!this.canShowRecord(tdoc, true)) throw new PermissionError(rid);
|
||||||
}
|
}
|
||||||
if (rdoc.uid !== this.user.uid && !this.user.hasPerm(PERM_READ_RECORD_CODE)) {
|
if (rdoc.uid !== this.user.uid && !this.user.hasPerm(PERM.PERM_READ_RECORD_CODE)) {
|
||||||
rdoc.code = null;
|
rdoc.code = null;
|
||||||
}
|
}
|
||||||
if (rdoc.type !== 'run') rdoc.stdout = rdoc.stderr = '';
|
if (rdoc.type !== 'run') rdoc.stdout = rdoc.stderr = '';
|
||||||
@@ -85,7 +85,7 @@ class RecordDetailHandler extends RecordHandler {
|
|||||||
|
|
||||||
class RecordRejudgeHandler extends Handler {
|
class RecordRejudgeHandler extends Handler {
|
||||||
async post({ domainId, rid }) {
|
async post({ domainId, rid }) {
|
||||||
this.checkPerm(PERM_REJUDGE);
|
this.checkPerm(PERM.PERM_REJUDGE);
|
||||||
const rdoc = await record.get(domainId, rid);
|
const rdoc = await record.get(domainId, rid);
|
||||||
if (rdoc) await record.rejudge(domainId, rid);
|
if (rdoc) await record.rejudge(domainId, rid);
|
||||||
this.back();
|
this.back();
|
||||||
@@ -131,7 +131,7 @@ class RecordMainConnectionHandler extends RecordConnectionHandler {
|
|||||||
user.getById(this.domainId, rdoc.uid),
|
user.getById(this.domainId, rdoc.uid),
|
||||||
problem.get(this.domainId, rdoc.pid, null, false),
|
problem.get(this.domainId, rdoc.pid, null, false),
|
||||||
]);
|
]);
|
||||||
if (pdoc && pdoc.hidden && !this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null;
|
if (pdoc && pdoc.hidden && !this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) pdoc = null;
|
||||||
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
|
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ class RecordDetailConnectionHandler extends contest.ContestHandlerMixin(Connecti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('record_main', '/record', RecordListHandler);
|
Route('record_main', '/record', RecordListHandler);
|
||||||
Route('record_detail', '/record/:rid', RecordDetailHandler);
|
Route('record_detail', '/record/:rid', RecordDetailHandler);
|
||||||
Route('record_rejudge', '/record/:rid/rejudge', RecordRejudgeHandler);
|
Route('record_rejudge', '/record/:rid/rejudge', RecordRejudgeHandler);
|
||||||
@@ -173,4 +173,4 @@ async function apply() {
|
|||||||
Connection('record_detail_conn', '/record-detail-conn', RecordDetailConnectionHandler);
|
Connection('record_detail_conn', '/record-detail-conn', RecordDetailConnectionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.record = module.exports = apply;
|
global.Hydro.handler.record = apply;
|
||||||
@@ -1,21 +1,14 @@
|
|||||||
const assert = require('assert');
|
import assert from 'assert';
|
||||||
const { ValidationError, ProblemNotFoundError } = require('../error');
|
import { ValidationError, ProblemNotFoundError } from '../error';
|
||||||
const paginate = require('../lib/paginate');
|
import paginate from '../lib/paginate';
|
||||||
const {
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
PERM: {
|
import * as problem from '../model/problem';
|
||||||
PERM_VIEW_TRAINING, PERM_VIEW_PROBLEM_HIDDEN, PERM_CREATE_TRAINING,
|
import * as builtin from '../model/builtin';
|
||||||
PERM_EDIT_TRAINING, PERM_EDIT_TRAINING_SELF,
|
import * as training from '../model/training';
|
||||||
},
|
import * as user from '../model/user';
|
||||||
PRIV: {
|
import * as system from '../model/system';
|
||||||
PRIV_USER_PROFILE,
|
import { Route, Handler } from '../service/server';
|
||||||
},
|
import { ObjectID } from 'mongodb';
|
||||||
} = require('../model/builtin');
|
|
||||||
const problem = require('../model/problem');
|
|
||||||
const builtin = require('../model/builtin');
|
|
||||||
const training = require('../model/training');
|
|
||||||
const user = require('../model/user');
|
|
||||||
const system = require('../model/system');
|
|
||||||
const { Route, Handler } = require('../service/server');
|
|
||||||
|
|
||||||
async function _parseDagJson(domainId, dag) {
|
async function _parseDagJson(domainId, dag) {
|
||||||
const parsed = [];
|
const parsed = [];
|
||||||
@@ -65,12 +58,12 @@ class TrainingMainHandler extends Handler {
|
|||||||
page,
|
page,
|
||||||
await system.get('TRAINING_PER_PAGE'),
|
await system.get('TRAINING_PER_PAGE'),
|
||||||
);
|
);
|
||||||
const tids = new Set();
|
const tids: Set<ObjectID> = new Set();
|
||||||
for (const tdoc of tdocs) tids.add(tdoc.docId);
|
for (const tdoc of tdocs) tids.add(tdoc.docId);
|
||||||
const tsdict = {};
|
const tsdict = {};
|
||||||
let tdict = {};
|
let tdict = {};
|
||||||
if (this.user.hasPriv(PRIV_USER_PROFILE)) {
|
if (this.user.hasPriv(PRIV.PRIV_USER_PROFILE)) {
|
||||||
const enrolledTids = new Set();
|
const enrolledTids: Set<ObjectID> = new Set();
|
||||||
const tsdocs = await training.getMultiStatus(domainId, {
|
const tsdocs = await training.getMultiStatus(domainId, {
|
||||||
uid: this.user._id,
|
uid: this.user._id,
|
||||||
$or: [{ docId: { $in: Array.from(tids) } }, { enroll: 1 }],
|
$or: [{ docId: { $in: Array.from(tids) } }, { enroll: 1 }],
|
||||||
@@ -84,7 +77,7 @@ class TrainingMainHandler extends Handler {
|
|||||||
tdict = await training.getList(domainId, Array.from(enrolledTids));
|
tdict = await training.getList(domainId, Array.from(enrolledTids));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const tdoc in tdocs) tdict[tdoc.docId] = tdoc;
|
for (const tdoc of tdocs) tdict[tdoc.docId] = tdoc;
|
||||||
const path = [
|
const path = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['training_main', null],
|
['training_main', null],
|
||||||
@@ -102,7 +95,7 @@ class TrainingDetailHandler extends Handler {
|
|||||||
const pids = training.getPids(tdoc);
|
const pids = training.getPids(tdoc);
|
||||||
const [owner, pdict] = await Promise.all([
|
const [owner, pdict] = await Promise.all([
|
||||||
user.getById(domainId, tdoc.owner),
|
user.getById(domainId, tdoc.owner),
|
||||||
problem.getList(domainId, pids, this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)),
|
problem.getList(domainId, pids, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)),
|
||||||
]);
|
]);
|
||||||
const psdict = await problem.getListStatus(domainId, this.user._id, pids);
|
const psdict = await problem.getListStatus(domainId, this.user._id, pids);
|
||||||
const donePids = new Set();
|
const donePids = new Set();
|
||||||
@@ -123,7 +116,7 @@ class TrainingDetailHandler extends Handler {
|
|||||||
const totalCount = node.pids.length;
|
const totalCount = node.pids.length;
|
||||||
const doneCount = Set.union(new Set(node.pids), donePids).size;
|
const doneCount = Set.union(new Set(node.pids), donePids).size;
|
||||||
const nsdoc = {
|
const nsdoc = {
|
||||||
progress: totalCount ? parseInt(100 * (doneCount / totalCount), 10) : 100,
|
progress: totalCount ? Math.floor(100 * (doneCount / totalCount)) : 100,
|
||||||
isDone: training.isDone(node, doneNids, donePids),
|
isDone: training.isDone(node, doneNids, donePids),
|
||||||
isProgress: training.isProgress(node, doneNids, donePids, progPids),
|
isProgress: training.isProgress(node, doneNids, donePids, progPids),
|
||||||
isOpen: training.isOpen(node, doneNids, donePids, progPids),
|
isOpen: training.isOpen(node, doneNids, donePids, progPids),
|
||||||
@@ -149,7 +142,7 @@ class TrainingDetailHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async postEnroll({ domainId, tid }) {
|
async postEnroll({ domainId, tid }) {
|
||||||
this.checkPriv(PRIV_USER_PROFILE);
|
this.checkPriv(PRIV.PRIV_USER_PROFILE);
|
||||||
const tdoc = await training.get(domainId, tid);
|
const tdoc = await training.get(domainId, tid);
|
||||||
await training.enroll(domainId, tdoc.docId, this.user._id);
|
await training.enroll(domainId, tdoc.docId, this.user._id);
|
||||||
this.back();
|
this.back();
|
||||||
@@ -186,8 +179,8 @@ class TrainingCreateHandler extends Handler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const pdoc in pdocs) {
|
for (const pdoc of pdocs) {
|
||||||
if (pdoc.hidden) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
if (pdoc.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
}
|
}
|
||||||
const tid = await training.add(domainId, title, content, this.user._id, dag, description);
|
const tid = await training.add(domainId, title, content, this.user._id, dag, description);
|
||||||
this.response.body = { tid };
|
this.response.body = { tid };
|
||||||
@@ -198,8 +191,8 @@ class TrainingCreateHandler extends Handler {
|
|||||||
class TrainingEditHandler extends Handler {
|
class TrainingEditHandler extends Handler {
|
||||||
async prepare({ domainId, tid }) {
|
async prepare({ domainId, tid }) {
|
||||||
this.tdoc = await training.get(domainId, tid);
|
this.tdoc = await training.get(domainId, tid);
|
||||||
if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_TRAINING);
|
if (this.tdoc.owner !== this.user._id) this.checkPerm(PERM.PERM_EDIT_TRAINING);
|
||||||
else this.checkPerm(PERM_EDIT_TRAINING_SELF);
|
else this.checkPerm(PERM.PERM_EDIT_TRAINING_SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get({ tid }) {
|
async get({ tid }) {
|
||||||
@@ -238,8 +231,8 @@ class TrainingEditHandler extends Handler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const pdoc in pdocs) {
|
for (const pdoc of pdocs) {
|
||||||
if (pdoc.hidden) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
if (pdoc.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
}
|
}
|
||||||
await training.edit(domainId, tid, {
|
await training.edit(domainId, tid, {
|
||||||
title, content, dag, description,
|
title, content, dag, description,
|
||||||
@@ -249,11 +242,11 @@ class TrainingEditHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('training_main', '/training', TrainingMainHandler, PERM_VIEW_TRAINING);
|
Route('training_main', '/training', TrainingMainHandler, PERM.PERM_VIEW_TRAINING);
|
||||||
Route('training_create', '/training/create', TrainingCreateHandler, PERM_CREATE_TRAINING);
|
Route('training_create', '/training/create', TrainingCreateHandler, PERM.PERM_CREATE_TRAINING);
|
||||||
Route('training_detail', '/training/:tid', TrainingDetailHandler, PERM_VIEW_TRAINING);
|
Route('training_detail', '/training/:tid', TrainingDetailHandler, PERM.PERM_VIEW_TRAINING);
|
||||||
Route('training_edit', '/training/:tid/edit', TrainingEditHandler);
|
Route('training_edit', '/training/:tid/edit', TrainingEditHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.training = module.exports = apply;
|
global.Hydro.handler.training = apply;
|
||||||
@@ -1,27 +1,20 @@
|
|||||||
const superagent = require('superagent');
|
import superagent from 'superagent';
|
||||||
const moment = require('moment-timezone');
|
import moment from 'moment-timezone';
|
||||||
const { Route, Handler } = require('../service/server.js');
|
import { Route, Handler } from '../service/server';
|
||||||
const user = require('../model/user');
|
import * as user from '../model/user';
|
||||||
const token = require('../model/token');
|
import * as token from '../model/token';
|
||||||
const record = require('../model/record');
|
import * as record from '../model/record';
|
||||||
const problem = require('../model/problem');
|
import * as problem from '../model/problem';
|
||||||
const task = require('../model/task');
|
import * as task from '../model/task';
|
||||||
const system = require('../model/system');
|
import * as system from '../model/system';
|
||||||
const { sendMail } = require('../lib/mail');
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
const misc = require('../lib/misc');
|
import { sendMail } from '../lib/mail';
|
||||||
const {
|
import * as misc from '../lib/misc';
|
||||||
|
import {
|
||||||
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
|
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
|
||||||
UserNotFoundError, LoginError, SystemError,
|
UserNotFoundError, LoginError, SystemError,
|
||||||
BlacklistedError, UserFacingError,
|
BlacklistedError, UserFacingError,
|
||||||
} = require('../error');
|
} from '../error';
|
||||||
const {
|
|
||||||
PERM: {
|
|
||||||
PERM_VIEW_PROBLEM_HIDDEN,
|
|
||||||
},
|
|
||||||
PRIV: {
|
|
||||||
PRIV_USER_PROFILE, PRIV_REGISTER_USER, PRIV_NONE,
|
|
||||||
},
|
|
||||||
} = require('../model/builtin');
|
|
||||||
|
|
||||||
class UserLoginHandler extends Handler {
|
class UserLoginHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
@@ -41,7 +34,7 @@ class UserLoginHandler extends Handler {
|
|||||||
if (!udoc) throw new LoginError(uname);
|
if (!udoc) throw new LoginError(uname);
|
||||||
udoc.checkPassword(password);
|
udoc.checkPassword(password);
|
||||||
await user.setById(udoc._id, { loginat: new Date(), loginip: this.request.ip });
|
await user.setById(udoc._id, { loginat: new Date(), loginip: this.request.ip });
|
||||||
if (udoc.priv === PRIV_NONE) throw new BlacklistedError(uname);
|
if (udoc.priv === PRIV.PRIV_NONE) throw new BlacklistedError(uname);
|
||||||
this.session.uid = udoc._id;
|
this.session.uid = udoc._id;
|
||||||
this.session.save = rememberme;
|
this.session.save = rememberme;
|
||||||
this.response.redirect = this.request.referer.endsWith('/login') ? '/' : this.request.referer;
|
this.response.redirect = this.request.referer.endsWith('/login') ? '/' : this.request.referer;
|
||||||
@@ -116,7 +109,7 @@ class UserLostPassHandler extends Handler {
|
|||||||
|
|
||||||
async post({ mail }) {
|
async post({ mail }) {
|
||||||
if (!await system.get('smtp.user')) throw new SystemError('Cannot send mail');
|
if (!await system.get('smtp.user')) throw new SystemError('Cannot send mail');
|
||||||
const udoc = await user.getByEmail(mail);
|
const udoc = await user.getByEmail('system', mail);
|
||||||
if (!udoc) throw new UserNotFoundError(mail);
|
if (!udoc) throw new UserNotFoundError(mail);
|
||||||
const [tid] = await token.add(
|
const [tid] = await token.add(
|
||||||
token.TYPE_LOSTPASS,
|
token.TYPE_LOSTPASS,
|
||||||
@@ -155,10 +148,14 @@ class UserDetailHandler extends Handler {
|
|||||||
const rdocs = await record.getByUid(domainId, uid);
|
const rdocs = await record.getByUid(domainId, uid);
|
||||||
const pdict = await problem.getList(
|
const pdict = await problem.getList(
|
||||||
domainId, rdocs.map((rdoc) => rdoc.pid),
|
domainId, rdocs.map((rdoc) => rdoc.pid),
|
||||||
this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN), false,
|
this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), false,
|
||||||
);
|
);
|
||||||
// Remove sensitive data
|
// Remove sensitive data
|
||||||
if (!isSelfProfile && sdoc) sdoc.createIp = sdoc.updateIp = sdoc._id = '';
|
if (!isSelfProfile && sdoc) {
|
||||||
|
sdoc.createIp = '';
|
||||||
|
sdoc.updateIp = '';
|
||||||
|
sdoc._id = '';
|
||||||
|
}
|
||||||
this.response.template = 'user_detail.html';
|
this.response.template = 'user_detail.html';
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
isSelfProfile, udoc, sdoc, rdocs, pdict,
|
isSelfProfile, udoc, sdoc, rdocs, pdict,
|
||||||
@@ -300,7 +297,7 @@ class OauthCallbackHandler extends Handler {
|
|||||||
if (udoc) {
|
if (udoc) {
|
||||||
this.session.uid = udoc._id;
|
this.session.uid = udoc._id;
|
||||||
} else {
|
} else {
|
||||||
this.checkPriv(PRIV_REGISTER_USER);
|
this.checkPriv(PRIV.PRIV_REGISTER_USER);
|
||||||
let username = '';
|
let username = '';
|
||||||
r.uname = r.uname || [];
|
r.uname = r.uname || [];
|
||||||
r.uname.push(String.random(16));
|
r.uname.push(String.random(16));
|
||||||
@@ -315,7 +312,7 @@ class OauthCallbackHandler extends Handler {
|
|||||||
const uid = await user.create({
|
const uid = await user.create({
|
||||||
mail: r.email, uname: username, password: String.random(32), regip: this.request.ip,
|
mail: r.email, uname: username, password: String.random(32), regip: this.request.ip,
|
||||||
});
|
});
|
||||||
const $set = {
|
const $set: any = {
|
||||||
oauth: args.type,
|
oauth: args.type,
|
||||||
};
|
};
|
||||||
if (r.bio) $set.bio = r.bio;
|
if (r.bio) $set.bio = r.bio;
|
||||||
@@ -326,18 +323,18 @@ class OauthCallbackHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('user_login', '/login', UserLoginHandler);
|
Route('user_login', '/login', UserLoginHandler);
|
||||||
Route('user_oauth', '/oauth/:type', OauthHandler);
|
Route('user_oauth', '/oauth/:type', OauthHandler);
|
||||||
Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler);
|
Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler);
|
||||||
Route('user_register', '/register', UserRegisterHandler, PRIV_REGISTER_USER);
|
Route('user_register', '/register', UserRegisterHandler, PRIV.PRIV_REGISTER_USER);
|
||||||
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler, PRIV_REGISTER_USER);
|
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler, PRIV.PRIV_REGISTER_USER);
|
||||||
Route('user_logout', '/logout', UserLogoutHandler, PRIV_USER_PROFILE);
|
Route('user_logout', '/logout', UserLogoutHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Route('user_lostpass', '/lostpass', UserLostPassHandler);
|
Route('user_lostpass', '/lostpass', UserLostPassHandler);
|
||||||
Route('user_lostpass_with_code', '/lostpass/:code', UserLostPassWithCodeHandler);
|
Route('user_lostpass_with_code', '/lostpass/:code', UserLostPassWithCodeHandler);
|
||||||
Route('user_search', '/user/search', UserSearchHandler);
|
Route('user_search', '/user/search', UserSearchHandler);
|
||||||
Route('user_delete', '/user/delete', UserDeleteHandler, PRIV_USER_PROFILE);
|
Route('user_delete', '/user/delete', UserDeleteHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Route('user_detail', '/user/:uid', UserDetailHandler);
|
Route('user_detail', '/user/:uid', UserDetailHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.user = module.exports = apply;
|
global.Hydro.handler.user = apply;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const { Route, Handler } = require('../service/server');
|
import { NotFoundError } from '../error';
|
||||||
const { NotFoundError } = require('../error');
|
import { Route, Handler } from '../service/server';
|
||||||
|
|
||||||
class WikiHandler extends Handler {
|
class WikiHandler extends Handler {
|
||||||
async get({ category = 'wiki', page }) {
|
async get({ category = 'wiki', page }) {
|
||||||
@@ -18,9 +18,9 @@ class WikiHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apply() {
|
export async function apply() {
|
||||||
Route('wiki', '/wiki/:page', WikiHandler);
|
Route('wiki', '/wiki/:page', WikiHandler);
|
||||||
Route('wiki_with_category', '/wiki/:category/:page', WikiHandler);
|
Route('wiki_with_category', '/wiki/:category/:page', WikiHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.handler.wiki = module.exports = apply;
|
global.Hydro.handler.wiki = apply;
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
const { ObjectID } = require('mongodb');
|
|
||||||
|
|
||||||
exports.udoc = {
|
|
||||||
_id: 0,
|
|
||||||
mail: '',
|
|
||||||
mailLower: '',
|
|
||||||
uname: '',
|
|
||||||
unameLower: '',
|
|
||||||
salt: '',
|
|
||||||
hash: '',
|
|
||||||
hashType: 'hydro',
|
|
||||||
nAccept: 0,
|
|
||||||
nSubmit: 0,
|
|
||||||
nLike: 0,
|
|
||||||
bio: '',
|
|
||||||
gender: 0,
|
|
||||||
regat: new Date(),
|
|
||||||
regip: '0.0.0.0',
|
|
||||||
gravatar: '',
|
|
||||||
viewLang: 'zh_CN',
|
|
||||||
loginat: new Date(),
|
|
||||||
loginip: '0.0.0.0',
|
|
||||||
priv: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.pdoc = {
|
|
||||||
_id: new ObjectID(),
|
|
||||||
pid: 'P1000',
|
|
||||||
owner: 0,
|
|
||||||
title: 'No Title',
|
|
||||||
content: 'No Content',
|
|
||||||
nSubmit: 0,
|
|
||||||
nAccept: 0,
|
|
||||||
tag: [],
|
|
||||||
category: [],
|
|
||||||
data: null,
|
|
||||||
hidden: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
export interface TestCase {
|
|
||||||
time: number,
|
|
||||||
memory: number,
|
|
||||||
status: number,
|
|
||||||
message: string
|
|
||||||
}
|
|
||||||
export interface Rdoc {
|
|
||||||
_id: ObjectID,
|
|
||||||
pid: ObjectID,
|
|
||||||
uid: number,
|
|
||||||
lang: string,
|
|
||||||
code: string,
|
|
||||||
score: number,
|
|
||||||
memory: number,
|
|
||||||
time: number,
|
|
||||||
judgeTexts: string[],
|
|
||||||
compilerTexts: string[],
|
|
||||||
testCases: TestCase[],
|
|
||||||
rejudged: boolean,
|
|
||||||
judger: string,
|
|
||||||
judgeAt: Date,
|
|
||||||
status: number
|
|
||||||
}
|
|
||||||
export interface Bdoc {
|
|
||||||
_id: string,
|
|
||||||
expireAt: Date
|
|
||||||
}
|
|
||||||
export interface Tdoc {
|
|
||||||
_id: ObjectID
|
|
||||||
beginAt: Date
|
|
||||||
endAt: Date
|
|
||||||
attend: number
|
|
||||||
title: string
|
|
||||||
content: string
|
|
||||||
pids: ObjectID[]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import { ObjectID } from 'mongodb';
|
|
||||||
|
|
||||||
export interface Udoc {
|
|
||||||
_id: number
|
|
||||||
mail: string
|
|
||||||
mailLower: string
|
|
||||||
uname: string
|
|
||||||
unameLower: string
|
|
||||||
salt: string
|
|
||||||
hash: string
|
|
||||||
hashType: string
|
|
||||||
nAccept: number
|
|
||||||
nSubmit: number
|
|
||||||
nLike: number
|
|
||||||
perm: string
|
|
||||||
viewLang: string
|
|
||||||
codeLang: string
|
|
||||||
codeTemplate: string
|
|
||||||
gravatar: string
|
|
||||||
hasPerm: Function
|
|
||||||
checkPassword: Function
|
|
||||||
}
|
|
||||||
export interface Pdoc {
|
|
||||||
_id: ObjectID
|
|
||||||
pid: string
|
|
||||||
owner: number
|
|
||||||
title: string
|
|
||||||
content: string
|
|
||||||
nSubmit: number
|
|
||||||
nAccept: number
|
|
||||||
tag: string[]
|
|
||||||
category: string[],
|
|
||||||
data: ObjectID | null
|
|
||||||
hidden: boolean
|
|
||||||
}
|
|
||||||
export interface TestCase {
|
|
||||||
time: number,
|
|
||||||
memory: number,
|
|
||||||
status: number,
|
|
||||||
message: string
|
|
||||||
}
|
|
||||||
export interface Rdoc {
|
|
||||||
_id: ObjectID,
|
|
||||||
pid: ObjectID,
|
|
||||||
uid: number,
|
|
||||||
lang: string,
|
|
||||||
code: string,
|
|
||||||
score: number,
|
|
||||||
memory: number,
|
|
||||||
time: number,
|
|
||||||
judgeTexts: string[],
|
|
||||||
compilerTexts: string[],
|
|
||||||
testCases: TestCase[],
|
|
||||||
rejudged: boolean,
|
|
||||||
judger: string,
|
|
||||||
judgeAt: Date,
|
|
||||||
status: number
|
|
||||||
}
|
|
||||||
export interface Bdoc {
|
|
||||||
_id: string,
|
|
||||||
expireAt: Date
|
|
||||||
}
|
|
||||||
export interface Tdoc {
|
|
||||||
_id: ObjectID
|
|
||||||
beginAt: Date
|
|
||||||
endAt: Date
|
|
||||||
attend: number
|
|
||||||
title: string
|
|
||||||
content: string
|
|
||||||
pids: ObjectID[]
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
const superagent = require('superagent');
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
async function _download(url, path, retry) {
|
|
||||||
const w = fs.createWriteStream(path);
|
|
||||||
await superagent.get(url).retry(retry).pipe(w);
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
w.on('finish', resolve);
|
|
||||||
w.on('error', reject);
|
|
||||||
});
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function download(url, path, retry = 3) {
|
|
||||||
if (path) return _download(url, path, retry);
|
|
||||||
return superagent.get(url).retry(retry);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.lib.download = module.exports = download;
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import superagent from 'superagent';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
async function _download(url: string, path: string, retry: number) {
|
||||||
|
const w = fs.createWriteStream(path);
|
||||||
|
superagent.get(url).retry(retry).pipe(w);
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
w.on('finish', resolve);
|
||||||
|
w.on('error', reject);
|
||||||
|
});
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function download(url: string, path: string | undefined | null, retry = 3) {
|
||||||
|
if (path) return _download(url, path, retry);
|
||||||
|
return superagent.get(url).retry(retry);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.lib.download = download;
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
const crypto = require('crypto');
|
|
||||||
|
|
||||||
function hash(password, salt) {
|
|
||||||
return crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha256').toString('hex').substr(0, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.lib['hash.hydro'] = module.exports = hash;
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
export default function hash(password: string, salt: string) {
|
||||||
|
return crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha256').toString('hex').substr(0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.lib['hash.hydro'] = hash;
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
/* eslint-disable import/no-dynamic-require */
|
/* eslint-disable import/no-dynamic-require */
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const download = require('./download');
|
import download from './download';
|
||||||
const { folderSize } = require('../utils');
|
import { folderSize } from '../utils';
|
||||||
|
|
||||||
function root(name) {
|
function root(name: string) {
|
||||||
return path.resolve(process.cwd(), name);
|
return path.resolve(process.cwd(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,8 @@ const moduleRoots = [
|
|||||||
root(path.resolve(os.homedir(), '.hydro', 'module')),
|
root(path.resolve(os.homedir(), '.hydro', 'module')),
|
||||||
root('.'),
|
root('.'),
|
||||||
];
|
];
|
||||||
let moduleRoot;
|
|
||||||
|
let moduleRoot: string;
|
||||||
for (const i of moduleRoots) {
|
for (const i of moduleRoots) {
|
||||||
if (fs.existsSync(i) && fs.statSync(i).isDirectory()) {
|
if (fs.existsSync(i) && fs.statSync(i).isDirectory()) {
|
||||||
moduleRoot = i;
|
moduleRoot = i;
|
||||||
@@ -25,7 +26,7 @@ for (const i of moduleRoots) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getInstalled() {
|
export async function getInstalled() {
|
||||||
const modules = [];
|
const modules = [];
|
||||||
const files = fs.readdirSync(`${os.tmpdir()}/hydro/tmp`);
|
const files = fs.readdirSync(`${os.tmpdir()}/hydro/tmp`);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -37,7 +38,7 @@ async function getInstalled() {
|
|||||||
return modules;
|
return modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDetail() {
|
export async function getDetail() {
|
||||||
const modules = [];
|
const modules = [];
|
||||||
const files = fs.readdirSync(`${os.tmpdir()}/hydro/tmp`);
|
const files = fs.readdirSync(`${os.tmpdir()}/hydro/tmp`);
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -56,14 +57,14 @@ async function getDetail() {
|
|||||||
return modules;
|
return modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function del(id) {
|
export async function del(id: string) {
|
||||||
fs.unlinkSync(root(`${moduleRoot}/${id}.hydro`));
|
fs.unlinkSync(root(`${moduleRoot}/${id}.hydro`));
|
||||||
}
|
}
|
||||||
|
|
||||||
function install(url) {
|
export function install(url: string) {
|
||||||
return download(url, root(`${moduleRoot}/${String.random(16)}.hydro`));
|
return download(url, root(`${moduleRoot}/${String.random(16)}.hydro`));
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.hpm = module.exports = {
|
global.Hydro.lib.hpm = {
|
||||||
getInstalled, getDetail, del, install,
|
getInstalled, getDetail, del, install,
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
const locales = {};
|
const locales = {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface String {
|
||||||
|
format: (...args: Array<any>) => string;
|
||||||
|
rawformat: (object: any) => string;
|
||||||
|
translate: (language: string | undefined) => string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String.prototype.format = function formatStr(...args) {
|
String.prototype.format = function formatStr(...args) {
|
||||||
let result = this;
|
let result = this;
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
@@ -24,7 +32,7 @@ String.prototype.format = function formatStr(...args) {
|
|||||||
|
|
||||||
String.prototype.rawformat = function rawFormat(object) {
|
String.prototype.rawformat = function rawFormat(object) {
|
||||||
const res = this.split('{@}');
|
const res = this.split('{@}');
|
||||||
return [res[0], object, res[1]];
|
return [res[0], object, res[1]].join();
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.translate = function translate(language = 'zh_CN') {
|
String.prototype.translate = function translate(language = 'zh_CN') {
|
||||||
@@ -34,7 +42,7 @@ String.prototype.translate = function translate(language = 'zh_CN') {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
function load(data) {
|
export default function load(data) {
|
||||||
for (const i in data) {
|
for (const i in data) {
|
||||||
if (!locales[i]) locales[i] = data[i];
|
if (!locales[i]) locales[i] = data[i];
|
||||||
else locales[i] = Object.assign(locales[i], data[i]);
|
else locales[i] = Object.assign(locales[i], data[i]);
|
||||||
@@ -42,4 +50,4 @@ function load(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.locales = locales;
|
global.Hydro.locales = locales;
|
||||||
global.Hydro.lib.i18n = module.exports = load;
|
global.Hydro.lib.i18n = load;
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
function decodeBase64WithUriEncoding(encodedText) {
|
function decodeBase64WithUriEncoding(encodedText: string) {
|
||||||
return Buffer.from(encodedText, 'base64').toString('utf8');
|
return Buffer.from(encodedText, 'base64').toString('utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
function unescapedString(escapedString) {
|
function unescapedString(escapedString: string) {
|
||||||
escapedString += new Array(5 - (escapedString.length % 4)).join('=');
|
escapedString += new Array(5 - (escapedString.length % 4)).join('=');
|
||||||
return escapedString.replace(/-/g, '+').replace(/_/g, '/');
|
return escapedString.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeJWT(idToken) {
|
function decodeJWT(idToken: string) {
|
||||||
const token = idToken.split('.');
|
const token = idToken.split('.');
|
||||||
if (token.length !== 3) {
|
if (token.length !== 3) {
|
||||||
throw new Error('Invalid idToken');
|
throw new Error('Invalid idToken');
|
||||||
@@ -27,9 +27,10 @@ function decodeJWT(idToken) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decode(idToken) {
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export function decode(idToken: string) {
|
||||||
const decodedJWT = decodeJWT(idToken);
|
const decodedJWT = decodeJWT(idToken);
|
||||||
return decodedJWT.payload;
|
return decodedJWT.payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.jwt = module.exports = { decode };
|
global.Hydro.lib.jwt = { decode };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
const yaml = require('js-yaml');
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
function warp(func, type) {
|
function wrap(func: Function, type: string) {
|
||||||
return (...args) => {
|
return (...args: any) => {
|
||||||
const time = new Date();
|
const time = new Date();
|
||||||
func(...args);
|
func(...args);
|
||||||
if (global.Hydro.model.message) {
|
if (global.Hydro.model.message) {
|
||||||
@@ -21,13 +21,27 @@ function warp(func, type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
|
log: (...args: any[]) => void;
|
||||||
|
|
||||||
|
error: (...args: any[]) => void;
|
||||||
|
|
||||||
|
info: (...args: any[]) => void;
|
||||||
|
|
||||||
|
warn: (...args: any[]) => void;
|
||||||
|
|
||||||
|
debug: (...args: any[]) => void;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.log = warp(console.log, 'log');
|
this.log = wrap(console.log, 'log');
|
||||||
this.error = warp(console.error, 'error');
|
this.error = wrap(console.error, 'error');
|
||||||
this.info = warp(console.info, 'info');
|
this.info = wrap(console.info, 'info');
|
||||||
this.warn = warp(console.warn, 'warn');
|
this.warn = wrap(console.warn, 'warn');
|
||||||
this.debug = warp(console.debug, 'debug');
|
this.debug = wrap(console.debug, 'debug');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.logger = module.exports = new Logger();
|
const exp = new Logger();
|
||||||
|
|
||||||
|
global.Hydro.lib.logger = exp;
|
||||||
|
|
||||||
|
export default exp;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
const nodemailer = require('nodemailer');
|
import nodemailer from 'nodemailer';
|
||||||
|
|
||||||
const { SendMailError } = require('../error');
|
const { SendMailError } = require('../error');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,8 +8,9 @@ const { SendMailError } = require('../error');
|
|||||||
* @param {string} text
|
* @param {string} text
|
||||||
* @param {string} html
|
* @param {string} html
|
||||||
*/
|
*/
|
||||||
async function sendMail(to, subject, text, html) {
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
let t;
|
export async function sendMail(to: string, subject: string, text: string, html: string) {
|
||||||
|
let t: unknown;
|
||||||
try {
|
try {
|
||||||
const { system } = global.Hydro.model;
|
const { system } = global.Hydro.model;
|
||||||
const [host, port, secure, user, pass, from] = await Promise.all([
|
const [host, port, secure, user, pass, from] = await Promise.all([
|
||||||
@@ -40,6 +42,6 @@ async function sendMail(to, subject, text, html) {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.mail = module.exports = {
|
global.Hydro.lib.mail = {
|
||||||
sendMail,
|
sendMail,
|
||||||
};
|
};
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
const MarkdownIt = require('markdown-it');
|
import MarkdownIt from 'markdown-it';
|
||||||
|
import Prism from 'prismjs';
|
||||||
const md = MarkdownIt();
|
|
||||||
const Prism = require('prismjs');
|
|
||||||
|
|
||||||
// For math: $a\times b\eq 10$
|
// For math: $a\times b\eq 10$
|
||||||
const katex = require('@hydrooj/markdown-it-katex');
|
import katex from '@hydrooj/markdown-it-katex';
|
||||||
// Specific image size: 
|
// Specific image size: 
|
||||||
const Imsize = require('markdown-it-imsize');
|
import Imsize from 'markdown-it-imsize';
|
||||||
/* Footnote support.
|
/* Footnote support.
|
||||||
Here is a footnote reference,[^1] and another.[^longnote]
|
Here is a footnote reference,[^1] and another.[^longnote]
|
||||||
|
|
||||||
@@ -17,17 +15,18 @@ Here is a footnote reference,[^1] and another.[^longnote]
|
|||||||
Subsequent paragraphs are indented to show that they
|
Subsequent paragraphs are indented to show that they
|
||||||
belong to the previous footnote.
|
belong to the previous footnote.
|
||||||
*/
|
*/
|
||||||
const Footnote = require('markdown-it-footnote');
|
import Footnote from 'markdown-it-footnote';
|
||||||
// ==Highlight==
|
// ==Highlight==
|
||||||
const Mark = require('markdown-it-mark');
|
import Mark from 'markdown-it-mark';
|
||||||
// :::warn This page requires javascript. :::
|
// :::warn This page requires javascript. :::
|
||||||
// :::record-pass Accepted :::
|
// :::record-pass Accepted :::
|
||||||
const Container = require('markdown-it-container');
|
import Container from 'markdown-it-container';
|
||||||
let Anchor = require('markdown-it-anchor');
|
|
||||||
// For webpack
|
|
||||||
if (Anchor.default) Anchor = Anchor.default;
|
|
||||||
|
|
||||||
const TOC = require('markdown-it-table-of-contents');
|
import TOC from 'markdown-it-table-of-contents';
|
||||||
|
|
||||||
|
import Anchor from 'markdown-it-anchor';
|
||||||
|
|
||||||
|
const md = MarkdownIt();
|
||||||
|
|
||||||
require('prismjs/components/index');
|
require('prismjs/components/index');
|
||||||
|
|
||||||
@@ -78,7 +77,9 @@ class Markdown extends MarkdownIt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.markdown = module.exports = {
|
export const safe = new Markdown(true);
|
||||||
unsafe: new Markdown(false),
|
export const unsafe = new Markdown(false);
|
||||||
safe: new Markdown(true),
|
|
||||||
|
global.Hydro.lib.markdown = {
|
||||||
|
safe, unsafe,
|
||||||
};
|
};
|
||||||
@@ -130,4 +130,8 @@ function rstr2hex(input) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.md5 = module.exports = (s) => rstr2hex(rstrMD5(unescape(encodeURIComponent(s))));
|
export default function md5(s: string) {
|
||||||
|
return rstr2hex(rstrMD5(unescape(encodeURIComponent(s))));
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.lib.md5 = md5;
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
const { default: AnsiUp } = require('ansi_up');
|
import AnsiUp from 'ansi_up';
|
||||||
const md5 = require('./md5');
|
import md5 from './md5';
|
||||||
|
|
||||||
const AU = new AnsiUp();
|
const AU = new AnsiUp();
|
||||||
|
|
||||||
function ansiToHtml(str) {
|
export function ansiToHtml(str) {
|
||||||
return AU.ansi_to_html(str);
|
return AU.ansi_to_html(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function gravatar(email, s = 32) {
|
export function gravatar(email, s = 32) {
|
||||||
return `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${s}`;
|
return `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${s}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function datetimeSpan(dt, relative = true, format = '%Y-%m-%d %H:%M:%S') {
|
export function datetimeSpan(dt, relative = true, format = '%Y-%m-%d %H:%M:%S') {
|
||||||
if (!dt) return 'DATETIME_SPAN_ERROR';
|
if (!dt) return 'DATETIME_SPAN_ERROR';
|
||||||
if (dt.generationTime) dt = new Date(dt.generationTime * 1000);
|
if (dt.generationTime) dt = new Date(dt.generationTime * 1000);
|
||||||
else if (typeof dt === 'number' || typeof dt === 'string') dt = new Date(dt);
|
else if (typeof dt === 'number' || typeof dt === 'string') dt = new Date(dt);
|
||||||
@@ -22,7 +22,7 @@ function datetimeSpan(dt, relative = true, format = '%Y-%m-%d %H:%M:%S') {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function* paginate(page, numPages) {
|
export function* paginate(page, numPages) {
|
||||||
const radius = 2; let first; let
|
const radius = 2; let first; let
|
||||||
last;
|
last;
|
||||||
if (page > 1) {
|
if (page > 1) {
|
||||||
@@ -45,7 +45,7 @@ function* paginate(page, numPages) {
|
|||||||
yield ['last', numPages];
|
yield ['last', numPages];
|
||||||
}
|
}
|
||||||
|
|
||||||
function size(s, base = 1) {
|
export function size(s, base = 1) {
|
||||||
s *= base;
|
s *= base;
|
||||||
const unit = 1024;
|
const unit = 1024;
|
||||||
const unitNames = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
const unitNames = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||||
@@ -56,13 +56,13 @@ function size(s, base = 1) {
|
|||||||
return '{0} {1}'.format(Math.round(s * unit), unitNames[unitNames.length - 1]);
|
return '{0} {1}'.format(Math.round(s * unit), unitNames[unitNames.length - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _digit2(number) {
|
export function _digit2(number: number) {
|
||||||
if (number < 10) return `0${number}`;
|
if (number < 10) return `0${number}`;
|
||||||
return number.toString();
|
return number.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSeconds(seconds = 0) {
|
export function formatSeconds(_seconds = '0') {
|
||||||
seconds = parseInt(seconds, 10);
|
const seconds = parseInt(_seconds, 10);
|
||||||
return '{0}:{1}:{2}'.format(
|
return '{0}:{1}:{2}'.format(
|
||||||
_digit2(Math.floor(seconds / 3600)),
|
_digit2(Math.floor(seconds / 3600)),
|
||||||
_digit2(Math.floor((seconds % 3600) / 60)),
|
_digit2(Math.floor((seconds % 3600) / 60)),
|
||||||
@@ -70,6 +70,6 @@ function formatSeconds(seconds = 0) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.misc = module.exports = {
|
global.Hydro.lib.misc = {
|
||||||
gravatar, datetimeSpan, paginate, size, formatSeconds, ansiToHtml,
|
gravatar, datetimeSpan, paginate, size, formatSeconds, ansiToHtml,
|
||||||
};
|
};
|
||||||
@@ -1,27 +1,34 @@
|
|||||||
const { isNull } = require('lodash');
|
import { isNull } from 'lodash';
|
||||||
const { PRIV, PERM } = require('../model/builtin');
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
|
|
||||||
global.Hydro.ui.nav = [];
|
global.Hydro.ui.nav = [];
|
||||||
|
|
||||||
const trueChecker = () => true;
|
const trueChecker = () => true;
|
||||||
const Checker = (perm, priv, checker = trueChecker) => (handler) => (
|
const Checker = (perm: string, priv: number, checker: Function = trueChecker) => (handler) => (
|
||||||
checker(handler)
|
checker(handler)
|
||||||
&& (perm ? handler.user.hasPerm(perm) : true)
|
&& (perm ? handler.user.hasPerm(perm) : true)
|
||||||
&& (priv ? handler.user.hasPriv(priv) : true)
|
&& (priv ? handler.user.hasPriv(priv) : true)
|
||||||
);
|
);
|
||||||
|
|
||||||
const Item = (name, args, prefix, ...permPrivChecker) => {
|
const Item = (
|
||||||
let _priv;
|
name: string, args: any, prefix: string,
|
||||||
let _perm;
|
...permPrivChecker: Array<number | string | Function | Array<number | string>>
|
||||||
let checker = trueChecker;
|
) => {
|
||||||
|
let _priv: number;
|
||||||
|
let _perm: string;
|
||||||
|
let checker: Function = trueChecker;
|
||||||
for (const item of permPrivChecker) {
|
for (const item of permPrivChecker) {
|
||||||
if (typeof item === 'object' && !isNull(item)) {
|
if (item instanceof Object && !isNull(item)) {
|
||||||
if (typeof item.call !== 'undefined') {
|
if (item instanceof Array) {
|
||||||
|
if (typeof item[0] === 'number') {
|
||||||
|
// @ts-ignore
|
||||||
|
_priv = item;
|
||||||
|
} else if (typeof item[0] === 'string') {
|
||||||
|
// @ts-ignore
|
||||||
|
_perm = item;
|
||||||
|
}
|
||||||
|
} else if (typeof item.call !== 'undefined') {
|
||||||
checker = item;
|
checker = item;
|
||||||
} if (typeof item[0] === 'number') {
|
|
||||||
_priv = item;
|
|
||||||
} else if (typeof item[0] === 'string') {
|
|
||||||
_perm = item;
|
|
||||||
}
|
}
|
||||||
} else if (typeof item === 'number') {
|
} else if (typeof item === 'number') {
|
||||||
_priv = item;
|
_priv = item;
|
||||||
@@ -46,4 +53,5 @@ Item('ranking', null, 'ranking', PERM.PERM_VIEW_RANKING);
|
|||||||
Item('domain_dashboard', null, 'domain', PERM.PERM_EDIT_DOMAIN);
|
Item('domain_dashboard', null, 'domain', PERM.PERM_EDIT_DOMAIN);
|
||||||
Item('manage_dashboard', null, 'manage', PRIV.PRIV_EDIT_SYSTEM);
|
Item('manage_dashboard', null, 'manage', PRIV.PRIV_EDIT_SYSTEM);
|
||||||
|
|
||||||
global.Hydro.lib.nav = module.exports = Item;
|
global.Hydro.lib.nav = Item;
|
||||||
|
export default Item;
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
const { ValidationError } = require('../error');
|
import { ValidationError } from '../error';
|
||||||
|
|
||||||
/**
|
async function paginate(cursor: import('mongodb').Cursor, page: number, pageSize: number): Promise<[any[], number, number]> {
|
||||||
* @param {import('mongodb').Cursor} cursor
|
|
||||||
* @param {number} page
|
|
||||||
* @param {number} pageSize
|
|
||||||
* @returns {Promise<Array[]>} pageDocs numPages count
|
|
||||||
*/
|
|
||||||
async function paginate(cursor, page, pageSize) {
|
|
||||||
if (page <= 0) throw new ValidationError('page');
|
if (page <= 0) throw new ValidationError('page');
|
||||||
const [count, pageDocs] = await Promise.all([
|
const [count, pageDocs] = await Promise.all([
|
||||||
cursor.count(),
|
cursor.count(),
|
||||||
@@ -16,4 +10,5 @@ async function paginate(cursor, page, pageSize) {
|
|||||||
return [pageDocs, numPages, count];
|
return [pageDocs, numPages, count];
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.paginate = module.exports = paginate;
|
global.Hydro.lib.paginate = paginate;
|
||||||
|
export default paginate;
|
||||||
@@ -10,4 +10,5 @@ function* ranked(diter, equ = (a, b) => a === b) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.rank = module.exports = ranked;
|
global.Hydro.lib.rank = ranked;
|
||||||
|
export default ranked;
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
class User {
|
class User {
|
||||||
constructor(rank, old, uid = 0) {
|
rank: any;
|
||||||
|
|
||||||
|
old: any;
|
||||||
|
|
||||||
|
seed: number;
|
||||||
|
|
||||||
|
uid: number;
|
||||||
|
|
||||||
|
constructor(rank: number, old: number, uid = 0) {
|
||||||
this.rank = rank;
|
this.rank = rank;
|
||||||
this.old = old;
|
this.old = old;
|
||||||
this.seed = 1;
|
this.seed = 1;
|
||||||
@@ -8,6 +16,8 @@ class User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RatingCalculator {
|
class RatingCalculator {
|
||||||
|
users: any;
|
||||||
|
|
||||||
constructor(users) {
|
constructor(users) {
|
||||||
this.users = [];
|
this.users = [];
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
@@ -80,9 +90,10 @@ class RatingCalculator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculate(users) {
|
function calculate(users: any[]) {
|
||||||
const calculator = new RatingCalculator(users);
|
const calculator = new RatingCalculator(users);
|
||||||
return calculator.calculate();
|
return calculator.calculate();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = calculate;
|
global.Hydro.lib.rating = calculate;
|
||||||
|
export default calculate;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
const Zip = require('adm-zip');
|
import Zip from 'adm-zip';
|
||||||
|
|
||||||
async function readConfig(filePath) {
|
async function readConfig(filePath: string) {
|
||||||
const data = new Zip(filePath);
|
const data = new Zip(filePath);
|
||||||
const entries = data.getEntries();
|
const entries = data.getEntries();
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
@@ -16,4 +16,5 @@ async function readConfig(filePath) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.readConfig = module.exports = readConfig;
|
global.Hydro.lib.readConfig = readConfig;
|
||||||
|
export default readConfig;
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
const crypto = require('crypto');
|
|
||||||
/**
|
|
||||||
* @param {any} content
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
global.Hydro.lib.sha1 = module.exports = (content) => {
|
|
||||||
const hash = crypto.createHash('sha1');
|
|
||||||
hash.update(content);
|
|
||||||
return hash.digest('hex');
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
function sha1(content: string) {
|
||||||
|
const hash = crypto.createHash('sha1');
|
||||||
|
hash.update(content);
|
||||||
|
return hash.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.lib.sha1 = sha1;
|
||||||
|
export default sha1;
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
const systeminformation = require('systeminformation');
|
import systeminformation from 'systeminformation';
|
||||||
const { size } = require('./misc');
|
import { size } from './misc';
|
||||||
|
|
||||||
const cache = {};
|
const cache = {
|
||||||
|
mid: '', cpu: '', osinfo: '', flags: '',
|
||||||
|
};
|
||||||
|
|
||||||
async function get() {
|
export async function get() {
|
||||||
const [Cpu, Memory, OsInfo, CurrentLoad, CpuFlags, CpuTemp, Battery] = await Promise.all([
|
const [Cpu, Memory, OsInfo, CurrentLoad, CpuFlags, CpuTemp, Battery] = await Promise.all([
|
||||||
systeminformation.cpu(),
|
systeminformation.cpu(),
|
||||||
systeminformation.mem(),
|
systeminformation.mem(),
|
||||||
@@ -31,7 +33,7 @@ async function get() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update() {
|
export async function update(): Promise<[string, any, any]> {
|
||||||
const [Memory, CurrentLoad, CpuTemp, Battery] = await Promise.all([
|
const [Memory, CurrentLoad, CpuTemp, Battery] = await Promise.all([
|
||||||
systeminformation.mem(),
|
systeminformation.mem(),
|
||||||
systeminformation.currentLoad(),
|
systeminformation.currentLoad(),
|
||||||
@@ -57,4 +59,4 @@ async function update() {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.sysinfo = module.exports = { get, update };
|
global.Hydro.lib.sysinfo = { get, update };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
const nunjucks = require('nunjucks');
|
import nunjucks from 'nunjucks';
|
||||||
const markdown = require('./markdown');
|
import * as markdown from './markdown';
|
||||||
const { STATUS, PERM, PRIV } = require('../model/builtin');
|
import { STATUS, PERM, PRIV } from '../model/builtin';
|
||||||
const misc = require('./misc');
|
import * as misc from './misc';
|
||||||
|
|
||||||
function Loader() { }
|
function Loader() { }
|
||||||
Loader.prototype.getSource = function getSource(name) {
|
Loader.prototype.getSource = function getSource(name) {
|
||||||
@@ -28,7 +28,7 @@ class Nunjucks extends nunjucks.Environment {
|
|||||||
}
|
}
|
||||||
const env = new Nunjucks();
|
const env = new Nunjucks();
|
||||||
|
|
||||||
function render(name, state) {
|
export function render(name: string, state: any): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
env.render(name, {
|
env.render(name, {
|
||||||
page_name: name.split('.')[0],
|
page_name: name.split('.')[0],
|
||||||
@@ -44,7 +44,7 @@ function render(name, state) {
|
|||||||
gravatar: misc.gravatar,
|
gravatar: misc.gravatar,
|
||||||
model: global.Hydro.model,
|
model: global.Hydro.model,
|
||||||
Context: global.Hydro.ui,
|
Context: global.Hydro.ui,
|
||||||
isIE: (str) => str.includes('MSIE') || str.includes('rv:11.0'),
|
isIE: (str: string) => str.includes('MSIE') || str.includes('rv:11.0'),
|
||||||
}, (err, res) => {
|
}, (err, res) => {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
else resolve(res);
|
else resolve(res);
|
||||||
@@ -52,4 +52,4 @@ function render(name, state) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.template = module.exports = { render };
|
global.Hydro.lib.template = { render };
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
const yaml = require('js-yaml');
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
function convert(ini) {
|
export default function convert(ini: string) {
|
||||||
const f = ini.split('\n');
|
const f = ini.split('\n');
|
||||||
const count = f[0];
|
const count = parseInt(f[0], 10);
|
||||||
const res = { cases: [] };
|
const res = { cases: [] };
|
||||||
for (let i = 1; i <= count; i++) {
|
for (let i = 1; i <= count; i++) {
|
||||||
const [input, output, time, score, memory] = f[i].split('|');
|
const [input, output, time, score, memory] = f[i].split('|');
|
||||||
const cur = { input, output, score: parseInt(score, 10) };
|
const cur = {
|
||||||
cur.time = `${time}s`;
|
input, output, score: parseInt(score, 10), time: `${time}s`, memory: '128m',
|
||||||
|
};
|
||||||
if (!Number.isNaN(parseInt(memory, 10))) cur.memory = `${Math.floor(parseInt(memory, 10) / 1024)}m`;
|
if (!Number.isNaN(parseInt(memory, 10))) cur.memory = `${Math.floor(parseInt(memory, 10) / 1024)}m`;
|
||||||
else cur.memory = '128m';
|
|
||||||
res.cases.push(cur);
|
res.cases.push(cur);
|
||||||
}
|
}
|
||||||
if (res.cases.length > 0) {
|
if (res.cases.length > 0) {
|
||||||
@@ -29,4 +29,4 @@ function convert(ini) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib['testdata.convert.ini'] = module.exports = convert;
|
global.Hydro.lib['testdata.convert.ini'] = convert;
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
const browser = require('detect-browser');
|
|
||||||
|
|
||||||
function parse(str) {
|
|
||||||
return browser.parseUserAgent(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function icon(str) {
|
|
||||||
return str.split(' ')[0].toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.lib.useragent = module.exports = { parse, icon };
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import browser from 'detect-browser';
|
||||||
|
|
||||||
|
export function parse(str: string) {
|
||||||
|
return browser.parseUserAgent(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function icon(str: string) {
|
||||||
|
return str.split(' ')[0].toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.lib.useragent = { parse, icon };
|
||||||
|
export default { parse, icon };
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
const { ValidationError } = require('../error');
|
|
||||||
|
|
||||||
const RE_UID = /^-?\d+$/i;
|
|
||||||
const RE_PID = /^([a-zA-Z]+[a-zA-Z0-9]*)|$/i;
|
|
||||||
const RE_UNAME = /[^\s\u3000](.{,254}[^\s\u3000])?$/i;
|
|
||||||
const RE_ROLE = /^[_0-9A-Za-z]{1,256}$/i;
|
|
||||||
const RE_MAIL = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/i;
|
|
||||||
|
|
||||||
const isTitle = (s) => s && s.length < 64;
|
|
||||||
const checkTitle = (s) => { if (!isTitle(s)) throw new ValidationError('title'); else return s; };
|
|
||||||
const isUid = (s) => RE_UID.test(s);
|
|
||||||
const checkUid = (s) => { if (!isUid(s)) throw new ValidationError('uid'); else return s; };
|
|
||||||
const isUname = (s) => RE_UNAME.test(s);
|
|
||||||
const checkUname = (s) => { if (!isUname(s)) throw new ValidationError('uname'); else return s; };
|
|
||||||
const isRole = (s) => RE_ROLE.test(s);
|
|
||||||
const checkRole = (s) => { if (!isRole(s)) throw new ValidationError('role'); else return s; };
|
|
||||||
const isPassword = (s) => s.length >= 5;
|
|
||||||
const checkPassword = (s) => { if (!isPassword(s)) throw new ValidationError('password'); else return s; };
|
|
||||||
const isEmail = (s) => RE_MAIL.test(s);
|
|
||||||
const checkEmail = (s) => { if (!isEmail(s)) throw new ValidationError('mail'); else return s; };
|
|
||||||
const isContent = (s) => s && s.length < 65536;
|
|
||||||
const checkContent = (s) => { if (!isContent(s)) throw new ValidationError('content'); else return s; };
|
|
||||||
const isName = (s) => s && s.length < 256;
|
|
||||||
const checkName = (s) => { if (!isName(s)) throw new ValidationError('name'); else return s; };
|
|
||||||
const isPid = (s) => RE_PID.test(s.toString());
|
|
||||||
const checkPid = (s) => { if (!RE_PID.test(s)) throw new ValidationError('pid'); else return s; };
|
|
||||||
const isIntro = () => true;
|
|
||||||
const checkIntro = (s) => { if (!isIntro(s)) throw new ValidationError('intro'); else return s; };
|
|
||||||
const isDescription = () => true;
|
|
||||||
const checkDescription = (s) => { if (!isDescription(s)) throw new ValidationError('description'); else return s; };
|
|
||||||
|
|
||||||
global.Hydro.lib.validator = module.exports = {
|
|
||||||
isTitle,
|
|
||||||
checkTitle,
|
|
||||||
isUid,
|
|
||||||
checkUid,
|
|
||||||
isUname,
|
|
||||||
checkUname,
|
|
||||||
isRole,
|
|
||||||
checkRole,
|
|
||||||
isPassword,
|
|
||||||
checkPassword,
|
|
||||||
isEmail,
|
|
||||||
checkEmail,
|
|
||||||
isContent,
|
|
||||||
checkContent,
|
|
||||||
isName,
|
|
||||||
checkName,
|
|
||||||
isPid,
|
|
||||||
checkPid,
|
|
||||||
isIntro,
|
|
||||||
checkIntro,
|
|
||||||
isDescription,
|
|
||||||
checkDescription,
|
|
||||||
};
|
|
||||||
/*
|
|
||||||
ID_RE = re.compile(r'[^\\/\s\u3000]([^\\/\n\r]*[^\\/\s\u3000])?')
|
|
||||||
|
|
||||||
def is_id(s):
|
|
||||||
return bool(ID_RE.fullmatch(s))
|
|
||||||
|
|
||||||
def check_category_name(s):
|
|
||||||
if not is_id(s):
|
|
||||||
raise error.ValidationError('category_name')
|
|
||||||
|
|
||||||
def check_node_name(s):
|
|
||||||
if not is_id(s):
|
|
||||||
raise error.ValidationError('node_name')
|
|
||||||
|
|
||||||
def is_intro(s):
|
|
||||||
return isinstance(s, str) and 0 < len(s.strip()) < 500
|
|
||||||
|
|
||||||
def check_intro(s):
|
|
||||||
if not is_intro(s):
|
|
||||||
raise error.ValidationError('intro')
|
|
||||||
|
|
||||||
def is_description(s):
|
|
||||||
return isinstance(s, str) and len(s) < 65536
|
|
||||||
|
|
||||||
def check_description(s):
|
|
||||||
if not is_description(s):
|
|
||||||
raise error.ValidationError('description')
|
|
||||||
|
|
||||||
def is_lang(i):
|
|
||||||
return i in constant.language.LANG_TEXTS
|
|
||||||
|
|
||||||
def check_lang(i):
|
|
||||||
if not is_lang(i):
|
|
||||||
raise error.ValidationError('lang')
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import { ValidationError } from '../error';
|
||||||
|
|
||||||
|
const RE_UID = /^-?\d+$/i;
|
||||||
|
const RE_PID = /^([a-zA-Z]+[a-zA-Z0-9]*)|$/i;
|
||||||
|
const RE_UNAME = /[^\s\u3000](.{,254}[^\s\u3000])?$/i;
|
||||||
|
const RE_ROLE = /^[_0-9A-Za-z]{1,256}$/i;
|
||||||
|
const RE_MAIL = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/i;
|
||||||
|
|
||||||
|
export const isTitle = (s) => s && s.length < 64;
|
||||||
|
export const checkTitle = (s) => { if (!isTitle(s)) throw new ValidationError('title'); else return s; };
|
||||||
|
export const isUid = (s) => RE_UID.test(s);
|
||||||
|
export const checkUid = (s) => { if (!isUid(s)) throw new ValidationError('uid'); else return s; };
|
||||||
|
export const isUname = (s) => RE_UNAME.test(s);
|
||||||
|
export const checkUname = (s) => { if (!isUname(s)) throw new ValidationError('uname'); else return s; };
|
||||||
|
export const isRole = (s) => RE_ROLE.test(s);
|
||||||
|
export const checkRole = (s) => { if (!isRole(s)) throw new ValidationError('role'); else return s; };
|
||||||
|
export const isPassword = (s) => s.length >= 5;
|
||||||
|
export const checkPassword = (s) => { if (!isPassword(s)) throw new ValidationError('password'); else return s; };
|
||||||
|
export const isEmail = (s) => RE_MAIL.test(s);
|
||||||
|
export const checkEmail = (s) => { if (!isEmail(s)) throw new ValidationError('mail'); else return s; };
|
||||||
|
export const isContent = (s) => s && s.length < 65536;
|
||||||
|
export const checkContent = (s) => { if (!isContent(s)) throw new ValidationError('content'); else return s; };
|
||||||
|
export const isName = (s) => s && s.length < 256;
|
||||||
|
export const checkName = (s) => { if (!isName(s)) throw new ValidationError('name'); else return s; };
|
||||||
|
export const isPid = (s) => RE_PID.test(s.toString());
|
||||||
|
export const checkPid = (s) => { if (!RE_PID.test(s)) throw new ValidationError('pid'); else return s; };
|
||||||
|
export const isIntro = () => true;
|
||||||
|
export const checkIntro = (s) => { if (!isIntro()) throw new ValidationError('intro'); else return s; };
|
||||||
|
export const isDescription = () => true;
|
||||||
|
export const checkDescription = (s) => { if (!isDescription()) throw new ValidationError('description'); else return s; };
|
||||||
|
|
||||||
|
global.Hydro.lib.validator = {
|
||||||
|
isTitle,
|
||||||
|
checkTitle,
|
||||||
|
isUid,
|
||||||
|
checkUid,
|
||||||
|
isUname,
|
||||||
|
checkUname,
|
||||||
|
isRole,
|
||||||
|
checkRole,
|
||||||
|
isPassword,
|
||||||
|
checkPassword,
|
||||||
|
isEmail,
|
||||||
|
checkEmail,
|
||||||
|
isContent,
|
||||||
|
checkContent,
|
||||||
|
isName,
|
||||||
|
checkName,
|
||||||
|
isPid,
|
||||||
|
checkPid,
|
||||||
|
isIntro,
|
||||||
|
checkIntro,
|
||||||
|
isDescription,
|
||||||
|
checkDescription,
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
ID_RE = re.compile(r'[^\\/\s\u3000]([^\\/\n\r]*[^\\/\s\u3000])?')
|
||||||
|
|
||||||
|
def is_id(s):
|
||||||
|
return bool(ID_RE.fullmatch(s))
|
||||||
|
|
||||||
|
def check_category_name(s):
|
||||||
|
if not is_id(s):
|
||||||
|
raise error.ValidationError('category_name')
|
||||||
|
|
||||||
|
def check_node_name(s):
|
||||||
|
if not is_id(s):
|
||||||
|
raise error.ValidationError('node_name')
|
||||||
|
|
||||||
|
def is_intro(s):
|
||||||
|
return isinstance(s, str) and 0 < len(s.strip()) < 500
|
||||||
|
|
||||||
|
def check_intro(s):
|
||||||
|
if not is_intro(s):
|
||||||
|
raise error.ValidationError('intro')
|
||||||
|
|
||||||
|
def is_description(s):
|
||||||
|
return isinstance(s, str) and len(s) < 65536
|
||||||
|
|
||||||
|
def check_description(s):
|
||||||
|
if not is_description(s):
|
||||||
|
raise error.ValidationError('description')
|
||||||
|
|
||||||
|
def is_lang(i):
|
||||||
|
return i in export const ant.language.LANG_TEXTS
|
||||||
|
|
||||||
|
def check_lang(i):
|
||||||
|
if not is_lang(i):
|
||||||
|
raise error.ValidationError('lang')
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -1,8 +1,25 @@
|
|||||||
/* eslint-disable import/no-dynamic-require */
|
/* eslint-disable import/no-dynamic-require */
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
/* eslint-disable no-eval */
|
/* eslint-disable no-eval */
|
||||||
const cluster = require('cluster');
|
|
||||||
const { argv } = require('yargs');
|
import './declare';
|
||||||
|
import cluster from 'cluster';
|
||||||
|
import { argv } from 'yargs';
|
||||||
|
|
||||||
|
global.Hydro = {
|
||||||
|
stat: { reqCount: 0 },
|
||||||
|
handler: {},
|
||||||
|
service: {},
|
||||||
|
model: {},
|
||||||
|
script: {},
|
||||||
|
lib: {},
|
||||||
|
wiki: {},
|
||||||
|
template: {},
|
||||||
|
ui: {},
|
||||||
|
error: {},
|
||||||
|
locales: {},
|
||||||
|
};
|
||||||
|
global.onDestory = [];
|
||||||
|
|
||||||
if (argv.debug) console.log(process.argv);
|
if (argv.debug) console.log(process.argv);
|
||||||
|
|
||||||
@@ -14,25 +31,25 @@ async function terminate() {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fork(args = []) {
|
async function fork(args: string[] = []) {
|
||||||
cluster.setupMaster({ args });
|
cluster.setupMaster({ args });
|
||||||
return cluster.fork();
|
return cluster.fork();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function entry(config) {
|
async function entry(config: any) {
|
||||||
if (config.entry) {
|
if (config.entry) {
|
||||||
if (config.newProcess) {
|
if (config.newProcess) {
|
||||||
const p = await fork([`--entry=${config.entry}`]);
|
const p = await fork([`--entry=${config.entry}`]);
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
p.on('exit', resolve);
|
p.on('exit', resolve);
|
||||||
p.on('error', (err) => {
|
p.on('error', (err: Error) => {
|
||||||
p.kill();
|
p.kill();
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const loader = require(`./entry/${config.entry}`);
|
const loader = require(`./entry/${config.entry}`);
|
||||||
return await loader(entry);
|
return await loader.load(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -42,12 +59,12 @@ async function stopWorker() {
|
|||||||
cluster.disconnect();
|
cluster.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startWorker(cnt) {
|
async function startWorker(cnt: number) {
|
||||||
await fork(['--firstWorker']);
|
await fork(['--firstWorker']);
|
||||||
for (let i = 1; i < cnt; i++) await fork();
|
for (let i = 1; i < cnt; i++) await fork();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeCommand(input) {
|
async function executeCommand(input: string) {
|
||||||
try {
|
try {
|
||||||
const t = eval(input.toString().trim());
|
const t = eval(input.toString().trim());
|
||||||
if (t instanceof Promise) console.log(await t);
|
if (t instanceof Promise) console.log(await t);
|
||||||
@@ -57,13 +74,13 @@ async function executeCommand(input) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function messageHandler(worker, msg) {
|
async function messageHandler(worker: cluster.Worker, msg: any) {
|
||||||
if (!msg) msg = worker;
|
if (!msg) msg = worker;
|
||||||
if (msg.event) {
|
if (msg.event) {
|
||||||
if (msg.event === 'bus') {
|
if (msg.event === 'bus') {
|
||||||
if (cluster.isMaster) {
|
if (cluster.isMaster) {
|
||||||
for (const i in cluster.workers) {
|
for (const i in cluster.workers) {
|
||||||
if (i !== worker.id) cluster.workers[i].send(msg);
|
cluster.workers[i].send(msg);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
global.Hydro.service.bus.publish(msg.eventName, msg.payload, false);
|
global.Hydro.service.bus.publish(msg.eventName, msg.payload, false);
|
||||||
@@ -88,18 +105,6 @@ async function load() {
|
|||||||
'js-yaml': require('js-yaml'),
|
'js-yaml': require('js-yaml'),
|
||||||
mongodb: require('mongodb'),
|
mongodb: require('mongodb'),
|
||||||
};
|
};
|
||||||
global.Hydro = {
|
|
||||||
stat: { reqCount: 0 },
|
|
||||||
handler: {},
|
|
||||||
service: {},
|
|
||||||
model: {},
|
|
||||||
script: {},
|
|
||||||
lib: {},
|
|
||||||
wiki: {},
|
|
||||||
template: {},
|
|
||||||
ui: {},
|
|
||||||
};
|
|
||||||
global.onDestory = [];
|
|
||||||
Error.stackTraceLimit = 50;
|
Error.stackTraceLimit = 50;
|
||||||
process.on('unhandledRejection', (e) => console.error(e));
|
process.on('unhandledRejection', (e) => console.error(e));
|
||||||
process.on('SIGINT', terminate);
|
process.on('SIGINT', terminate);
|
||||||
@@ -108,7 +113,8 @@ async function load() {
|
|||||||
if (cluster.isMaster || argv.startAsMaster) {
|
if (cluster.isMaster || argv.startAsMaster) {
|
||||||
console.log(`Master ${process.pid} Starting`);
|
console.log(`Master ${process.pid} Starting`);
|
||||||
process.stdin.setEncoding('utf8');
|
process.stdin.setEncoding('utf8');
|
||||||
process.stdin.on('data', (input) => {
|
process.stdin.on('data', (buf) => {
|
||||||
|
const input = buf.toString();
|
||||||
if (input[0] === '@') {
|
if (input[0] === '@') {
|
||||||
for (const i in cluster.workers) {
|
for (const i in cluster.workers) {
|
||||||
cluster.workers[i].send({ event: 'run', command: input.substr(1, input.length - 1) });
|
cluster.workers[i].send({ event: 'run', command: input.substr(1, input.length - 1) });
|
||||||
@@ -140,6 +146,7 @@ async function load() {
|
|||||||
console.log(`Worker ${process.pid} Started as ${argv.entry}`);
|
console.log(`Worker ${process.pid} Started as ${argv.entry}`);
|
||||||
} else {
|
} else {
|
||||||
if (argv.firstWorker) cluster.isFirstWorker = true;
|
if (argv.firstWorker) cluster.isFirstWorker = true;
|
||||||
|
else cluster.isFirstWorker = false;
|
||||||
console.log(`Worker ${process.pid} Starting`);
|
console.log(`Worker ${process.pid} Starting`);
|
||||||
await entry({ entry: 'worker' });
|
await entry({ entry: 'worker' });
|
||||||
console.log(`Worker ${process.pid} Started`);
|
console.log(`Worker ${process.pid} Started`);
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
const db = require('../service/db');
|
import * as db from '../service/db';
|
||||||
|
|
||||||
const coll = db.collection('blacklist');
|
const coll = db.collection('blacklist');
|
||||||
|
|
||||||
async function add(ip) {
|
export async function add(ip: string) {
|
||||||
const expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
|
const expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
|
||||||
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
|
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(ip) {
|
export function get(ip: string) {
|
||||||
return coll.findOne({ _id: ip });
|
return coll.findOne({ _id: ip });
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(ip) {
|
export function del(ip: string) {
|
||||||
return coll.deleteOne({ _id: ip });
|
return coll.deleteOne({ _id: ip });
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureIndexes() {
|
export function ensureIndexes() {
|
||||||
return coll.createIndex('expireAt', { expireAfterSeconds: 0 });
|
return coll.createIndex('expireAt', { expireAfterSeconds: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.blacklist = module.exports = {
|
global.Hydro.model.blacklist = {
|
||||||
add, get, del, ensureIndexes,
|
add, get, del, ensureIndexes,
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const PERM = {
|
export const PERM = {
|
||||||
PERM_NONE: '',
|
PERM_NONE: '',
|
||||||
|
|
||||||
// Domain Settings
|
// Domain Settings
|
||||||
@@ -30,8 +30,10 @@ const PERM = {
|
|||||||
PERM_REPLY_PROBLEM_SOLUTION: 'L',
|
PERM_REPLY_PROBLEM_SOLUTION: 'L',
|
||||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY: 'M',
|
PERM_EDIT_PROBLEM_SOLUTION_REPLY: 'M',
|
||||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF: 'N',
|
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF: 'N',
|
||||||
|
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF_SOLUTION: 'x',
|
||||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY: 'O',
|
PERM_DELETE_PROBLEM_SOLUTION_REPLY: 'O',
|
||||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF: 'P',
|
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF: 'P',
|
||||||
|
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF_SOLUTION: 'y',
|
||||||
|
|
||||||
// Discussion
|
// Discussion
|
||||||
PERM_VIEW_DISCUSSION: 'Q',
|
PERM_VIEW_DISCUSSION: 'Q',
|
||||||
@@ -75,11 +77,17 @@ const PERM = {
|
|||||||
|
|
||||||
// Ranking
|
// Ranking
|
||||||
PERM_VIEW_RANKING: 'w',
|
PERM_VIEW_RANKING: 'w',
|
||||||
|
|
||||||
|
// Placeholder
|
||||||
|
PERM_ALL: '',
|
||||||
|
PERM_BASIC: '',
|
||||||
|
PERM_DEFAULT: '',
|
||||||
|
PERM_ADMIN: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const Permission = (family, key, desc) => ({ family, key, desc });
|
export const Permission = (family, key, desc) => ({ family, key, desc });
|
||||||
|
|
||||||
const PERMS = [
|
export const PERMS = [
|
||||||
Permission('perm_general', PERM.PERM_VIEW, 'View this domain'),
|
Permission('perm_general', PERM.PERM_VIEW, 'View this domain'),
|
||||||
Permission('perm_general', PERM.PERM_EDIT_DOMAIN, 'Edit domain settings'),
|
Permission('perm_general', PERM.PERM_EDIT_DOMAIN, 'Edit domain settings'),
|
||||||
Permission('perm_general', PERM.PERM_MOD_BADGE, 'Show MOD badge'),
|
Permission('perm_general', PERM.PERM_MOD_BADGE, 'Show MOD badge'),
|
||||||
@@ -142,10 +150,10 @@ const PERMS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
let PERM_ALL = '';
|
let PERM_ALL = '';
|
||||||
for (const p in PERM) PERM_ALL += PERM[p];
|
for (const p in PERM) if (PERM[p]) PERM_ALL += PERM[p];
|
||||||
PERM.PERM_ALL = PERM_ALL;
|
PERM.PERM_ALL = PERM_ALL;
|
||||||
|
|
||||||
const PERMS_BY_FAMILY = {};
|
export const PERMS_BY_FAMILY = {};
|
||||||
for (const p of PERMS) {
|
for (const p of PERMS) {
|
||||||
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
|
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
|
||||||
else PERMS_BY_FAMILY[p.family].push(p);
|
else PERMS_BY_FAMILY[p.family].push(p);
|
||||||
@@ -208,7 +216,7 @@ PERM.PERM_DEFAULT = PERM.PERM_VIEW
|
|||||||
|
|
||||||
PERM.PERM_ADMIN = PERM.PERM_ALL;
|
PERM.PERM_ADMIN = PERM.PERM_ALL;
|
||||||
|
|
||||||
const PRIV = {
|
export const PRIV = {
|
||||||
PRIV_NONE: 0,
|
PRIV_NONE: 0,
|
||||||
PRIV_EDIT_SYSTEM: 1 << 0, // renamed from PRIV_SET_PRIV
|
PRIV_EDIT_SYSTEM: 1 << 0, // renamed from PRIV_SET_PRIV
|
||||||
PRIV_SET_PERM: 1 << 1,
|
PRIV_SET_PERM: 1 << 1,
|
||||||
@@ -231,18 +239,19 @@ const PRIV = {
|
|||||||
PRIV_DELETE_FILE: 1 << 18,
|
PRIV_DELETE_FILE: 1 << 18,
|
||||||
PRIV_DELETE_FILE_SELF: 1 << 19,
|
PRIV_DELETE_FILE_SELF: 1 << 19,
|
||||||
PRIV_ALL: -1,
|
PRIV_ALL: -1,
|
||||||
|
|
||||||
|
PRIV_DEFAULT: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
PRIV.PRIV_DETAULT = PRIV.PRIV_USER_PROFILE
|
PRIV.PRIV_DEFAULT = PRIV.PRIV_USER_PROFILE
|
||||||
+ PRIV.PRIV_REGISTER_USER
|
+ PRIV.PRIV_REGISTER_USER
|
||||||
+ PRIV.PRIV_READ_PRETEST_DATA_SELF
|
+ PRIV.PRIV_READ_PRETEST_DATA_SELF
|
||||||
+ PRIV.PRIV_CREATE_DOMAIN
|
+ PRIV.PRIV_CREATE_DOMAIN
|
||||||
+ PRIV.PRIV_CREATE_FILE
|
+ PRIV.PRIV_CREATE_FILE
|
||||||
+ PRIV.PRIV_DELETE_FILE_SELF
|
+ PRIV.PRIV_DELETE_FILE_SELF;
|
||||||
+ PRIV.PRIV_USERFILE;
|
|
||||||
|
|
||||||
// [10, 1] means that people whose rank is less than 1% will get Level 10
|
// [10, 1] means that people whose rank is less than 1% will get Level 10
|
||||||
const LEVELS = [
|
export const LEVELS = [
|
||||||
[10, 1],
|
[10, 1],
|
||||||
[9, 2],
|
[9, 2],
|
||||||
[8, 10],
|
[8, 10],
|
||||||
@@ -255,7 +264,7 @@ const LEVELS = [
|
|||||||
[1, 100],
|
[1, 100],
|
||||||
];
|
];
|
||||||
|
|
||||||
const BUILTIN_USERS = [
|
export const BUILTIN_USERS = [
|
||||||
{
|
{
|
||||||
_id: 0,
|
_id: 0,
|
||||||
mail: 'Guest@hydro.local',
|
mail: 'Guest@hydro.local',
|
||||||
@@ -288,13 +297,13 @@ const BUILTIN_USERS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const BUILTIN_ROLES = {
|
export const BUILTIN_ROLES = {
|
||||||
guest: { perm: PERM.PERM_BASIC },
|
guest: { perm: PERM.PERM_BASIC },
|
||||||
default: { perm: PERM.PERM_DEFAULT },
|
default: { perm: PERM.PERM_DEFAULT },
|
||||||
admin: { perm: PERM.PERM_ADMIN },
|
admin: { perm: PERM.PERM_ADMIN },
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_NODES = {
|
export const DEFAULT_NODES = {
|
||||||
探索: [
|
探索: [
|
||||||
{ pic: 'qa', name: '问答' },
|
{ pic: 'qa', name: '问答' },
|
||||||
{ pic: 'share', name: '分享' },
|
{ pic: 'share', name: '分享' },
|
||||||
@@ -353,7 +362,7 @@ const DEFAULT_NODES = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const CATEGORIES = {
|
export const CATEGORIES = {
|
||||||
动态规划: [
|
动态规划: [
|
||||||
'LCS',
|
'LCS',
|
||||||
'LIS',
|
'LIS',
|
||||||
@@ -490,15 +499,15 @@ const CATEGORIES = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const FOOTER_EXTRA_HTMLS = [];
|
export const FOOTER_EXTRA_HTMLS = [];
|
||||||
|
|
||||||
const VIEW_LANGS = [
|
export const VIEW_LANGS = [
|
||||||
{ code: 'zh_CN', name: '简体中文' },
|
{ code: 'zh_CN', name: '简体中文' },
|
||||||
{ code: 'zh_TW', name: '正體中文' },
|
{ code: 'zh_TW', name: '正體中文' },
|
||||||
{ code: 'en', name: 'English' },
|
{ code: 'en', name: 'English' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const LANG_TEXTS = {
|
export const LANG_TEXTS = {
|
||||||
c: 'C',
|
c: 'C',
|
||||||
cc: 'C++',
|
cc: 'C++',
|
||||||
cs: 'C#',
|
cs: 'C#',
|
||||||
@@ -514,7 +523,7 @@ const LANG_TEXTS = {
|
|||||||
rb: 'Ruby',
|
rb: 'Ruby',
|
||||||
};
|
};
|
||||||
|
|
||||||
const LANG_HIGHLIGHT_ID = {
|
export const LANG_HIGHLIGHT_ID = {
|
||||||
c: 'c',
|
c: 'c',
|
||||||
cc: 'cpp',
|
cc: 'cpp',
|
||||||
cs: 'csharp',
|
cs: 'csharp',
|
||||||
@@ -530,7 +539,7 @@ const LANG_HIGHLIGHT_ID = {
|
|||||||
rb: 'ruby',
|
rb: 'ruby',
|
||||||
};
|
};
|
||||||
|
|
||||||
const STATUS = {
|
export const STATUS = {
|
||||||
STATUS_WAITING: 0,
|
STATUS_WAITING: 0,
|
||||||
STATUS_ACCEPTED: 1,
|
STATUS_ACCEPTED: 1,
|
||||||
STATUS_WRONG_ANSWER: 2,
|
STATUS_WRONG_ANSWER: 2,
|
||||||
@@ -548,7 +557,7 @@ const STATUS = {
|
|||||||
STATUS_IGNORED: 30,
|
STATUS_IGNORED: 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
const STATUS_TEXTS = {
|
export const STATUS_TEXTS = {
|
||||||
[STATUS.STATUS_WAITING]: 'Waiting',
|
[STATUS.STATUS_WAITING]: 'Waiting',
|
||||||
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
||||||
[STATUS.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
[STATUS.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
||||||
@@ -566,7 +575,7 @@ const STATUS_TEXTS = {
|
|||||||
[STATUS.STATUS_IGNORED]: 'Ignored',
|
[STATUS.STATUS_IGNORED]: 'Ignored',
|
||||||
};
|
};
|
||||||
|
|
||||||
const STATUS_CODES = {
|
export const STATUS_CODES = {
|
||||||
0: 'pending',
|
0: 'pending',
|
||||||
1: 'pass',
|
1: 'pass',
|
||||||
2: 'fail',
|
2: 'fail',
|
||||||
@@ -584,7 +593,22 @@ const STATUS_CODES = {
|
|||||||
30: 'ignored',
|
30: 'ignored',
|
||||||
};
|
};
|
||||||
|
|
||||||
global.Hydro.model.builtin = module.exports = {
|
export const USER_GENDER_MALE = 0;
|
||||||
|
export const USER_GENDER_FEMALE = 1;
|
||||||
|
export const USER_GENDER_OTHER = 2;
|
||||||
|
export const USER_GENDERS = [USER_GENDER_MALE, USER_GENDER_FEMALE, USER_GENDER_OTHER];
|
||||||
|
export const USER_GENDER_RANGE = {
|
||||||
|
[USER_GENDER_MALE]: 'Boy ♂',
|
||||||
|
[USER_GENDER_FEMALE]: 'Girl ♀',
|
||||||
|
[USER_GENDER_OTHER]: 'Other',
|
||||||
|
};
|
||||||
|
export const USER_GENDER_ICONS = {
|
||||||
|
[USER_GENDER_MALE]: '♂',
|
||||||
|
[USER_GENDER_FEMALE]: '♀',
|
||||||
|
[USER_GENDER_OTHER]: '?',
|
||||||
|
};
|
||||||
|
|
||||||
|
global.Hydro.model.builtin = {
|
||||||
PERM,
|
PERM,
|
||||||
PERMS,
|
PERMS,
|
||||||
PERMS_BY_FAMILY,
|
PERMS_BY_FAMILY,
|
||||||
@@ -601,4 +625,10 @@ global.Hydro.model.builtin = module.exports = {
|
|||||||
STATUS,
|
STATUS,
|
||||||
STATUS_TEXTS,
|
STATUS_TEXTS,
|
||||||
STATUS_CODES,
|
STATUS_CODES,
|
||||||
|
USER_GENDER_MALE,
|
||||||
|
USER_GENDER_FEMALE,
|
||||||
|
USER_GENDER_OTHER,
|
||||||
|
USER_GENDERS,
|
||||||
|
USER_GENDER_RANGE,
|
||||||
|
USER_GENDER_ICONS,
|
||||||
};
|
};
|
||||||
@@ -1,28 +1,51 @@
|
|||||||
const user = require('./user');
|
import { ObjectID } from 'mongodb';
|
||||||
const problem = require('./problem');
|
import * as user from './user';
|
||||||
const {
|
import * as problem from './problem';
|
||||||
|
import {
|
||||||
ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
|
ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
|
||||||
ContestNotAttendedError, ContestScoreboardHiddenError,
|
ContestNotAttendedError, ContestScoreboardHiddenError,
|
||||||
} = require('../error');
|
} from '../error';
|
||||||
const { PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('./builtin').PERM;
|
import * as document from './document';
|
||||||
const validator = require('../lib/validator');
|
import { PERM } from './builtin';
|
||||||
const ranked = require('../lib/rank');
|
import * as validator from '../lib/validator';
|
||||||
const document = require('./document');
|
import * as misc from '../lib/misc';
|
||||||
|
import ranked from '../lib/rank';
|
||||||
|
|
||||||
|
interface Column {
|
||||||
|
type: string,
|
||||||
|
value: string,
|
||||||
|
raw?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tdoc {
|
||||||
|
_id: ObjectID,
|
||||||
|
domainId: string,
|
||||||
|
docId: ObjectID,
|
||||||
|
docType: number,
|
||||||
|
beginAt: Date,
|
||||||
|
endAt: Date,
|
||||||
|
penaltySince?: Date,
|
||||||
|
attend: number,
|
||||||
|
title: string,
|
||||||
|
content: string,
|
||||||
|
rule: string,
|
||||||
|
pids: number[]
|
||||||
|
}
|
||||||
|
|
||||||
const acm = {
|
const acm = {
|
||||||
TEXT: 'ACM/ICPC',
|
TEXT: 'ACM/ICPC',
|
||||||
check: () => { },
|
check: () => { },
|
||||||
showScoreboard: () => true,
|
showScoreboard: () => true,
|
||||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||||
stat: (tdoc, journal) => {
|
stat: (tdoc, journal: any[]) => {
|
||||||
const naccept = {};
|
const naccept = {};
|
||||||
const effective = {};
|
const effective = {};
|
||||||
const detail = [];
|
const detail = [];
|
||||||
let accept = 0;
|
let accept = 0;
|
||||||
let time = 0;
|
let time = 0;
|
||||||
for (const j in journal) {
|
for (const j of journal) {
|
||||||
if (tdoc.pids.includes(j.pid)
|
if (tdoc.pids.includes(j.pid)
|
||||||
&& !(effective.includes(j.pid) && effective[j.pid].accept)) {
|
&& !(effective[j.pid] && effective[j.pid].accept)) {
|
||||||
effective[j.pid] = j;
|
effective[j.pid] = j;
|
||||||
}
|
}
|
||||||
if (!j.accept) naccept[j.pid]++;
|
if (!j.accept) naccept[j.pid]++;
|
||||||
@@ -32,15 +55,19 @@ const acm = {
|
|||||||
const penalty = 20 * 60 * naccept[jdoc.pid];
|
const penalty = 20 * 60 * naccept[jdoc.pid];
|
||||||
return real + penalty;
|
return real + penalty;
|
||||||
}
|
}
|
||||||
for (const j of effective) detail.push({ ...j, naccept: naccept[j.pid], time: _time(j) });
|
for (const j in effective) {
|
||||||
|
detail.push({
|
||||||
|
...effective[j], naccept: naccept[effective[j].pid], time: _time(effective[j]),
|
||||||
|
});
|
||||||
|
}
|
||||||
for (const d of detail) {
|
for (const d of detail) {
|
||||||
accept += d.accept;
|
accept += d.accept;
|
||||||
if (d.accept) time += d.time;
|
if (d.accept) time += d.time;
|
||||||
}
|
}
|
||||||
return { accept, time, detail };
|
return { accept, time, detail };
|
||||||
},
|
},
|
||||||
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
scoreboard(isExport: boolean, _: (s: string) => string, tdoc, rankedTsdocs, udict, pdict) {
|
||||||
const columns = [
|
const columns: Column[] = [
|
||||||
{ type: 'rank', value: _('Rank') },
|
{ type: 'rank', value: _('Rank') },
|
||||||
{ type: 'user', value: _('User') },
|
{ type: 'user', value: _('User') },
|
||||||
{ type: 'solved_problems', value: _('Solved Problems') },
|
{ type: 'solved_problems', value: _('Solved Problems') },
|
||||||
@@ -130,7 +157,7 @@ const oi = {
|
|||||||
stat: (tdoc, journal) => {
|
stat: (tdoc, journal) => {
|
||||||
const detail = {};
|
const detail = {};
|
||||||
let score = 0;
|
let score = 0;
|
||||||
for (const j in journal) {
|
for (const j of journal) {
|
||||||
if (tdoc.pids.includes(j.pid)) {
|
if (tdoc.pids.includes(j.pid)) {
|
||||||
detail[j.pid] = j;
|
detail[j.pid] = j;
|
||||||
score += j.score;
|
score += j.score;
|
||||||
@@ -145,7 +172,7 @@ const oi = {
|
|||||||
return now > tdoc.endAt;
|
return now > tdoc.endAt;
|
||||||
},
|
},
|
||||||
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||||
const columns = [
|
const columns: Column[] = [
|
||||||
{ type: 'rank', value: _('Rank') },
|
{ type: 'rank', value: _('Rank') },
|
||||||
{ type: 'user', value: _('User') },
|
{ type: 'user', value: _('User') },
|
||||||
{ type: 'total_score', value: _('Total Score') },
|
{ type: 'total_score', value: _('Total Score') },
|
||||||
@@ -186,30 +213,156 @@ const oi = {
|
|||||||
rank: (tdocs) => ranked(tdocs, (a, b) => a.score === b.score),
|
rank: (tdocs) => ranked(tdocs, (a, b) => a.score === b.score),
|
||||||
};
|
};
|
||||||
|
|
||||||
const RULES = {
|
const homework = {
|
||||||
acm, oi,
|
TEXT: 'Assignment',
|
||||||
|
check: () => { },
|
||||||
|
stat: (tdoc, journal) => {
|
||||||
|
const effective = {};
|
||||||
|
for (const j of journal) {
|
||||||
|
if (tdoc.pids.includes(j.pid)
|
||||||
|
&& !effective[j.pid]
|
||||||
|
&& journal[j.pid].accept) {
|
||||||
|
effective[j.pid] = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function time(jdoc) {
|
||||||
|
const real = jdoc.rid.generationTime - tdoc.beginAt.getTime() / 1000;
|
||||||
|
return Math.floor(real);
|
||||||
|
}
|
||||||
|
function penaltyScore(jdoc) {
|
||||||
|
const { score } = jdoc;
|
||||||
|
const exceedSeconds = Math.floor(
|
||||||
|
jdoc.rid.generationTime - tdoc.penaltySince.getTime() / 1000,
|
||||||
|
);
|
||||||
|
if (exceedSeconds < 0) return score;
|
||||||
|
let coefficient = 1;
|
||||||
|
const keys = Object.keys(tdoc.penaltyRules)
|
||||||
|
.map((k) => parseFloat(k)).sort((a, b) => a - b);
|
||||||
|
for (const i of keys) {
|
||||||
|
if (i * 3600 <= exceedSeconds) coefficient = tdoc.penaltyRules[i];
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
return score * coefficient;
|
||||||
|
}
|
||||||
|
const detail = [];
|
||||||
|
for (const j in effective) {
|
||||||
|
detail.push({
|
||||||
|
...effective[j],
|
||||||
|
penaltyScore: penaltyScore(effective[j]),
|
||||||
|
time: time(j),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
score: Math.sum(detail.map((d) => d.score)),
|
||||||
|
penaltyScore: Math.sum(detail.map((d) => d.penaltySince)),
|
||||||
|
time: Math.sum(detail.map((d) => d.time)),
|
||||||
|
detail,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
showScoreboard: () => true,
|
||||||
|
showRecord: () => true,
|
||||||
|
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||||
|
const columns: Column[] = [
|
||||||
|
{ type: 'rank', value: _('Rank') },
|
||||||
|
{ type: 'user', value: _('User') },
|
||||||
|
{ type: 'total_score', value: _('Score') },
|
||||||
|
];
|
||||||
|
if (isExport) {
|
||||||
|
columns.push(
|
||||||
|
{ type: 'total_original_score', value: _('Original Score') },
|
||||||
|
{ type: 'total_time', value: _('Total Time (Seconds)') },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
columns.push({ type: 'total_time_str', value: _('Total Time') });
|
||||||
|
for (const index in tdoc.pids) {
|
||||||
|
const pid = tdoc.pids[index];
|
||||||
|
if (isExport) {
|
||||||
|
columns.push(
|
||||||
|
{
|
||||||
|
type: 'problem_score',
|
||||||
|
value: '#{0} {1}'.format(index + 1, pdict[pid].title),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'problem_original_score',
|
||||||
|
value: '#{0} {1}'.format(index + 1, _('Original Score')),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'problem_time',
|
||||||
|
value: '#{0} {1}'.format(index + 1, _('Time (Seconds)')),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'problem_time_str',
|
||||||
|
value: '#{0} {1}'.format(index + 1, _('Time')),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
columns.push({
|
||||||
|
type: 'problem_detail',
|
||||||
|
value: '#{0}'.format(index + 1),
|
||||||
|
raw: pdict[pid],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const rows = [columns];
|
||||||
|
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||||
|
const tsddict = {};
|
||||||
|
for (const item of tsdoc.journal || []) {
|
||||||
|
tsddict[item.pid] = item;
|
||||||
|
}
|
||||||
|
const row = [
|
||||||
|
{ type: 'string', value: rank },
|
||||||
|
{
|
||||||
|
type: 'user',
|
||||||
|
value: udict[tsdoc.uid].uname,
|
||||||
|
raw: udict[tsdoc.uid],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
value: tsdoc.penaltyScore || 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (isExport) {
|
||||||
|
row.push({ type: 'string', value: tsdoc.score || 0 });
|
||||||
|
row.push({ type: 'string', value: tsdoc.time || 0.0 });
|
||||||
|
}
|
||||||
|
row.push({ type: 'string', value: misc.formatSeconds(tsdoc.time || 0) });
|
||||||
|
for (const pid of tdoc.pids) {
|
||||||
|
const rdoc = (tsddict[pid] || {}).rid;
|
||||||
|
const colScore = (tsddict[pid] || {}).penaltyScore || '-';
|
||||||
|
const colOriginalScore = (tsddict[pid] || {}).score || '-';
|
||||||
|
const colTime = (tsddict[pid] || {}).time || '-';
|
||||||
|
const colTimeStr = colTime !== '-' ? misc.formatSeconds(colTime || 0) : '-';
|
||||||
|
if (isExport) {
|
||||||
|
row.push(
|
||||||
|
{ type: 'string', value: colScore },
|
||||||
|
{ type: 'string', value: colOriginalScore },
|
||||||
|
{ type: 'string', value: colTime },
|
||||||
|
{ type: 'string', value: colTimeStr },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
row.push({
|
||||||
|
type: 'record',
|
||||||
|
value: '{0} / {1}\n{2}'.format(colScore, colOriginalScore, colTimeStr),
|
||||||
|
raw: rdoc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
rank: (tsdocs) => ranked(tsdocs, (a, b) => a.score === b.score),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export const RULES = {
|
||||||
* @typedef {import('mongodb').ObjectID} ObjectID
|
acm, oi, homework,
|
||||||
* @typedef {import('../interface').Tdoc} Tdoc
|
};
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
export function add(
|
||||||
* @param {string} domainId
|
domainId: string, title: string, content: string, owner: number,
|
||||||
* @param {string} title
|
rule: string, beginAt = new Date(), endAt = new Date(), pids = [],
|
||||||
* @param {string} content
|
rated = false, data = {}, type = document.TYPE_CONTEST,
|
||||||
* @param {number} owner
|
) {
|
||||||
* @param {string} rule
|
|
||||||
* @param {Date} beginAt
|
|
||||||
* @param {Date} endAt
|
|
||||||
* @param {ObjectID[]} pids
|
|
||||||
* @param {object} data
|
|
||||||
* @returns {ObjectID} tid
|
|
||||||
*/
|
|
||||||
function add(domainId, title, content, owner, rule,
|
|
||||||
beginAt = new Date(), endAt = new Date(), pids = [], rated = false,
|
|
||||||
data = {}, type = document.TYPE_CONTEST) {
|
|
||||||
validator.checkTitle(title);
|
validator.checkTitle(title);
|
||||||
validator.checkContent(content);
|
validator.checkContent(content);
|
||||||
if (!this.RULES[rule]) throw new ValidationError('rule');
|
if (!this.RULES[rule]) throw new ValidationError('rule');
|
||||||
@@ -223,14 +376,10 @@ function add(domainId, title, content, owner, rule,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function edit(
|
||||||
* @param {string} domainId
|
domainId: string, tid: ObjectID,
|
||||||
* @param {ObjectID} tid
|
$set: any, type = document.TYPE_CONTEST,
|
||||||
* @param {object} $set
|
) {
|
||||||
* @param {number} type
|
|
||||||
* @returns {Promise<Tdoc>} tdoc after modification
|
|
||||||
*/
|
|
||||||
async function edit(domainId, tid, $set, type = document.TYPE_CONTEST) {
|
|
||||||
if ($set.rule) {
|
if ($set.rule) {
|
||||||
if (!this.RULES[$set.rule]) throw new ValidationError('rule');
|
if (!this.RULES[$set.rule]) throw new ValidationError('rule');
|
||||||
}
|
}
|
||||||
@@ -240,13 +389,7 @@ async function edit(domainId, tid, $set, type = document.TYPE_CONTEST) {
|
|||||||
return await document.set(domainId, type, tid, $set);
|
return await document.set(domainId, type, tid, $set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function get(domainId: string, tid: ObjectID, type = document.TYPE_CONTEST) {
|
||||||
* @param {string} domainId
|
|
||||||
* @param {ObjectID} tid
|
|
||||||
* @param {number} type
|
|
||||||
* @returns {Promise<Tdoc>}
|
|
||||||
*/
|
|
||||||
async function get(domainId, tid, type = document.TYPE_CONTEST) {
|
|
||||||
let tdoc;
|
let tdoc;
|
||||||
if (type === -1) {
|
if (type === -1) {
|
||||||
tdoc = await document.get(domainId, document.TYPE_CONTEST, tid);
|
tdoc = await document.get(domainId, document.TYPE_CONTEST, tid);
|
||||||
@@ -256,8 +399,8 @@ async function get(domainId, tid, type = document.TYPE_CONTEST) {
|
|||||||
return tdoc;
|
return tdoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateStatus(
|
export async function updateStatus(
|
||||||
domainId, tid, uid, rid, pid,
|
domainId: string, tid: ObjectID, uid: number, rid: ObjectID, pid: number,
|
||||||
accept = false, score = 0, type = document.TYPE_CONTEST,
|
accept = false, score = 0, type = document.TYPE_CONTEST,
|
||||||
) {
|
) {
|
||||||
await get(domainId, tid, type);
|
await get(domainId, tid, type);
|
||||||
@@ -267,18 +410,27 @@ async function updateStatus(
|
|||||||
if (!tsdoc.attend) throw new ContestNotAttendedError(tid, uid);
|
if (!tsdoc.attend) throw new ContestNotAttendedError(tid, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatus(domainId, tid, uid, type = document.TYPE_CONTEST) {
|
export function getStatus(
|
||||||
|
domainId: string, tid: ObjectID, uid: number,
|
||||||
|
type = document.TYPE_CONTEST,
|
||||||
|
) {
|
||||||
return document.getStatus(domainId, type, tid, uid);
|
return document.getStatus(domainId, type, tid, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getListStatus(domainId, uid, tids, type = document.TYPE_CONTEST) {
|
export async function getListStatus(
|
||||||
|
domainId: string, uid: number, tids: ObjectID[],
|
||||||
|
type = document.TYPE_CONTEST,
|
||||||
|
) {
|
||||||
const r = {};
|
const r = {};
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
for (const tid of tids) r[tid] = await getStatus(domainId, tid, uid, type);
|
for (const tid of tids) r[tid.toHexString()] = await getStatus(domainId, tid, uid, type);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function attend(domainId, tid, uid, type = document.TYPE_CONTEST) {
|
export async function attend(
|
||||||
|
domainId: string, tid: ObjectID, uid: number,
|
||||||
|
type = document.TYPE_CONTEST,
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
await document.cappedIncStatus(domainId, type, tid, uid, 'attend', 1, 0, 1);
|
await document.cappedIncStatus(domainId, type, tid, uid, 'attend', 1, 0, 1);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -288,49 +440,61 @@ async function attend(domainId, tid, uid, type = document.TYPE_CONTEST) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMultiStatus(domainId, query, docType = document.TYPE_CONTEST) {
|
export function getMultiStatus(domainId: string, query: any, docType = document.TYPE_CONTEST) {
|
||||||
return document.getMultiStatus(domainId, docType, query);
|
return document.getMultiStatus(domainId, docType, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNew(tdoc, days = 1) {
|
export function isNew(tdoc: Tdoc, days = 1) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
const readyAt = tdoc.beginAt.getTime();
|
const readyAt = tdoc.beginAt.getTime();
|
||||||
return (now < readyAt - days * 24 * 3600 * 1000);
|
return (now < readyAt - days * 24 * 3600 * 1000);
|
||||||
}
|
}
|
||||||
function isUpcoming(tdoc, days = 1) {
|
|
||||||
|
export function isUpcoming(tdoc: Tdoc, days = 1) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
const readyAt = tdoc.beginAt.getTime();
|
const readyAt = tdoc.beginAt.getTime();
|
||||||
return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt);
|
return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt.getTime());
|
||||||
}
|
}
|
||||||
function isNotStarted(tdoc) {
|
|
||||||
|
export function isNotStarted(tdoc: Tdoc) {
|
||||||
return (new Date()) < tdoc.beginAt;
|
return (new Date()) < tdoc.beginAt;
|
||||||
}
|
}
|
||||||
function isOngoing(tdoc) {
|
|
||||||
|
export function isOngoing(tdoc: Tdoc) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
||||||
}
|
}
|
||||||
function isDone(tdoc) {
|
|
||||||
|
export function isDone(tdoc: Tdoc) {
|
||||||
return tdoc.endAt <= new Date();
|
return tdoc.endAt <= new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContestHandlerMixin = (c) => class extends c {
|
export function isExtended(tdoc: Tdoc) {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
return tdoc.penaltySince.getTime() <= now && now < tdoc.endAt.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ContestHandlerMixin = (c) => class extends c {
|
||||||
canViewHiddenScoreboard() {
|
canViewHiddenScoreboard() {
|
||||||
return this.user.hasPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
return this.user.hasPerm(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
canShowRecord(tdoc, allowPermOverride = true) {
|
canShowRecord(tdoc: Tdoc, allowPermOverride = true) {
|
||||||
if (RULES[tdoc.rule].showRecord(tdoc, new Date())) return true;
|
if (RULES[tdoc.rule].showRecord(tdoc, new Date())) return true;
|
||||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
if (allowPermOverride && this.canViewHiddenScoreboard()) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canShowScoreboard(tdoc, allowPermOverride = true) {
|
canShowScoreboard(tdoc: Tdoc, allowPermOverride = true) {
|
||||||
if (RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
|
if (RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
|
||||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
if (allowPermOverride && this.canViewHiddenScoreboard()) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getScoreboard(domainId, tid, isExport = false, docType = document.TYPE_CONTEST) {
|
async getScoreboard(
|
||||||
|
domainId: string, tid: ObjectID,
|
||||||
|
isExport = false, docType = document.TYPE_CONTEST,
|
||||||
|
) {
|
||||||
const tdoc = await get(domainId, tid, docType);
|
const tdoc = await get(domainId, tid, docType);
|
||||||
if (!this.canShowScoreboard(tdoc)) throw new ContestScoreboardHiddenError(tid);
|
if (!this.canShowScoreboard(tdoc)) throw new ContestScoreboardHiddenError(tid);
|
||||||
const tsdocs = await getMultiStatus(domainId, { docId: tid }, docType)
|
const tsdocs = await getMultiStatus(domainId, { docId: tid }, docType)
|
||||||
@@ -346,29 +510,32 @@ const ContestHandlerMixin = (c) => class extends c {
|
|||||||
return [tdoc, rows, udict];
|
return [tdoc, rows, udict];
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyProblems(domainId, pids) { // eslint-disable-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
async verifyProblems(domainId: string, pids: number[]) {
|
||||||
await problem.getList(domainId, pids, true);
|
await problem.getList(domainId, pids, true);
|
||||||
return pids;
|
return pids;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function setStatus(domainId, tid, uid, $set) {
|
export function setStatus(domainId: string, tid: ObjectID, uid: number, $set: any) {
|
||||||
return document.setStatus(domainId, document.TYPE_CONTEST, tid, uid, $set);
|
return document.setStatus(domainId, document.TYPE_CONTEST, tid, uid, $set);
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(domainId, query, type = document.TYPE_CONTEST) {
|
export function count(domainId: string, query: any, type = document.TYPE_CONTEST) {
|
||||||
return document.count(domainId, type, query);
|
return document.count(domainId, type, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMulti(domainId, query = {}, type = document.TYPE_CONTEST) {
|
export function getMulti(domainId: string, query = {}, type = document.TYPE_CONTEST) {
|
||||||
return document.getMulti(domainId, type, query);
|
return document.getMulti(domainId, type, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getStatusJournal(tsdoc) {
|
export function _getStatusJournal(tsdoc) {
|
||||||
return tsdoc.journal.sort((a, b) => (a.rid.generationTime - b.rid.generationTime));
|
return tsdoc.journal.sort((a, b) => (a.rid.generationTime - b.rid.generationTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAndListStatus(domainId, tid, docType = document.TYPE_CONTEST) {
|
export async function getAndListStatus(
|
||||||
|
domainId: string, tid: ObjectID, docType = document.TYPE_CONTEST,
|
||||||
|
) {
|
||||||
// TODO(iceboy): projection, pagination.
|
// TODO(iceboy): projection, pagination.
|
||||||
const tdoc = await get(domainId, tid, docType);
|
const tdoc = await get(domainId, tid, docType);
|
||||||
const tsdocs = await document.getMultiStatus(domainId, docType, { docId: tid })
|
const tsdocs = await document.getMultiStatus(domainId, docType, { docId: tid })
|
||||||
@@ -376,7 +543,7 @@ async function getAndListStatus(domainId, tid, docType = document.TYPE_CONTEST)
|
|||||||
return [tdoc, tsdocs];
|
return [tdoc, tsdocs];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function recalcStatus(domainId, tid, type) {
|
export async function recalcStatus(domainId: string, tid: ObjectID, type = document.TYPE_CONTEST) {
|
||||||
const [tdoc, tsdocs] = await Promise.all([
|
const [tdoc, tsdocs] = await Promise.all([
|
||||||
document.get(domainId, type, tid),
|
document.get(domainId, type, tid),
|
||||||
document.getMultiStatus(domainId, type, { docId: tid }).toArray(),
|
document.getMultiStatus(domainId, type, { docId: tid }).toArray(),
|
||||||
@@ -397,7 +564,23 @@ async function recalcStatus(domainId, tid, type) {
|
|||||||
return await Promise.all(tasks);
|
return await Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.contest = module.exports = {
|
export const statusText = (tdoc: Tdoc) => (
|
||||||
|
isNew(tdoc)
|
||||||
|
? 'New'
|
||||||
|
: isUpcoming(tdoc)
|
||||||
|
? 'Ready (☆▽☆)'
|
||||||
|
: isOngoing(tdoc)
|
||||||
|
? 'Live...'
|
||||||
|
: 'Done');
|
||||||
|
|
||||||
|
export const getStatusText = (tdoc: Tdoc) => (
|
||||||
|
isNotStarted(tdoc)
|
||||||
|
? 'not_started'
|
||||||
|
: isOngoing(tdoc)
|
||||||
|
? 'ongoing'
|
||||||
|
: 'finished');
|
||||||
|
|
||||||
|
global.Hydro.model.contest = {
|
||||||
RULES,
|
RULES,
|
||||||
ContestHandlerMixin,
|
ContestHandlerMixin,
|
||||||
add,
|
add,
|
||||||
@@ -418,18 +601,7 @@ global.Hydro.model.contest = module.exports = {
|
|||||||
isNotStarted,
|
isNotStarted,
|
||||||
isOngoing,
|
isOngoing,
|
||||||
isDone,
|
isDone,
|
||||||
statusText: (tdoc) => (
|
isExtended,
|
||||||
isNew(tdoc)
|
statusText,
|
||||||
? 'New'
|
getStatusText,
|
||||||
: isUpcoming(tdoc)
|
|
||||||
? 'Ready (☆▽☆)'
|
|
||||||
: isOngoing(tdoc)
|
|
||||||
? 'Live...'
|
|
||||||
: 'Done'),
|
|
||||||
getStatusText: (tdoc) => (
|
|
||||||
isNotStarted(tdoc)
|
|
||||||
? 'not_started'
|
|
||||||
: isOngoing(tdoc)
|
|
||||||
? 'ongoing'
|
|
||||||
: 'finished'),
|
|
||||||
};
|
};
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
const { ObjectID } = require('mongodb');
|
import { ObjectID } from 'mongodb';
|
||||||
const problem = require('./problem');
|
import * as problem from './problem';
|
||||||
const document = require('./document');
|
import * as contest from './contest';
|
||||||
const contest = require('./contest');
|
import * as training from './training';
|
||||||
const training = require('./training');
|
import * as document from './document';
|
||||||
const { PERM_VIEW_PROBLEM_HIDDEN } = require('./builtin').PERM;
|
import { PERM } from './builtin';
|
||||||
const { DocumentNotFoundError } = require('../error');
|
import { DocumentNotFoundError } from '../error';
|
||||||
|
|
||||||
const typeDisplay = {
|
export const typeDisplay = {
|
||||||
[document.TYPE_PROBLEM]: 'problem',
|
[document.TYPE_PROBLEM]: 'problem',
|
||||||
[document.TYPE_CONTEST]: 'contest',
|
[document.TYPE_CONTEST]: 'contest',
|
||||||
[document.TYPE_DISCUSSION_NODE]: 'node',
|
[document.TYPE_DISCUSSION_NODE]: 'node',
|
||||||
@@ -14,7 +14,11 @@ const typeDisplay = {
|
|||||||
[document.TYPE_HOMEWORK]: 'homework',
|
[document.TYPE_HOMEWORK]: 'homework',
|
||||||
};
|
};
|
||||||
|
|
||||||
function add(domainId, parentType, parentId, owner, title, content, ip = null, highlight = false) {
|
export function add(
|
||||||
|
domainId: string, parentType: number, parentId: ObjectID,
|
||||||
|
owner: number, title: string, content: string,
|
||||||
|
ip: string | null = null, highlight: boolean = false,
|
||||||
|
) {
|
||||||
return document.add(
|
return document.add(
|
||||||
domainId, content, owner, document.TYPE_DISCUSSION,
|
domainId, content, owner, document.TYPE_DISCUSSION,
|
||||||
null, parentType, parentId,
|
null, parentType, parentId,
|
||||||
@@ -29,15 +33,18 @@ function add(domainId, parentType, parentId, owner, title, content, ip = null, h
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(domainId, did) {
|
export function get(domainId: string, did: ObjectID) {
|
||||||
return document.get(domainId, document.TYPE_DISCUSSION, did);
|
return document.get(domainId, document.TYPE_DISCUSSION, did);
|
||||||
}
|
}
|
||||||
|
|
||||||
function edit(domainId, did, title, content, highlight) {
|
export function edit(
|
||||||
|
domainId: string, did: ObjectID,
|
||||||
|
title: string, content: string, highlight: boolean,
|
||||||
|
) {
|
||||||
return document.set(domainId, document.TYPE_DISCUSSION, did, { title, content, highlight });
|
return document.set(domainId, document.TYPE_DISCUSSION, did, { title, content, highlight });
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(domainId, did) {
|
export function del(domainId: string, did: ObjectID) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
document.deleteOne(domainId, document.TYPE_DISCUSSION, did),
|
document.deleteOne(domainId, document.TYPE_DISCUSSION, did),
|
||||||
document.deleteMulti(domainId, document.TYPE_DISCUSSION_REPLY, {
|
document.deleteMulti(domainId, document.TYPE_DISCUSSION_REPLY, {
|
||||||
@@ -47,15 +54,18 @@ function del(domainId, did) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(domainId, query) {
|
export function count(domainId: string, query: any) {
|
||||||
return document.count(domainId, document.TYPE_DISCUSSION, query);
|
return document.count(domainId, document.TYPE_DISCUSSION, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMulti(domainId, query) {
|
export function getMulti(domainId: string, query: any = {}) {
|
||||||
return document.getMulti(domainId, document.TYPE_DISCUSSION, query).sort('updateAt', -1);
|
return document.getMulti(domainId, document.TYPE_DISCUSSION, query).sort('updateAt', -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addReply(domainId, did, owner, content, ip) {
|
export async function addReply(
|
||||||
|
domainId: string, did: ObjectID, owner: number,
|
||||||
|
content: string, ip: string,
|
||||||
|
) {
|
||||||
const [drdoc] = await Promise.all([
|
const [drdoc] = await Promise.all([
|
||||||
document.add(
|
document.add(
|
||||||
domainId, content, owner, document.TYPE_DISCUSSION_REPLY,
|
domainId, content, owner, document.TYPE_DISCUSSION_REPLY,
|
||||||
@@ -66,15 +76,15 @@ async function addReply(domainId, did, owner, content, ip) {
|
|||||||
return drdoc;
|
return drdoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReply(domainId, drid) {
|
export function getReply(domainId: string, drid: ObjectID) {
|
||||||
return document.get(domainId, document.TYPE_DISCUSSION_REPLY, drid);
|
return document.get(domainId, document.TYPE_DISCUSSION_REPLY, drid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editReply(domainId, drid, content) {
|
export async function editReply(domainId: string, drid: ObjectID, content: string) {
|
||||||
return document.set(domainId, document.TYPE_DISCUSSION_REPLY, drid, { content });
|
return document.set(domainId, document.TYPE_DISCUSSION_REPLY, drid, { content });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function delReply(domainId, drid) {
|
export async function delReply(domainId: string, drid: ObjectID) {
|
||||||
const drdoc = await getReply(domainId, drid);
|
const drdoc = await getReply(domainId, drid);
|
||||||
if (!drdoc) throw new DocumentNotFoundError(drid);
|
if (!drdoc) throw new DocumentNotFoundError(drid);
|
||||||
return await Promise.all([
|
return await Promise.all([
|
||||||
@@ -83,18 +93,21 @@ async function delReply(domainId, drid) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMultiReply(domainId, did) {
|
export function getMultiReply(domainId: string, did: ObjectID) {
|
||||||
return document.getMulti(
|
return document.getMulti(
|
||||||
domainId, document.TYPE_DISCUSSION_REPLY,
|
domainId, document.TYPE_DISCUSSION_REPLY,
|
||||||
{ parentType: document.TYPE_DISCUSSION, parentId: did },
|
{ parentType: document.TYPE_DISCUSSION, parentId: did },
|
||||||
).sort('_id', -1);
|
).sort('_id', -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getListReply(domainId, did) {
|
export function getListReply(domainId: string, did: ObjectID) {
|
||||||
return getMultiReply(domainId, did).toArray();
|
return getMultiReply(domainId, did).toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addTailReply(domainId, drid, owner, content, ip) {
|
export async function addTailReply(
|
||||||
|
domainId: string, drid: ObjectID,
|
||||||
|
owner: number, content: string, ip: string,
|
||||||
|
) {
|
||||||
let drdoc = await document.get(domainId, document.TYPE_DISCUSSION_REPLY, drid);
|
let drdoc = await document.get(domainId, document.TYPE_DISCUSSION_REPLY, drid);
|
||||||
const sid = new ObjectID();
|
const sid = new ObjectID();
|
||||||
[drdoc] = await Promise.all([
|
[drdoc] = await Promise.all([
|
||||||
@@ -104,42 +117,42 @@ async function addTailReply(domainId, drid, owner, content, ip) {
|
|||||||
return [drdoc, sid];
|
return [drdoc, sid];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTailReply(domainId, drid, drrid) {
|
export function getTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) {
|
||||||
return document.getSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid);
|
return document.getSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editTailReply(domainId, drid, drrid, content) {
|
export function editTailReply(domainId: string, drid: ObjectID, drrid: ObjectID, content: string) {
|
||||||
return document.setSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid, { content });
|
return document.setSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid, { content });
|
||||||
}
|
}
|
||||||
|
|
||||||
function delTailReply(domainId, drid, drrid) {
|
export async function delTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) {
|
||||||
return document.deleteSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid);
|
return document.deleteSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStar(domainId, did, uid, star) {
|
export function setStar(domainId: string, did: ObjectID, uid: number, star: boolean) {
|
||||||
return document.setStatus(domainId, document.TYPE_DISCUSSION, did, uid, { star });
|
return document.setStatus(domainId, document.TYPE_DISCUSSION, did, uid, { star });
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatus(domainId, did, uid) {
|
export function getStatus(domainId: string, did: ObjectID, uid: number) {
|
||||||
return document.getStatus(domainId, document.TYPE_DISCUSSION, did, uid);
|
return document.getStatus(domainId, document.TYPE_DISCUSSION, did, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNode(domainId, _id, category, args) {
|
export function addNode(domainId: string, _id: string, category: string, args: any) {
|
||||||
return document.add(
|
return document.add(
|
||||||
domainId, category, 1, document.TYPE_DISCUSSION_NODE,
|
domainId, category, 1, document.TYPE_DISCUSSION_NODE,
|
||||||
_id, null, null, args,
|
_id, null, null, args,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNode(domainId, _id) {
|
export function getNode(domainId: string, _id: string) {
|
||||||
return document.get(domainId, document.TYPE_DISCUSSION_NODE, _id);
|
return document.get(domainId, document.TYPE_DISCUSSION_NODE, _id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getVnode(domainId, ddoc, handler) {
|
export async function getVnode(domainId: string, ddoc: any, handler: any) {
|
||||||
if (ddoc.parentType === document.TYPE_PROBLEM) {
|
if (ddoc.parentType === document.TYPE_PROBLEM) {
|
||||||
const pdoc = await problem.getById(domainId, ddoc.parentId);
|
const pdoc = await problem.get(domainId, ddoc.parentId);
|
||||||
if (!pdoc) return null;
|
if (!pdoc) return null;
|
||||||
if (pdoc.hidden && handler) handler.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
if (pdoc.hidden && handler) handler.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
return { ...pdoc, type: ddoc.parentType, id: ddoc.parentId };
|
return { ...pdoc, type: ddoc.parentType, id: ddoc.parentId };
|
||||||
}
|
}
|
||||||
if (ddoc.parentType === document.TYPE_CONTEST) {
|
if (ddoc.parentType === document.TYPE_CONTEST) {
|
||||||
@@ -170,11 +183,11 @@ async function getVnode(domainId, ddoc, handler) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodes(domainId) {
|
export function getNodes(domainId: string) {
|
||||||
return document.getMulti(domainId, document.TYPE_DISCUSSION_NODE).toArray();
|
return document.getMulti(domainId, document.TYPE_DISCUSSION_NODE).toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getListVnodes(domainId, ddocs, handler) {
|
export async function getListVnodes(domainId: string, ddocs: any, handler: any) {
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
const res = {};
|
const res = {};
|
||||||
function task(ddoc) {
|
function task(ddoc) {
|
||||||
@@ -188,7 +201,7 @@ async function getListVnodes(domainId, ddocs, handler) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.discussion = module.exports = {
|
global.Hydro.model.discussion = {
|
||||||
typeDisplay,
|
typeDisplay,
|
||||||
add,
|
add,
|
||||||
get,
|
get,
|
||||||
@@ -1,28 +1,32 @@
|
|||||||
const assert = require('assert');
|
import assert from 'assert';
|
||||||
const { ObjectID } = require('mongodb');
|
import { ObjectID } from 'mongodb';
|
||||||
const db = require('../service/db');
|
import * as db from '../service/db';
|
||||||
|
|
||||||
|
type DocID = ObjectID | string | number;
|
||||||
|
|
||||||
const coll = db.collection('document');
|
const coll = db.collection('document');
|
||||||
const collStatus = db.collection('document.status');
|
const collStatus = db.collection('document.status');
|
||||||
|
|
||||||
const TYPE_DOMAIN_USER = 0;
|
export const TYPE_DOMAIN_USER = 0;
|
||||||
const TYPE_PROBLEM = 10;
|
export const TYPE_PROBLEM = 10;
|
||||||
const TYPE_PROBLEM_SOLUTION = 11;
|
export const TYPE_PROBLEM_SOLUTION = 11;
|
||||||
const TYPE_PROBLEM_LIST = 12;
|
export const TYPE_PROBLEM_LIST = 12;
|
||||||
const TYPE_DISCUSSION_NODE = 20;
|
export const TYPE_DISCUSSION_NODE = 20;
|
||||||
const TYPE_DISCUSSION = 21;
|
export const TYPE_DISCUSSION = 21;
|
||||||
const TYPE_DISCUSSION_REPLY = 22;
|
export const TYPE_DISCUSSION_REPLY = 22;
|
||||||
const TYPE_CONTEST = 30;
|
export const TYPE_CONTEST = 30;
|
||||||
const TYPE_TRAINING = 40;
|
export const TYPE_TRAINING = 40;
|
||||||
const TYPE_FILE = 50;
|
export const TYPE_FILE = 50;
|
||||||
const TYPE_HOMEWORK = 60;
|
export const TYPE_HOMEWORK = 60;
|
||||||
|
|
||||||
async function add(
|
export async function add(
|
||||||
domainId, content, owner, docType, docId = null,
|
domainId: string, content: string, owner: number,
|
||||||
parentType = null, parentId = null, args = {},
|
docType: number, docId: DocID = null,
|
||||||
|
parentType: number | null = null, parentId: DocID = null,
|
||||||
|
args: any = {},
|
||||||
) {
|
) {
|
||||||
const _id = new ObjectID();
|
const _id = new ObjectID();
|
||||||
const doc = {
|
const doc: any = {
|
||||||
_id,
|
_id,
|
||||||
content,
|
content,
|
||||||
owner,
|
owner,
|
||||||
@@ -40,11 +44,11 @@ async function add(
|
|||||||
return docId || res.insertedId;
|
return docId || res.insertedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(domainId, docType, docId) {
|
export function get(domainId: string, docType: number, docId: DocID) {
|
||||||
return coll.findOne({ domainId, docType, docId });
|
return coll.findOne({ domainId, docType, docId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function set(domainId, docType, docId, args) {
|
export async function set(domainId: string, docType: number, docId: DocID, args: any) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
{ $set: args },
|
{ $set: args },
|
||||||
@@ -53,26 +57,29 @@ async function set(domainId, docType, docId, args) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteOne(domainId, docType, docId) {
|
export function deleteOne(domainId: string, docType: number, docId: DocID) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
coll.deleteMany({ domainId, docType, docId }),
|
coll.deleteMany({ domainId, docType, docId }),
|
||||||
coll.deleteOne({ domainId, docType, docId }),
|
coll.deleteOne({ domainId, docType, docId }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteMulti(domainId, docType, args = {}) {
|
export function deleteMulti(domainId: string, docType: number, args: any = {}) {
|
||||||
return coll.deleteMany({ ...args, domainId, docType });
|
return coll.deleteMany({ ...args, domainId, docType });
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteMultiStatus(domainId, docType, args = {}) {
|
export function deleteMultiStatus(domainId: string, docType: number, args: any = {}) {
|
||||||
return collStatus.deleteMany({ ...args, domainId, docType });
|
return collStatus.deleteMany({ ...args, domainId, docType });
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMulti(domainId, docType, args = {}) {
|
export function getMulti(domainId: string, docType: number, args: any = {}) {
|
||||||
return coll.find({ ...args, docType, domainId });
|
return coll.find({ ...args, docType, domainId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function inc(domainId, docType, docId, key, value) {
|
export async function inc(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
key: string, value: number,
|
||||||
|
) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
{ $inc: { [key]: value } },
|
{ $inc: { [key]: value } },
|
||||||
@@ -81,7 +88,10 @@ async function inc(domainId, docType, docId, key, value) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function incAndSet(domainId, docType, docId, key, value, args) {
|
export async function incAndSet(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
key: string, value: number, args: any,
|
||||||
|
) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
{ $inc: { [key]: value }, $set: args },
|
{ $inc: { [key]: value }, $set: args },
|
||||||
@@ -90,11 +100,14 @@ async function incAndSet(domainId, docType, docId, key, value, args) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(domainId, docType, query) {
|
export function count(domainId: string, docType: number, query: any) {
|
||||||
return coll.find({ ...query, docType, domainId }).count();
|
return coll.find({ ...query, docType, domainId }).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function push(domainId, docType, docId, key, content, owner, args) {
|
export async function push(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
key: string, content: string, owner: number, args: any = {},
|
||||||
|
) {
|
||||||
const _id = new ObjectID();
|
const _id = new ObjectID();
|
||||||
const doc = await coll.findOneAndUpdate(
|
const doc = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
@@ -110,7 +123,10 @@ async function push(domainId, docType, docId, key, content, owner, args) {
|
|||||||
return [doc.value, _id];
|
return [doc.value, _id];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pull(domainId, docType, docId, setKey, contents) {
|
export async function pull(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
setKey: string, contents: string[],
|
||||||
|
) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
{ $pull: { [setKey]: { $in: contents } } },
|
{ $pull: { [setKey]: { $in: contents } } },
|
||||||
@@ -119,7 +135,10 @@ async function pull(domainId, docType, docId, setKey, contents) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteSub(domainId, docType, docId, key, subId) {
|
export async function deleteSub(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
key: string, subId: ObjectID,
|
||||||
|
) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
{ $pull: { [key]: { _id: subId } } },
|
{ $pull: { [key]: { _id: subId } } },
|
||||||
@@ -128,7 +147,10 @@ async function deleteSub(domainId, docType, docId, key, subId) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSub(domainId, docType, docId, key, subId) {
|
export async function getSub(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
key: string, subId: ObjectID,
|
||||||
|
) {
|
||||||
const doc = await coll.findOne({
|
const doc = await coll.findOne({
|
||||||
domainId,
|
domainId,
|
||||||
docType,
|
docType,
|
||||||
@@ -142,7 +164,10 @@ async function getSub(domainId, docType, docId, key, subId) {
|
|||||||
return [doc, null];
|
return [doc, null];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setSub(domainId, docType, docId, key, subId, args) {
|
export async function setSub(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
key: string, subId: ObjectID, args: any,
|
||||||
|
) {
|
||||||
const $set = {};
|
const $set = {};
|
||||||
for (const k in args) {
|
for (const k in args) {
|
||||||
$set[`${key}.$.${k}`] = args[k];
|
$set[`${key}.$.${k}`] = args[k];
|
||||||
@@ -160,7 +185,10 @@ async function setSub(domainId, docType, docId, key, subId, args) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addToSet(domainId, docType, docId, setKey, content) {
|
export async function addToSet(
|
||||||
|
domainId: string, docType: number, docId: DocID,
|
||||||
|
setKey: string, content: string,
|
||||||
|
) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ domainId, docType, docId },
|
{ domainId, docType, docId },
|
||||||
{ $addToSet: { [setKey]: content } },
|
{ $addToSet: { [setKey]: content } },
|
||||||
@@ -169,17 +197,19 @@ async function addToSet(domainId, docType, docId, setKey, content) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatus(domainId, docType, docId, uid) {
|
export function getStatus(domainId: string, docType: number, docId: DocID, uid: number) {
|
||||||
return collStatus.findOne({
|
return collStatus.findOne({
|
||||||
domainId, docType, docId, uid,
|
domainId, docType, docId, uid,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMultiStatus(domainId, docType, args) {
|
export function getMultiStatus(domainId: string, docType: number, args: any) {
|
||||||
return collStatus.find({ ...args, docType, domainId });
|
return collStatus.find({ ...args, docType, domainId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setStatus(domainId, docType, docId, uid, args) {
|
export async function setStatus(
|
||||||
|
domainId: string, docType: number, docId: DocID, uid: number, args: any,
|
||||||
|
) {
|
||||||
const res = await collStatus.findOneAndUpdate(
|
const res = await collStatus.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
domainId, docType, docId, uid,
|
domainId, docType, docId, uid,
|
||||||
@@ -190,14 +220,17 @@ async function setStatus(domainId, docType, docId, uid, args) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMultiStatus(domainId, docType, query, args) {
|
export function setMultiStatus(domainId: string, docType: number, query: any, args: any) {
|
||||||
return collStatus.updateMany(
|
return collStatus.updateMany(
|
||||||
{ domainId, docType, ...query },
|
{ domainId, docType, ...query },
|
||||||
{ $set: args },
|
{ $set: args },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setIfNotStatus(domainId, docType, docId, uid, key, value, ifNot, args) {
|
export async function setIfNotStatus(
|
||||||
|
domainId: string, docType: number, docId: DocID, uid: number,
|
||||||
|
key: string, value: number, ifNot: any, args: any,
|
||||||
|
) {
|
||||||
const res = await collStatus.findOneAndUpdate(
|
const res = await collStatus.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
domainId, docType, docId, uid, key: { $not: { $eq: ifNot } },
|
domainId, docType, docId, uid, key: { $not: { $eq: ifNot } },
|
||||||
@@ -208,9 +241,9 @@ async function setIfNotStatus(domainId, docType, docId, uid, key, value, ifNot,
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cappedIncStatus(
|
export async function cappedIncStatus(
|
||||||
domainId, docType, docId, uid,
|
domainId: string, docType: number, docId: DocID, uid: number,
|
||||||
key, value, minValue = -1, maxValue = 1,
|
key: string, value: number, minValue = -1, maxValue = 1,
|
||||||
) {
|
) {
|
||||||
assert(value !== 0);
|
assert(value !== 0);
|
||||||
const $not = value > 0 ? { $gte: maxValue } : { $lte: minValue };
|
const $not = value > 0 ? { $gte: maxValue } : { $lte: minValue };
|
||||||
@@ -224,7 +257,10 @@ async function cappedIncStatus(
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function incStatus(domainId, docType, docId, uid, key, value) {
|
export async function incStatus(
|
||||||
|
domainId: string, docType: number, docId: DocID, uid: number,
|
||||||
|
key: string, value: number,
|
||||||
|
) {
|
||||||
const res = await collStatus.findOneAndUpdate(
|
const res = await collStatus.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
domainId, docType, docId, uid,
|
domainId, docType, docId, uid,
|
||||||
@@ -235,7 +271,10 @@ async function incStatus(domainId, docType, docId, uid, key, value) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function revPushStatus(domainId, docType, docId, uid, key, value) {
|
export async function revPushStatus(
|
||||||
|
domainId: string, docType: number, docId: DocID, uid: number,
|
||||||
|
key: string, value: any,
|
||||||
|
) {
|
||||||
const res = await collStatus.findOneAndUpdate(
|
const res = await collStatus.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
domainId, docType, docId, uid,
|
domainId, docType, docId, uid,
|
||||||
@@ -246,7 +285,9 @@ async function revPushStatus(domainId, docType, docId, uid, key, value) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function revInitStatus(domainId, docType, docId, uid) {
|
export async function revInitStatus(
|
||||||
|
domainId: string, docType: number, docId: DocID, uid: number,
|
||||||
|
) {
|
||||||
const res = await collStatus.findOneAndUpdate(
|
const res = await collStatus.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
domainId, docType, docId, uid,
|
domainId, docType, docId, uid,
|
||||||
@@ -257,7 +298,10 @@ async function revInitStatus(domainId, docType, docId, uid) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function revSetStatus(domainId, docType, docId, uid, rev, args, returnDoc = true) {
|
export async function revSetStatus(
|
||||||
|
domainId: string, docType: number, docId: DocID, uid: number,
|
||||||
|
rev: number, args: any, returnDoc = true,
|
||||||
|
) {
|
||||||
const filter = {
|
const filter = {
|
||||||
domainId, docType, docId, uid, rev,
|
domainId, docType, docId, uid, rev,
|
||||||
};
|
};
|
||||||
@@ -270,7 +314,7 @@ async function revSetStatus(domainId, docType, docId, uid, rev, args, returnDoc
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureIndexes() {
|
export async function ensureIndexes() {
|
||||||
await coll.createIndex({ domainId: 1, docType: 1, docId: 1 }, { unique: true });
|
await coll.createIndex({ domainId: 1, docType: 1, docId: 1 }, { unique: true });
|
||||||
await coll.createIndex({
|
await coll.createIndex({
|
||||||
domainId: 1, docType: 1, owner: 1, docId: -1,
|
domainId: 1, docType: 1, owner: 1, docId: -1,
|
||||||
@@ -332,7 +376,7 @@ async function ensureIndexes() {
|
|||||||
}, { sparse: true });
|
}, { sparse: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.document = module.exports = {
|
global.Hydro.model.document = {
|
||||||
add,
|
add,
|
||||||
addToSet,
|
addToSet,
|
||||||
cappedIncStatus,
|
cappedIncStatus,
|
||||||
@@ -373,3 +417,5 @@ global.Hydro.model.document = module.exports = {
|
|||||||
TYPE_FILE,
|
TYPE_FILE,
|
||||||
TYPE_TRAINING,
|
TYPE_TRAINING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default global.Hydro.model.document;
|
||||||
@@ -1,21 +1,11 @@
|
|||||||
const builtin = require('./builtin');
|
import * as builtin from './builtin';
|
||||||
const document = require('./document');
|
import * as document from './document';
|
||||||
const db = require('../service/db');
|
import * as db from '../service/db';
|
||||||
const validator = require('../lib/validator');
|
import * as validator from '../lib/validator';
|
||||||
|
|
||||||
const coll = db.collection('domain');
|
const coll = db.collection('domain');
|
||||||
|
|
||||||
/**
|
export function add(domainId: string, owner: number, name: string, isEnsure = false) {
|
||||||
* @typedef {import('../interface').Pdoc} Pdoc
|
|
||||||
* @typedef {import('mongodb').ObjectID} ObjectID
|
|
||||||
* @typedef {import('mongodb').Cursor} Cursor
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {number} owner
|
|
||||||
*/
|
|
||||||
function add(domainId, owner, name, isEnsure = false) {
|
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
if (isEnsure) {
|
if (isEnsure) {
|
||||||
tasks.push(
|
tasks.push(
|
||||||
@@ -43,46 +33,26 @@ function add(domainId, owner, name, isEnsure = false) {
|
|||||||
return Promise.all(tasks);
|
return Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function get(domainId: string) {
|
||||||
* @param {string} domainId
|
|
||||||
* @returns {Ddoc}
|
|
||||||
*/
|
|
||||||
async function get(domainId) {
|
|
||||||
return coll.findOne({ _id: domainId });
|
return coll.findOne({ _id: domainId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function getMany(query: any, sort: any, page: number, limit: number) {
|
||||||
* @param {object} query
|
|
||||||
* @param {object} sort
|
|
||||||
* @param {number} page
|
|
||||||
* @param {number} limit
|
|
||||||
* @returns {Ddoc[]}
|
|
||||||
*/
|
|
||||||
function getMany(query, sort, page, limit) {
|
|
||||||
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit)
|
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit)
|
||||||
.toArray();
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function getMulti(query: any) {
|
||||||
* @param {object} query
|
|
||||||
* @returns {Cursor}
|
|
||||||
*/
|
|
||||||
function getMulti(query) {
|
|
||||||
return coll.find(query);
|
return coll.find(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function edit(domainId: string, $set: any) {
|
||||||
* @param {string} domainId
|
|
||||||
* @param {object} $set
|
|
||||||
* @returns {Ddoc}
|
|
||||||
*/
|
|
||||||
function edit(domainId, $set) {
|
|
||||||
if ($set.title) validator.checkTitle($set.title);
|
if ($set.title) validator.checkTitle($set.title);
|
||||||
if ($set.content) validator.checkContent($set.content);
|
if ($set.content) validator.checkContent($set.content);
|
||||||
return coll.updateOne({ _id: domainId }, { $set });
|
return coll.updateOne({ _id: domainId }, { $set });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function inc(domainId, field, n) {
|
export async function inc(domainId: string, field: string, n: number) {
|
||||||
const res = await coll.findOneAndUpdate(
|
const res = await coll.findOneAndUpdate(
|
||||||
{ _id: domainId },
|
{ _id: domainId },
|
||||||
{ $inc: { [field]: n } },
|
{ $inc: { [field]: n } },
|
||||||
@@ -91,14 +61,14 @@ async function inc(domainId, field, n) {
|
|||||||
return res.value;
|
return res.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getList(domainIds) {
|
export async function getList(domainIds: string[]) {
|
||||||
const r = {};
|
const r = {};
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
for (const domainId of domainIds) r[domainId] = await get(domainId);
|
for (const domainId of domainIds) r[domainId] = await get(domainId);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.domain = module.exports = {
|
global.Hydro.model.domain = {
|
||||||
add,
|
add,
|
||||||
inc,
|
inc,
|
||||||
get,
|
get,
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const { ForbiddenError, NotFoundError } = require('../error');
|
|
||||||
const db = require('../service/db');
|
|
||||||
const gridfs = require('../service/gridfs');
|
|
||||||
const hash = require('../lib/hash.hydro');
|
|
||||||
|
|
||||||
const coll = db.collection('file');
|
|
||||||
const collFile = db.collection('fs.files');
|
|
||||||
const collChunk = db.collection('fs.chunks');
|
|
||||||
|
|
||||||
function _timestamp() {
|
|
||||||
return Math.floor(Number(new Date()) / 1000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function add(streamOrPath, filename, meta = {}) {
|
|
||||||
if (typeof streamOrPath === 'string') streamOrPath = fs.createReadStream(streamOrPath);
|
|
||||||
const w = gridfs.openUploadStream(filename);
|
|
||||||
await coll.insertOne({ ...meta, _id: w.id, secret: String.random(32) });
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
w.on('error', reject);
|
|
||||||
w.on('finish', resolve);
|
|
||||||
streamOrPath.pipe(w);
|
|
||||||
});
|
|
||||||
const c = await gridfs.find({ _id: w.id }).toArray();
|
|
||||||
await coll.updateOne({ _id: w.id }, { $set: { md5: c[0].md5, size: c[0].size } });
|
|
||||||
return w.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
function del(_id) {
|
|
||||||
return Promise.all([
|
|
||||||
coll.deleteOne({ _id }),
|
|
||||||
collFile.deleteOne({ _id }),
|
|
||||||
collChunk.deleteMany({ files_id: _id }),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function inc(_id) {
|
|
||||||
const doc = await coll.findOneAndUpdate(
|
|
||||||
{ _id },
|
|
||||||
{ $inc: { count: 1 } },
|
|
||||||
{ returnOriginal: false },
|
|
||||||
);
|
|
||||||
return (doc.value || {}).count;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function dec(_id) {
|
|
||||||
const file = await coll.findOneAndUpdate(
|
|
||||||
{ _id },
|
|
||||||
{ $inc: { count: -1 } },
|
|
||||||
{ returnOriginal: false },
|
|
||||||
);
|
|
||||||
if (!file.count) await del(_id);
|
|
||||||
return file.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function get(_id, secret) {
|
|
||||||
const file = await coll.findOne({ _id });
|
|
||||||
if (!file) throw new NotFoundError(_id);
|
|
||||||
if (typeof secret !== 'undefined') {
|
|
||||||
const timestamp = _timestamp();
|
|
||||||
if (!(hash(file.secret, timestamp.toString()) === secret)) {
|
|
||||||
if (!(hash(file.secret, timestamp.toString() - 1) === secret)) {
|
|
||||||
throw new ForbiddenError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return gridfs.openDownloadStream(_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMeta(_id) {
|
|
||||||
return coll.findOne({ _id });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function url(_id, name) {
|
|
||||||
const file = await coll.findOne({ _id });
|
|
||||||
const secret = hash(file.secret, _timestamp().toString());
|
|
||||||
if (name) return `/fs/${_id}/${Buffer.from(name).toString('base64')}/${secret}`;
|
|
||||||
return `/fs/${_id}/${secret}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.model.file = module.exports = {
|
|
||||||
add, get, del, inc, dec, getMeta, url,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import { ObjectID } from 'mongodb';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { ForbiddenError, NotFoundError } from '../error';
|
||||||
|
import * as db from '../service/db';
|
||||||
|
import gridfs from '../service/gridfs';
|
||||||
|
import hash from '../lib/hash.hydro';
|
||||||
|
|
||||||
|
const coll = db.collection('file');
|
||||||
|
const collFile = db.collection('fs.files');
|
||||||
|
const collChunk = db.collection('fs.chunks');
|
||||||
|
|
||||||
|
function _timestamp() {
|
||||||
|
return Math.floor(Number(new Date()) / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function add(streamOrPath: fs.ReadStream | string, filename: string, meta = {}) {
|
||||||
|
let stream: fs.ReadStream;
|
||||||
|
if (typeof streamOrPath === 'string') stream = fs.createReadStream(streamOrPath);
|
||||||
|
else stream = streamOrPath;
|
||||||
|
const w = gridfs.openUploadStream(filename);
|
||||||
|
await coll.insertOne({ ...meta, _id: w.id, secret: String.random(32) });
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
w.on('error', reject);
|
||||||
|
w.on('finish', resolve);
|
||||||
|
stream.pipe(w);
|
||||||
|
});
|
||||||
|
const c = await gridfs.find({ _id: w.id }).toArray();
|
||||||
|
await coll.updateOne({ _id: w.id }, { $set: { md5: c[0].md5, size: c[0].size } });
|
||||||
|
return w.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function del(_id: ObjectID) {
|
||||||
|
return Promise.all([
|
||||||
|
coll.deleteOne({ _id }),
|
||||||
|
collFile.deleteOne({ _id }),
|
||||||
|
collChunk.deleteMany({ files_id: _id }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function inc(_id: ObjectID) {
|
||||||
|
const doc = await coll.findOneAndUpdate(
|
||||||
|
{ _id },
|
||||||
|
{ $inc: { count: 1 } },
|
||||||
|
{ returnOriginal: false },
|
||||||
|
);
|
||||||
|
return (doc.value || {}).count;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function dec(_id: ObjectID) {
|
||||||
|
const file = await coll.findOneAndUpdate(
|
||||||
|
{ _id },
|
||||||
|
{ $inc: { count: -1 } },
|
||||||
|
{ returnOriginal: false },
|
||||||
|
);
|
||||||
|
if (!file.value.count) await del(_id);
|
||||||
|
return file.value.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getWithSecret(_id: ObjectID, secret: string) {
|
||||||
|
const file = await coll.findOne({ _id });
|
||||||
|
if (!file) throw new NotFoundError(_id);
|
||||||
|
const timestamp = _timestamp();
|
||||||
|
if (!(hash(file.secret, timestamp.toString()) === secret)) {
|
||||||
|
if (!(hash(file.secret, (timestamp - 1).toString()) === secret)) {
|
||||||
|
throw new ForbiddenError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gridfs.openDownloadStream(_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(_id: ObjectID) {
|
||||||
|
const file = await coll.findOne({ _id });
|
||||||
|
if (!file) throw new NotFoundError(_id);
|
||||||
|
return gridfs.openDownloadStream(_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMeta(_id: ObjectID) {
|
||||||
|
return coll.findOne({ _id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMetaDict(ufids: ObjectID[]) {
|
||||||
|
const r = {};
|
||||||
|
ufids = Array.from(new Set(ufids));
|
||||||
|
const ufdocs = await coll.find({ _id: { $in: ufids } }).toArray();
|
||||||
|
for (const ufdoc of ufdocs) {
|
||||||
|
r[ufdoc._id.toHexString()] = ufdoc;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMulti(query: any) {
|
||||||
|
return coll.find(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function url(_id: ObjectID, name: string | undefined) {
|
||||||
|
const file = await coll.findOne({ _id });
|
||||||
|
const secret = hash(file.secret, _timestamp().toString());
|
||||||
|
if (name) return `/fs/${_id}/${Buffer.from(name).toString('base64')}/${secret}`;
|
||||||
|
return `/fs/${_id}/${secret}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.model.file = {
|
||||||
|
add, get, del, inc, dec, getMeta, getWithSecret, getMulti, getMetaDict, url,
|
||||||
|
};
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
const { MessageNotFoundError } = require('../error');
|
import { ObjectID, QuerySelector } from 'mongodb';
|
||||||
const db = require('../service/db.js');
|
import { MessageNotFoundError } from '../error';
|
||||||
|
import * as db from '../service/db';
|
||||||
|
|
||||||
const coll = db.collection('message');
|
const coll = db.collection('message');
|
||||||
|
|
||||||
async function send(from, to, content) {
|
export async function send(from: number, to: number, content: string) {
|
||||||
const res = await coll.insertOne({
|
const res = await coll.insertOne({
|
||||||
from, to, content, unread: true,
|
from, to, content, unread: true,
|
||||||
});
|
});
|
||||||
@@ -12,42 +13,42 @@ async function send(from, to, content) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function get(_id) {
|
export async function get(_id: ObjectID) {
|
||||||
const doc = await coll.findOne({ _id });
|
const doc = await coll.findOne({ _id });
|
||||||
if (!doc) throw new MessageNotFoundError(_id);
|
if (!doc) throw new MessageNotFoundError(_id);
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getByUser(uid) {
|
export function getByUser(uid: number) {
|
||||||
return coll.find({ $or: [{ from: uid }, { to: uid }] }).sort('_id', 1).toArray();
|
return coll.find({ $or: [{ from: uid }, { to: uid }] }).sort('_id', 1).toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMany(query, sort, page, limit) {
|
export function getMany<T>(query: QuerySelector<T>, sort, page: number, limit: number) {
|
||||||
return coll.find(query).sort(sort)
|
return coll.find(query).sort(sort)
|
||||||
.skip((page - 1) * limit).limit(limit)
|
.skip((page - 1) * limit).limit(limit)
|
||||||
.toArray();
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(_id) {
|
export function del(_id: ObjectID) {
|
||||||
return coll.deleteOne({ _id });
|
return coll.deleteOne({ _id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(query) {
|
export function count<T>(query: QuerySelector<T>) {
|
||||||
return coll.find(query).count();
|
return coll.find(query).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMulti(uid) {
|
export function getMulti(uid: number) {
|
||||||
return coll.find({ $or: [{ from: uid }, { to: uid }] });
|
return coll.find({ $or: [{ from: uid }, { to: uid }] });
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureIndexes() {
|
export function ensureIndexes() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
coll.createIndex({ to: 1, _id: -1 }),
|
coll.createIndex({ to: 1, _id: -1 }),
|
||||||
coll.createIndex({ from: 1, _id: -1 }),
|
coll.createIndex({ from: 1, _id: -1 }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.message = module.exports = {
|
global.Hydro.model.message = {
|
||||||
count,
|
count,
|
||||||
get,
|
get,
|
||||||
getByUser,
|
getByUser,
|
||||||
@@ -1,15 +1,9 @@
|
|||||||
const { OpcountExceededError } = require('../error');
|
import { OpcountExceededError } from '../error';
|
||||||
const db = require('../service/db');
|
import * as db from '../service/db';
|
||||||
|
|
||||||
const coll = db.collection('opcount');
|
const coll = db.collection('opcount');
|
||||||
|
|
||||||
/**
|
export async function inc(op: string, ident: string, periodSecs: number, maxOperations: number) {
|
||||||
* @param {string} op
|
|
||||||
* @param {string} ident
|
|
||||||
* @param {number} period_secs
|
|
||||||
* @param {number} max_operations
|
|
||||||
*/
|
|
||||||
async function inc(op, ident, periodSecs, maxOperations) {
|
|
||||||
const curTime = new Date().getTime();
|
const curTime = new Date().getTime();
|
||||||
const beginAt = new Date(curTime - (curTime % (periodSecs * 1000)));
|
const beginAt = new Date(curTime - (curTime % (periodSecs * 1000)));
|
||||||
const expireAt = new Date(beginAt.getTime() + periodSecs * 1000);
|
const expireAt = new Date(beginAt.getTime() + periodSecs * 1000);
|
||||||
@@ -25,8 +19,8 @@ async function inc(op, ident, periodSecs, maxOperations) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureIndexes() {
|
export function ensureIndexes() {
|
||||||
return coll.createIndex('expireAt', { expireAfterSeconds: 0 });
|
return coll.createIndex('expireAt', { expireAfterSeconds: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.opcount = module.exports = { inc, ensureIndexes };
|
global.Hydro.model.opcount = { inc, ensureIndexes };
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
const { ObjectId } = require('mongodb');
|
|
||||||
const { STATUS_ACCEPTED } = require('./builtin').STATUS;
|
|
||||||
const file = require('./file');
|
|
||||||
const document = require('./document');
|
|
||||||
const domain = require('./domain');
|
|
||||||
const { ProblemNotFoundError } = require('../error');
|
|
||||||
const readConfig = require('../lib/readConfig');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {import('../interface').Pdoc} Pdoc
|
|
||||||
* @typedef {import('mongodb').ObjectID} ObjectID
|
|
||||||
* @typedef {import('mongodb').Cursor} Cursor
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Problem {
|
|
||||||
constructor(pdoc) {
|
|
||||||
this.docType = document.TYPE_PROBLEM;
|
|
||||||
if (pdoc) {
|
|
||||||
this._id = pdoc._id;
|
|
||||||
this.docId = pdoc.docId;
|
|
||||||
this.pid = pdoc.pid;
|
|
||||||
this.title = pdoc.title;
|
|
||||||
this.content = pdoc.content;
|
|
||||||
this.owner = pdoc.owner;
|
|
||||||
this.config = pdoc.config;
|
|
||||||
this.data = pdoc.data;
|
|
||||||
this.nSubmit = pdoc.nSubmit;
|
|
||||||
this.nAccept = pdoc.nAccept;
|
|
||||||
this.difficulty = pdoc.difficulty || 0;
|
|
||||||
this.tag = pdoc.tag || [];
|
|
||||||
this.category = pdoc.category || [];
|
|
||||||
this.hidden = pdoc.hidden || false;
|
|
||||||
} else {
|
|
||||||
this._id = new ObjectId();
|
|
||||||
this.docId = this._id;
|
|
||||||
this.pid = String.random(8);
|
|
||||||
this.title = '*';
|
|
||||||
this.content = '';
|
|
||||||
this.owner = 1;
|
|
||||||
this.config = '';
|
|
||||||
this.data = null;
|
|
||||||
this.nSubmit = 0;
|
|
||||||
this.nAccept = 0;
|
|
||||||
this.difficulty = 0;
|
|
||||||
this.tag = [];
|
|
||||||
this.category = [];
|
|
||||||
this.hidden = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {string} title
|
|
||||||
* @param {string} content
|
|
||||||
* @param {number} owner
|
|
||||||
* @param {object} args
|
|
||||||
* @returns {Promise<ObjectID>} docId
|
|
||||||
*/
|
|
||||||
async function add(domainId, title, content, owner, {
|
|
||||||
pid = null,
|
|
||||||
data = null,
|
|
||||||
category = [],
|
|
||||||
tag = [],
|
|
||||||
hidden = false,
|
|
||||||
}) {
|
|
||||||
const d = await domain.inc(domainId, 'pidCounter', 1);
|
|
||||||
if (!pid) pid = d.pidCounter.toString();
|
|
||||||
return await document.add(
|
|
||||||
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
|
|
||||||
{
|
|
||||||
pid, title, data, category, tag, hidden, nSubmit: 0, nAccept: 0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {string|number} pid
|
|
||||||
* @param {number} uid
|
|
||||||
* @returns {Promise<Problem>}
|
|
||||||
*/
|
|
||||||
async function get(domainId, pid, uid = null, doThrow = true) {
|
|
||||||
if (!Number.isNaN(parseInt(pid, 10))) pid = parseInt(pid, 10);
|
|
||||||
const pdoc = Number.isInteger(pid)
|
|
||||||
? await document.get(domainId, document.TYPE_PROBLEM, pid)
|
|
||||||
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { pid }).toArray())[0];
|
|
||||||
if (!pdoc) {
|
|
||||||
if (doThrow) throw new ProblemNotFoundError(domainId, pid);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (uid) {
|
|
||||||
pdoc.psdoc = await document.getStatus(domainId, document.TYPE_PROBLEM, pdoc.docId, uid);
|
|
||||||
}
|
|
||||||
return new Problem(pdoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {object} query
|
|
||||||
* @param {object} sort
|
|
||||||
* @param {number} page
|
|
||||||
* @param {number} limit
|
|
||||||
* @returns {Promise<Problem[]>}
|
|
||||||
*/
|
|
||||||
async function getMany(domainId, query, sort, page, limit) {
|
|
||||||
const pdocs = await document.getMulti(domainId, query)
|
|
||||||
.sort(sort).skip((page - 1) * limit).limit(limit)
|
|
||||||
.toArray();
|
|
||||||
for (let i = 0; i < pdocs.length; i++) {
|
|
||||||
pdocs[i] = new Problem(pdocs[i]);
|
|
||||||
}
|
|
||||||
return pdocs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {object} query
|
|
||||||
* @returns {Cursor}
|
|
||||||
*/
|
|
||||||
function getMulti(domainId, query) {
|
|
||||||
return document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {object} query
|
|
||||||
* @returns {Cursor}
|
|
||||||
*/
|
|
||||||
function getMultiStatus(domainId, query) {
|
|
||||||
return document.getMultiStatus(domainId, document.TYPE_PROBLEM, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {ObjectID} _id
|
|
||||||
* @param {object} query
|
|
||||||
* @returns {Promise<Problem>}
|
|
||||||
*/
|
|
||||||
async function edit(domainId, _id, $set) {
|
|
||||||
const pdoc = await document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
|
||||||
return new Problem(pdoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
function inc(domainId, _id, field, n) {
|
|
||||||
return document.inc(domainId, document.TYPE_PROBLEM, _id, field, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function count(domainId, query) {
|
|
||||||
return document.count(domainId, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function random(domainId, query) {
|
|
||||||
const cursor = document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
|
||||||
const pcount = await cursor.count();
|
|
||||||
if (pcount) {
|
|
||||||
const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray();
|
|
||||||
return pdoc[0].pid;
|
|
||||||
} return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getList(domainId, pids, getHidden = false, doThrow = true) {
|
|
||||||
pids = Array.from(new Set(pids));
|
|
||||||
const r = {};
|
|
||||||
const q = { $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] };
|
|
||||||
if (!getHidden) q.hidden = false;
|
|
||||||
const pdocs = await document.getMulti(domainId, document.TYPE_PROBLEM, q).toArray();
|
|
||||||
for (const pdoc of pdocs) {
|
|
||||||
r[pdoc.docId] = r[pdoc.pid] = pdoc;
|
|
||||||
}
|
|
||||||
if (pdocs.length !== pids.length) {
|
|
||||||
for (const pid of pids) {
|
|
||||||
if (!r[pid]) {
|
|
||||||
if (doThrow) {
|
|
||||||
throw new ProblemNotFoundError(domainId, pid);
|
|
||||||
} else {
|
|
||||||
r[pid] = new Problem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getListStatus(domainId, uid, pids) {
|
|
||||||
const psdocs = await getMultiStatus(
|
|
||||||
domainId, { uid, docId: { $in: Array.from(new Set(pids)) } },
|
|
||||||
).toArray();
|
|
||||||
const r = {};
|
|
||||||
for (const psdoc of psdocs) r[psdoc.docId] = psdoc;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateStatus(domainId, pid, uid, rid, status) {
|
|
||||||
try {
|
|
||||||
await document.setIfNotStatus(domainId, document.TYPE_PROBLEM, pid, uid, 'status', status, STATUS_ACCEPTED, { rid });
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setStar(domainId, pid, uid, star) {
|
|
||||||
return document.setStatus(domainId, document.TYPE_PROBLEM, pid, uid, { star });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setTestdata(domainId, _id, filePath) {
|
|
||||||
const pdoc = await get(domainId, _id);
|
|
||||||
const config = await readConfig(filePath);
|
|
||||||
const id = await file.add(filePath, 'data.zip');
|
|
||||||
if (pdoc.data && typeof pdoc.data === 'object') file.dec(pdoc.data);
|
|
||||||
return await edit(domainId, _id, { data: id, config });
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.model.problem = module.exports = {
|
|
||||||
add,
|
|
||||||
inc,
|
|
||||||
get,
|
|
||||||
getMany,
|
|
||||||
edit,
|
|
||||||
count,
|
|
||||||
random,
|
|
||||||
getMulti,
|
|
||||||
getList,
|
|
||||||
getListStatus,
|
|
||||||
getMultiStatus,
|
|
||||||
setStar,
|
|
||||||
setTestdata,
|
|
||||||
updateStatus,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
import { ObjectID, FilterQuery } from 'mongodb';
|
||||||
|
import { STATUS } from './builtin';
|
||||||
|
import * as file from './file';
|
||||||
|
import * as document from './document';
|
||||||
|
import * as domain from './domain';
|
||||||
|
import { ProblemNotFoundError } from '../error';
|
||||||
|
import readConfig from '../lib/readConfig';
|
||||||
|
|
||||||
|
export interface Pdoc {
|
||||||
|
_id: ObjectID
|
||||||
|
domainId: string
|
||||||
|
docId: number
|
||||||
|
pid: string
|
||||||
|
owner: number
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
nSubmit: number
|
||||||
|
nAccept: number
|
||||||
|
tag: string[]
|
||||||
|
category: string[],
|
||||||
|
data: ObjectID | null
|
||||||
|
hidden: boolean
|
||||||
|
config: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pdocHidden: Pdoc = {
|
||||||
|
_id: new ObjectID(),
|
||||||
|
domainId: 'system',
|
||||||
|
docId: -1,
|
||||||
|
pid: '',
|
||||||
|
owner: 1,
|
||||||
|
title: '*',
|
||||||
|
content: '',
|
||||||
|
nSubmit: 0,
|
||||||
|
nAccept: 0,
|
||||||
|
tag: [],
|
||||||
|
category: [],
|
||||||
|
data: null,
|
||||||
|
hidden: true,
|
||||||
|
config: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function add(domainId: string, title: string, content: string, owner: number, {
|
||||||
|
pid = null,
|
||||||
|
data = null,
|
||||||
|
category = [],
|
||||||
|
tag = [],
|
||||||
|
hidden = false,
|
||||||
|
}) {
|
||||||
|
const d = await domain.inc(domainId, 'pidCounter', 1);
|
||||||
|
if (!pid) pid = d.pidCounter.toString();
|
||||||
|
return await document.add(
|
||||||
|
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
|
||||||
|
{
|
||||||
|
pid, title, data, category, tag, hidden, nSubmit: 0, nAccept: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(
|
||||||
|
domainId: string, pid: string | number,
|
||||||
|
uid: number = null, doThrow = true,
|
||||||
|
): Promise<Pdoc> {
|
||||||
|
if (typeof pid !== 'number') {
|
||||||
|
if (!Number.isNaN(parseInt(pid, 10))) pid = parseInt(pid, 10);
|
||||||
|
else if (doThrow) throw new ProblemNotFoundError(domainId, pid);
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
const pdoc = Number.isInteger(pid)
|
||||||
|
? await document.get(domainId, document.TYPE_PROBLEM, pid)
|
||||||
|
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { pid }).toArray())[0];
|
||||||
|
if (!pdoc) {
|
||||||
|
if (doThrow) throw new ProblemNotFoundError(domainId, pid);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (uid) {
|
||||||
|
pdoc.psdoc = await document.getStatus(domainId, document.TYPE_PROBLEM, pdoc.docId, uid);
|
||||||
|
}
|
||||||
|
return pdoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMany(
|
||||||
|
domainId: string, query: any, sort: any, page: number, limit: number,
|
||||||
|
): Promise<Pdoc[]> {
|
||||||
|
return document.getMulti(domainId, query)
|
||||||
|
.sort(sort).skip((page - 1) * limit).limit(limit)
|
||||||
|
.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMulti(domainId: string, query: object) {
|
||||||
|
return document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMultiStatus(domainId: string, query: object) {
|
||||||
|
return document.getMultiStatus(domainId, document.TYPE_PROBLEM, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function edit(domainId: string, _id: number, $set: any): Promise<Pdoc> {
|
||||||
|
return document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inc(domainId: string, _id: number, field: string, n: number) {
|
||||||
|
return document.inc(domainId, document.TYPE_PROBLEM, _id, field, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function count<T>(domainId: string, query: FilterQuery<T>) {
|
||||||
|
return document.count(domainId, document.TYPE_PROBLEM, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function random<T>(domainId: string, query: FilterQuery<T>) {
|
||||||
|
const cursor = document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
||||||
|
const pcount = await cursor.count();
|
||||||
|
if (pcount) {
|
||||||
|
const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray();
|
||||||
|
return pdoc[0].pid;
|
||||||
|
} return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getList(domainId: string, pids: number[], getHidden = false, doThrow = true) {
|
||||||
|
pids = Array.from(new Set(pids));
|
||||||
|
const r = {};
|
||||||
|
const q: any = { $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] };
|
||||||
|
if (!getHidden) q.hidden = false;
|
||||||
|
const pdocs = await document.getMulti(domainId, document.TYPE_PROBLEM, q).toArray();
|
||||||
|
for (const pdoc of pdocs) {
|
||||||
|
r[pdoc.docId] = pdoc;
|
||||||
|
r[pdoc.pid] = pdoc;
|
||||||
|
}
|
||||||
|
if (pdocs.length !== pids.length) {
|
||||||
|
for (const pid of pids) {
|
||||||
|
if (!r[pid]) {
|
||||||
|
if (doThrow) {
|
||||||
|
throw new ProblemNotFoundError(domainId, pid);
|
||||||
|
} else {
|
||||||
|
r[pid] = pdocHidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getListStatus(domainId: string, uid: number, pids: number[]) {
|
||||||
|
const psdocs = await getMultiStatus(
|
||||||
|
domainId, { uid, docId: { $in: Array.from(new Set(pids)) } },
|
||||||
|
).toArray();
|
||||||
|
const r = {};
|
||||||
|
for (const psdoc of psdocs) r[psdoc.docId] = psdoc;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateStatus(
|
||||||
|
domainId: string, pid: number, uid: number,
|
||||||
|
rid: ObjectID, status: number,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await document.setIfNotStatus(
|
||||||
|
domainId, document.TYPE_PROBLEM, pid, uid,
|
||||||
|
'status', status, STATUS.STATUS_ACCEPTED, { rid },
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setStar(domainId: string, pid: number, uid: number, star: boolean) {
|
||||||
|
return document.setStatus(domainId, document.TYPE_PROBLEM, pid, uid, { star });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setTestdata(domainId: string, _id: number, filePath: string) {
|
||||||
|
const pdoc = await get(domainId, _id);
|
||||||
|
const config = await readConfig(filePath);
|
||||||
|
const id = await file.add(filePath, 'data.zip');
|
||||||
|
if (pdoc.data && typeof pdoc.data === 'object') file.dec(pdoc.data);
|
||||||
|
return await edit(domainId, _id, { data: id, config });
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.model.problem = {
|
||||||
|
add,
|
||||||
|
inc,
|
||||||
|
get,
|
||||||
|
getMany,
|
||||||
|
edit,
|
||||||
|
count,
|
||||||
|
random,
|
||||||
|
getMulti,
|
||||||
|
getList,
|
||||||
|
getListStatus,
|
||||||
|
getMultiStatus,
|
||||||
|
setStar,
|
||||||
|
setTestdata,
|
||||||
|
updateStatus,
|
||||||
|
};
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
const _ = require('lodash');
|
|
||||||
const { ObjectID } = require('mongodb');
|
|
||||||
const { STATUS_WAITING } = require('./builtin').STATUS;
|
|
||||||
const task = require('./task');
|
|
||||||
const problem = require('./problem');
|
|
||||||
const { RecordNotFoundError } = require('../error');
|
|
||||||
const db = require('../service/db');
|
|
||||||
|
|
||||||
const coll = db.collection('record');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {import('../interface').Record} data
|
|
||||||
*/
|
|
||||||
async function add(domainId, data, addTask = true) {
|
|
||||||
_.defaults(data, {
|
|
||||||
status: STATUS_WAITING,
|
|
||||||
domainId,
|
|
||||||
score: 0,
|
|
||||||
time: 0,
|
|
||||||
memory: 0,
|
|
||||||
hidden: false,
|
|
||||||
judgeTexts: [],
|
|
||||||
compilerTexts: [],
|
|
||||||
testCases: [],
|
|
||||||
judger: null,
|
|
||||||
judgeAt: null,
|
|
||||||
});
|
|
||||||
const [pdoc, res] = await Promise.all([
|
|
||||||
problem.get(domainId, data.pid, null, false),
|
|
||||||
coll.insertOne(data),
|
|
||||||
]);
|
|
||||||
if (addTask) {
|
|
||||||
const t = {
|
|
||||||
type: data.type || 'judge',
|
|
||||||
event: data.type || 'judge',
|
|
||||||
rid: res.insertedId,
|
|
||||||
domainId,
|
|
||||||
pid: data.pid,
|
|
||||||
lang: data.lang,
|
|
||||||
code: data.code,
|
|
||||||
};
|
|
||||||
if (t.type === 'judge') {
|
|
||||||
t.data = pdoc.data;
|
|
||||||
t.config = pdoc.config;
|
|
||||||
} else {
|
|
||||||
t.config = {
|
|
||||||
time: data.time,
|
|
||||||
memory: data.memory,
|
|
||||||
input: data.input,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
await task.add(t);
|
|
||||||
}
|
|
||||||
return res.insertedId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} domainId
|
|
||||||
* @param {ObjectID} rid
|
|
||||||
* @returns {import('../interface').Record}
|
|
||||||
*/
|
|
||||||
async function get(domainId, rid) {
|
|
||||||
const _id = new ObjectID(rid);
|
|
||||||
const rdoc = await coll.findOne({ domainId, _id });
|
|
||||||
if (!rdoc) throw new RecordNotFoundError(rid);
|
|
||||||
return rdoc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMulti(domainId, query) {
|
|
||||||
return coll.find({ ...query, domainId });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function update(domainId, rid, $set, $push, $unset) {
|
|
||||||
const _id = new ObjectID(rid);
|
|
||||||
const $update = {};
|
|
||||||
if ($set && Object.keys($set).length) $update.$set = $set;
|
|
||||||
if ($push && Object.keys($push).length) $update.$push = $push;
|
|
||||||
if ($unset && Object.keys($unset).length) $update.$unset = $unset;
|
|
||||||
const res = await coll.findOneAndUpdate({ domainId, _id }, $update, { returnOriginal: false });
|
|
||||||
if (!res.value) throw new RecordNotFoundError(rid);
|
|
||||||
return res.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset(domainId, rid) {
|
|
||||||
return update(domainId, rid, {
|
|
||||||
score: 0,
|
|
||||||
status: STATUS_WAITING,
|
|
||||||
time: 0,
|
|
||||||
memory: 0,
|
|
||||||
testCases: [],
|
|
||||||
judgeTexts: [],
|
|
||||||
compilerTexts: [],
|
|
||||||
judgeAt: null,
|
|
||||||
judger: null,
|
|
||||||
rejudged: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function count(domainId, query) {
|
|
||||||
return coll.find({ domainId, ...query }).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getList(domainId, rids, showHidden = false) {
|
|
||||||
const r = {};
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
for (const rid of rids) r[rid] = await get(domainId, rid, showHidden);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUserInProblemMulti(domainId, uid, pid) {
|
|
||||||
return coll.find({ domainId, uid, pid });
|
|
||||||
}
|
|
||||||
|
|
||||||
function getByUid(domainId, uid) {
|
|
||||||
return coll.find({ domainId, uid }).toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function judge(domainId, rid) {
|
|
||||||
const rdoc = await get(domainId, rid);
|
|
||||||
const pdoc = await problem.get(domainId, rdoc.pid);
|
|
||||||
await task.add({
|
|
||||||
type: 'judge',
|
|
||||||
rid,
|
|
||||||
domainId,
|
|
||||||
config: pdoc.config || '',
|
|
||||||
pid: rdoc.pid,
|
|
||||||
data: pdoc.data,
|
|
||||||
lang: rdoc.lang,
|
|
||||||
code: rdoc.code,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function rejudge(domainId, rid) {
|
|
||||||
await reset(domainId, rid);
|
|
||||||
const rdoc = await get(domainId, rid);
|
|
||||||
const pdoc = await problem.get(domainId, rdoc.pid);
|
|
||||||
await task.add({
|
|
||||||
type: 'judge',
|
|
||||||
rid,
|
|
||||||
domainId,
|
|
||||||
pid: rdoc.pid,
|
|
||||||
data: pdoc.data,
|
|
||||||
lang: rdoc.lang,
|
|
||||||
code: rdoc.code,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.model.record = module.exports = {
|
|
||||||
add,
|
|
||||||
get,
|
|
||||||
getMulti,
|
|
||||||
update,
|
|
||||||
count,
|
|
||||||
reset,
|
|
||||||
getList,
|
|
||||||
getUserInProblemMulti,
|
|
||||||
getByUid,
|
|
||||||
judge,
|
|
||||||
rejudge,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import { ObjectID, FilterQuery } from 'mongodb';
|
||||||
|
import { STATUS } from './builtin';
|
||||||
|
import * as task from './task';
|
||||||
|
import * as problem from './problem';
|
||||||
|
import { RecordNotFoundError } from '../error';
|
||||||
|
import * as db from '../service/db';
|
||||||
|
|
||||||
|
const coll = db.collection('record');
|
||||||
|
|
||||||
|
export interface TestCase {
|
||||||
|
time: number,
|
||||||
|
memory: number,
|
||||||
|
status: number,
|
||||||
|
message: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Rdoc {
|
||||||
|
_id: ObjectID,
|
||||||
|
domainId: string,
|
||||||
|
pid: ObjectID
|
||||||
|
uid: number,
|
||||||
|
lang: string,
|
||||||
|
code: string,
|
||||||
|
score: number,
|
||||||
|
memory: number,
|
||||||
|
time: number,
|
||||||
|
judgeTexts: string[],
|
||||||
|
compilerTexts: string[],
|
||||||
|
testCases: TestCase[],
|
||||||
|
rejudged: boolean,
|
||||||
|
judger: string,
|
||||||
|
judgeAt: Date,
|
||||||
|
status: number,
|
||||||
|
type: string,
|
||||||
|
hidden: boolean,
|
||||||
|
input?: string,
|
||||||
|
tid?: ObjectID,
|
||||||
|
ttype?: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RdocBase {
|
||||||
|
_id?: ObjectID,
|
||||||
|
domainId?: string,
|
||||||
|
pid: number
|
||||||
|
uid: number,
|
||||||
|
lang: string,
|
||||||
|
code: string,
|
||||||
|
score?: number,
|
||||||
|
memory?: number,
|
||||||
|
time?: number,
|
||||||
|
judgeTexts?: string[],
|
||||||
|
compilerTexts?: string[],
|
||||||
|
testCases?: TestCase[],
|
||||||
|
rejudged?: boolean,
|
||||||
|
judger?: string,
|
||||||
|
judgeAt?: Date,
|
||||||
|
status?: number,
|
||||||
|
type?: string,
|
||||||
|
hidden?: boolean,
|
||||||
|
input?: string,
|
||||||
|
tid?: ObjectID,
|
||||||
|
ttype?: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JudgeTask {
|
||||||
|
_id: ObjectID,
|
||||||
|
rid: ObjectID,
|
||||||
|
domainId: string,
|
||||||
|
pid: number
|
||||||
|
lang: string,
|
||||||
|
code: string,
|
||||||
|
data?: ObjectID,
|
||||||
|
config: string,
|
||||||
|
type?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function add(domainId: string, data: RdocBase, addTask = true) {
|
||||||
|
_.defaults(data, {
|
||||||
|
status: STATUS.STATUS_WAITING,
|
||||||
|
type: 'judge',
|
||||||
|
domainId,
|
||||||
|
score: 0,
|
||||||
|
time: 0,
|
||||||
|
memory: 0,
|
||||||
|
hidden: false,
|
||||||
|
judgeTexts: [],
|
||||||
|
compilerTexts: [],
|
||||||
|
testCases: [],
|
||||||
|
judger: null,
|
||||||
|
judgeAt: null,
|
||||||
|
});
|
||||||
|
const [pdoc, res] = await Promise.all([
|
||||||
|
problem.get(domainId, data.pid, null, false),
|
||||||
|
coll.insertOne(data),
|
||||||
|
]);
|
||||||
|
if (addTask) {
|
||||||
|
const t: any = {
|
||||||
|
type: data.type || 'judge',
|
||||||
|
rid: res.insertedId,
|
||||||
|
domainId,
|
||||||
|
pid: data.pid,
|
||||||
|
lang: data.lang,
|
||||||
|
code: data.code,
|
||||||
|
};
|
||||||
|
if (t.type === 'judge') {
|
||||||
|
t.data = pdoc.data;
|
||||||
|
t.config = pdoc.config;
|
||||||
|
} else {
|
||||||
|
t.config = yaml.safeDump({
|
||||||
|
time: data.time,
|
||||||
|
memory: data.memory,
|
||||||
|
input: data.input,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await task.add(t);
|
||||||
|
}
|
||||||
|
return res.insertedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(domainId: string, _id: ObjectID) {
|
||||||
|
const rdoc = await coll.findOne({ domainId, _id });
|
||||||
|
if (!rdoc) throw new RecordNotFoundError(_id);
|
||||||
|
return rdoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMulti(domainId: string, query: any) {
|
||||||
|
return coll.find({ ...query, domainId });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(
|
||||||
|
domainId: string, _id: ObjectID,
|
||||||
|
$set: any = {}, $push: any = {}, $unset: any = {},
|
||||||
|
) {
|
||||||
|
const $update: any = {};
|
||||||
|
if ($set && Object.keys($set).length) $update.$set = $set;
|
||||||
|
if ($push && Object.keys($push).length) $update.$push = $push;
|
||||||
|
if ($unset && Object.keys($unset).length) $update.$unset = $unset;
|
||||||
|
const res = await coll.findOneAndUpdate({ domainId, _id }, $update, { returnOriginal: false });
|
||||||
|
if (!res.value) throw new RecordNotFoundError(_id);
|
||||||
|
return res.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reset(domainId: string, rid: ObjectID) {
|
||||||
|
return update(domainId, rid, {
|
||||||
|
score: 0,
|
||||||
|
status: STATUS.STATUS_WAITING,
|
||||||
|
time: 0,
|
||||||
|
memory: 0,
|
||||||
|
testCases: [],
|
||||||
|
judgeTexts: [],
|
||||||
|
compilerTexts: [],
|
||||||
|
judgeAt: null,
|
||||||
|
judger: null,
|
||||||
|
rejudged: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function count<T>(domainId: string, query: FilterQuery<T>) {
|
||||||
|
return coll.find({ domainId, ...query }).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getList(domainId: string, rids: ObjectID[], showHidden = false) {
|
||||||
|
const r = {};
|
||||||
|
for (const rid of rids) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const rdoc = await get(domainId, rid);
|
||||||
|
if (rdoc.hidden && !showHidden) r[rid.toHexString()] = null;
|
||||||
|
else r[rid.toHexString()] = rdoc;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserInProblemMulti(
|
||||||
|
domainId: string, uid: number, pid: number,
|
||||||
|
getHidden = false,
|
||||||
|
) {
|
||||||
|
if (!getHidden) {
|
||||||
|
return coll.find({
|
||||||
|
domainId, uid, pid, hidden: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return coll.find({ domainId, uid, pid });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getByUid(domainId: string, uid: number) {
|
||||||
|
return coll.find({ domainId, uid }).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function judge(domainId: string, rid: ObjectID) {
|
||||||
|
const rdoc = await get(domainId, rid);
|
||||||
|
const pdoc = await problem.get(domainId, rdoc.pid);
|
||||||
|
await task.add({
|
||||||
|
type: 'judge',
|
||||||
|
rid,
|
||||||
|
domainId,
|
||||||
|
config: pdoc.config || '',
|
||||||
|
pid: rdoc.pid,
|
||||||
|
data: pdoc.data,
|
||||||
|
lang: rdoc.lang,
|
||||||
|
code: rdoc.code,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rejudge(domainId: string, rid: ObjectID) {
|
||||||
|
await reset(domainId, rid);
|
||||||
|
const rdoc = await get(domainId, rid);
|
||||||
|
const pdoc = await problem.get(domainId, rdoc.pid);
|
||||||
|
await task.add({
|
||||||
|
type: 'judge',
|
||||||
|
rid,
|
||||||
|
domainId,
|
||||||
|
pid: rdoc.pid,
|
||||||
|
data: pdoc.data,
|
||||||
|
lang: rdoc.lang,
|
||||||
|
code: rdoc.code,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.model.record = {
|
||||||
|
add,
|
||||||
|
get,
|
||||||
|
getMulti,
|
||||||
|
update,
|
||||||
|
count,
|
||||||
|
reset,
|
||||||
|
getList,
|
||||||
|
getUserInProblemMulti,
|
||||||
|
getByUid,
|
||||||
|
judge,
|
||||||
|
rejudge,
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const moment = require('moment-timezone');
|
import moment from 'moment-timezone';
|
||||||
const builtin = require('./builtin');
|
import * as builtin from './builtin';
|
||||||
|
|
||||||
const countries = moment.tz.countries();
|
const countries = moment.tz.countries();
|
||||||
const tzs = new Set();
|
const tzs = new Set();
|
||||||
@@ -9,22 +9,22 @@ for (const country of countries) {
|
|||||||
}
|
}
|
||||||
const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]);
|
const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]);
|
||||||
|
|
||||||
const FLAG_HIDDEN = 1;
|
export const FLAG_HIDDEN = 1;
|
||||||
const FLAG_DISABLED = 2;
|
export const FLAG_DISABLED = 2;
|
||||||
const FLAG_SECRET = 4;
|
export const FLAG_SECRET = 4;
|
||||||
|
|
||||||
const PREFERENCE_SETTINGS = [];
|
export const PREFERENCE_SETTINGS = [];
|
||||||
const ACCOUNT_SETTINGS = [];
|
export const ACCOUNT_SETTINGS = [];
|
||||||
const DOMAIN_SETTINGS = [];
|
export const DOMAIN_SETTINGS = [];
|
||||||
const DOMAIN_USER_SETTINGS = [];
|
export const DOMAIN_USER_SETTINGS = [];
|
||||||
const SYSTEM_SETTINGS = [];
|
export const SYSTEM_SETTINGS = [];
|
||||||
const SETTINGS = [];
|
export const SETTINGS = [];
|
||||||
const SETTINGS_BY_KEY = {};
|
export const SETTINGS_BY_KEY = {};
|
||||||
const DOMAIN_USER_SETTINGS_BY_KEY = {};
|
export const DOMAIN_USER_SETTINGS_BY_KEY = {};
|
||||||
const DOMAIN_SETTINGS_BY_KEY = {};
|
export const DOMAIN_SETTINGS_BY_KEY = {};
|
||||||
const SYSTEM_SETTINGS_BY_KEY = {};
|
export const SYSTEM_SETTINGS_BY_KEY = {};
|
||||||
|
|
||||||
const Setting = (
|
export const Setting = (
|
||||||
family, key, range = null,
|
family, key, range = null,
|
||||||
value = null, type = 'text', name = '',
|
value = null, type = 'text', name = '',
|
||||||
desc = '', flag = 0,
|
desc = '', flag = 0,
|
||||||
@@ -32,33 +32,33 @@ const Setting = (
|
|||||||
family, key, range, value, type, name, desc, flag,
|
family, key, range, value, type, name, desc, flag,
|
||||||
});
|
});
|
||||||
|
|
||||||
const PreferenceSetting = (...settings) => {
|
export const PreferenceSetting = (...settings) => {
|
||||||
for (const setting of settings) {
|
for (const setting of settings) {
|
||||||
PREFERENCE_SETTINGS.push(setting);
|
PREFERENCE_SETTINGS.push(setting);
|
||||||
SETTINGS.push(setting);
|
SETTINGS.push(setting);
|
||||||
SETTINGS_BY_KEY[setting.key] = setting;
|
SETTINGS_BY_KEY[setting.key] = setting;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const AccountSetting = (...settings) => {
|
export const AccountSetting = (...settings) => {
|
||||||
for (const setting of settings) {
|
for (const setting of settings) {
|
||||||
ACCOUNT_SETTINGS.push(setting);
|
ACCOUNT_SETTINGS.push(setting);
|
||||||
SETTINGS.push(setting);
|
SETTINGS.push(setting);
|
||||||
SETTINGS_BY_KEY[setting.key] = setting;
|
SETTINGS_BY_KEY[setting.key] = setting;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const DomainUserSetting = (...settings) => {
|
export const DomainUserSetting = (...settings) => {
|
||||||
for (const setting of settings) {
|
for (const setting of settings) {
|
||||||
DOMAIN_USER_SETTINGS.push(setting);
|
DOMAIN_USER_SETTINGS.push(setting);
|
||||||
DOMAIN_USER_SETTINGS_BY_KEY[setting.key] = setting;
|
DOMAIN_USER_SETTINGS_BY_KEY[setting.key] = setting;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const DomainSetting = (...settings) => {
|
export const DomainSetting = (...settings) => {
|
||||||
for (const setting of settings) {
|
for (const setting of settings) {
|
||||||
DOMAIN_SETTINGS.push(setting);
|
DOMAIN_SETTINGS.push(setting);
|
||||||
DOMAIN_SETTINGS_BY_KEY[setting.key] = setting;
|
DOMAIN_SETTINGS_BY_KEY[setting.key] = setting;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const SystemSetting = (...settings) => {
|
export const SystemSetting = (...settings) => {
|
||||||
for (const setting of settings) {
|
for (const setting of settings) {
|
||||||
SYSTEM_SETTINGS.push(setting);
|
SYSTEM_SETTINGS.push(setting);
|
||||||
SYSTEM_SETTINGS_BY_KEY[setting.key] = setting;
|
SYSTEM_SETTINGS_BY_KEY[setting.key] = setting;
|
||||||
@@ -130,7 +130,7 @@ SystemSetting(
|
|||||||
Setting('setting_constant', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page'),
|
Setting('setting_constant', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page'),
|
||||||
);
|
);
|
||||||
|
|
||||||
global.Hydro.model.setting = module.exports = {
|
global.Hydro.model.setting = {
|
||||||
Setting,
|
Setting,
|
||||||
PreferenceSetting,
|
PreferenceSetting,
|
||||||
AccountSetting,
|
AccountSetting,
|
||||||
@@ -1,67 +1,63 @@
|
|||||||
const document = require('./document');
|
import { ObjectID } from 'mongodb';
|
||||||
const { SolutionNotFoundError } = require('../error');
|
import * as document from './document';
|
||||||
|
import { SolutionNotFoundError } from '../error';
|
||||||
|
|
||||||
/**
|
export function add(domainId: string, pid: number, owner: number, content: string) {
|
||||||
* @param {string} pid
|
|
||||||
* @param {number} owner
|
|
||||||
* @param {string} content
|
|
||||||
*/
|
|
||||||
function add(domainId, pid, owner, content) {
|
|
||||||
return document.add(
|
return document.add(
|
||||||
domainId, content, owner, document.TYPE_PROBLEM_SOLUTION,
|
domainId, content, owner, document.TYPE_PROBLEM_SOLUTION,
|
||||||
null, document.TYPE_PROBLEM, pid, { reply: [], vote: 0 },
|
null, document.TYPE_PROBLEM, pid, { reply: [], vote: 0 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function get(domainId, psid) {
|
export async function get(domainId: string, psid: ObjectID) {
|
||||||
const psdoc = await document.get(domainId, document.TYPE_PROBLEM_SOLUTION, psid);
|
const psdoc = await document.get(domainId, document.TYPE_PROBLEM_SOLUTION, psid);
|
||||||
if (!psdoc) throw new SolutionNotFoundError();
|
if (!psdoc) throw new SolutionNotFoundError();
|
||||||
return psdoc;
|
return psdoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMany(domainId, query, sort, page, limit) {
|
export function getMany(domainId: string, query: any, sort: any, page: number, limit: number) {
|
||||||
return document.getMulti(domainId, document.TYPE_PROBLEM_SOLUTION, query)
|
return document.getMulti(domainId, document.TYPE_PROBLEM_SOLUTION, query)
|
||||||
.sort(sort)
|
.sort(sort)
|
||||||
.skip((page - 1) * limit).limit(limit)
|
.skip((page - 1) * limit).limit(limit)
|
||||||
.toArray();
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
function edit(domainId, psid, content) {
|
export function edit(domainId: string, psid: ObjectID, content: string) {
|
||||||
return document.set(domainId, document.TYPE_PROBLEM_SOLUTION, psid, { content });
|
return document.set(domainId, document.TYPE_PROBLEM_SOLUTION, psid, { content });
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(domainId, psid) {
|
export function del(domainId: string, psid: ObjectID) {
|
||||||
return document.deleteOne(domainId, document.TYPE_PROBLEM_SOLUTION, psid);
|
return document.deleteOne(domainId, document.TYPE_PROBLEM_SOLUTION, psid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(domainId, query) {
|
export function count(domainId: string, query: any) {
|
||||||
return document.count(domainId, document.TYPE_PROBLEM_SOLUTION, query);
|
return document.count(domainId, document.TYPE_PROBLEM_SOLUTION, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMulti(domainId, pid) {
|
export function getMulti(domainId: string, pid: number) {
|
||||||
return document.getMulti(
|
return document.getMulti(
|
||||||
domainId, document.TYPE_PROBLEM_SOLUTION,
|
domainId, document.TYPE_PROBLEM_SOLUTION,
|
||||||
{ parentType: document.TYPE_PROBLEM, parentId: pid },
|
{ parentType: document.TYPE_PROBLEM, parentId: pid },
|
||||||
).sort({ vote: -1 });
|
).sort({ vote: -1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function reply(domainId, psid, owner, content) {
|
export function reply(domainId: string, psid: ObjectID, owner: number, content: string) {
|
||||||
return document.push(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', content, owner);
|
return document.push(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', content, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReply(domainId, psid, psrid) {
|
export function getReply(domainId: string, psid: ObjectID, psrid: ObjectID) {
|
||||||
return document.getSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
|
return document.getSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editReply(domainId, psid, psrid, content) {
|
export function editReply(domainId: string, psid: ObjectID, psrid: ObjectID, content: string) {
|
||||||
return document.setSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid, { content });
|
return document.setSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid, { content });
|
||||||
}
|
}
|
||||||
|
|
||||||
function delReply(domainId, psid, psrid) {
|
export function delReply(domainId: string, psid: ObjectID, psrid: ObjectID) {
|
||||||
return document.deleteSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
|
return document.deleteSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function vote(domainId, psid, uid, value) {
|
export async function vote(domainId: string, psid: ObjectID, uid: number, value: number) {
|
||||||
let pssdoc = await document.getStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid);
|
let pssdoc = await document.getStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid);
|
||||||
await document.setStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid, { vote: value });
|
await document.setStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid, { vote: value });
|
||||||
if (pssdoc) value += -pssdoc.vote;
|
if (pssdoc) value += -pssdoc.vote;
|
||||||
@@ -70,16 +66,16 @@ async function vote(domainId, psid, uid, value) {
|
|||||||
return [psdoc, pssdoc];
|
return [psdoc, pssdoc];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getListStatus(domainId, list, uid) {
|
export async function getListStatus(domainId: string, psids: ObjectID[], uid: number) {
|
||||||
const result = {};
|
const result = {};
|
||||||
const res = await document.getMultiStatus(
|
const res = await document.getMultiStatus(
|
||||||
domainId, document.TYPE_PROBLEM_SOLUTION, { uid, psid: { $in: list } },
|
domainId, document.TYPE_PROBLEM_SOLUTION, { uid, psid: { $in: psids } },
|
||||||
).toArray();
|
).toArray();
|
||||||
for (const i of res) result[i.psid] = i;
|
for (const i of res) result[i.psid] = i;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.solution = module.exports = {
|
global.Hydro.model.solution = {
|
||||||
count,
|
count,
|
||||||
add,
|
add,
|
||||||
get,
|
get,
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
const db = require('../service/db');
|
import { FindOneAndUpdateOption } from 'mongodb';
|
||||||
|
import * as db from '../service/db';
|
||||||
|
|
||||||
const coll = db.collection('system');
|
const coll = db.collection('system');
|
||||||
|
|
||||||
async function get(_id) {
|
export async function get(_id: string) {
|
||||||
const doc = await coll.findOne({ _id });
|
const doc = await coll.findOne({ _id });
|
||||||
if (doc) return doc.value;
|
if (doc) return doc.value;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMany(keys) {
|
export async function getMany(keys: string[]) {
|
||||||
const docs = await coll.find({ _id: { $in: keys } }).toArray();
|
const docs = await coll.find({ _id: { $in: keys } }).toArray();
|
||||||
const dict = {};
|
const dict = {};
|
||||||
const res = [];
|
const res = [];
|
||||||
@@ -21,24 +22,21 @@ async function getMany(keys) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(_id, operation, config) {
|
export async function update(_id: string, operation: any, config: FindOneAndUpdateOption) {
|
||||||
await coll.findOneAndUpdate({ _id }, operation, config);
|
await coll.findOneAndUpdate({ _id }, operation, config);
|
||||||
return get(_id);
|
return get(_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function set(_id, value) {
|
export async function set(_id: string, value: any) {
|
||||||
await coll.findOneAndUpdate({ _id }, { $set: { value } }, { upsert: true });
|
await coll.findOneAndUpdate({ _id }, { $set: { value } }, { upsert: true });
|
||||||
return get(_id);
|
return get(_id);
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Increments the counter.
|
export function inc(field: string) {
|
||||||
* @returns {number} Integer value after increment.
|
|
||||||
*/
|
|
||||||
function inc(field) {
|
|
||||||
return update(field, { $inc: { value: 1 } }, { upsert: true });
|
return update(field, { $inc: { value: 1 } }, { upsert: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.system = module.exports = {
|
global.Hydro.model.system = {
|
||||||
get,
|
get,
|
||||||
getMany,
|
getMany,
|
||||||
update,
|
update,
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
const moment = require('moment-timezone');
|
import moment from 'moment-timezone';
|
||||||
const db = require('../service/db');
|
import { ObjectID } from 'mongodb';
|
||||||
|
import * as db from '../service/db';
|
||||||
|
|
||||||
const coll = db.collection('task');
|
const coll = db.collection('task');
|
||||||
|
|
||||||
async function add(task) {
|
export interface Task {
|
||||||
|
_id: ObjectID,
|
||||||
|
count: number,
|
||||||
|
executeAfter: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function add(task: any) {
|
||||||
const t = { ...task };
|
const t = { ...task };
|
||||||
if (typeof t.executeAfter === 'object') t.executeAfter = t.executeAfter.getTime();
|
if (typeof t.executeAfter === 'object') t.executeAfter = t.executeAfter.getTime();
|
||||||
t.count = t.count || 1;
|
t.count = t.count || 1;
|
||||||
@@ -12,19 +19,19 @@ async function add(task) {
|
|||||||
return res.insertedId;
|
return res.insertedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(_id) {
|
export function get(_id: ObjectID) {
|
||||||
return coll.findOne({ _id });
|
return coll.findOne({ _id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(query) {
|
export function count(query: any) {
|
||||||
return coll.find(query).count();
|
return coll.find(query).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(_id) {
|
export function del(_id: ObjectID) {
|
||||||
return coll.deleteOne({ _id });
|
return coll.deleteOne({ _id });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFirst(query) {
|
export async function getFirst(query: any) {
|
||||||
const q = { ...query };
|
const q = { ...query };
|
||||||
q.executeAfter = q.executeAfter || { $lt: new Date().getTime() };
|
q.executeAfter = q.executeAfter || { $lt: new Date().getTime() };
|
||||||
const res = await coll.find(q).sort('_id', 1).limit(1).toArray();
|
const res = await coll.find(q).sort('_id', 1).limit(1).toArray();
|
||||||
@@ -40,7 +47,7 @@ async function getFirst(query) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function consume(query, cb) {
|
export async function consume(query, cb: Function) {
|
||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
if (isRunning) return;
|
if (isRunning) return;
|
||||||
@@ -57,6 +64,6 @@ async function consume(query, cb) {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.task = module.exports = {
|
global.Hydro.model.task = {
|
||||||
add, get, del, count, getFirst, consume,
|
add, get, del, count, getFirst, consume,
|
||||||
};
|
};
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
const db = require('../service/db');
|
|
||||||
const { ValidationError } = require('../error');
|
|
||||||
|
|
||||||
const coll = db.collection('token');
|
|
||||||
|
|
||||||
function ensureIndexes() {
|
|
||||||
return Promise.all([
|
|
||||||
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
|
|
||||||
coll.createIndex('expireAt', { expireAfterSeconds: 0 }),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a token.
|
|
||||||
* @param {number} tokenType type of the token.
|
|
||||||
* @param {number} expireSeconds expire time, in seconds.
|
|
||||||
* @param {object} data extra data.
|
|
||||||
* @returns {Promise<Array>} token ID, token data
|
|
||||||
*/
|
|
||||||
async function add(tokenType, expireSeconds, data) {
|
|
||||||
const now = new Date();
|
|
||||||
const str = String.random(32);
|
|
||||||
const res = await coll.insertOne({
|
|
||||||
...data,
|
|
||||||
_id: str,
|
|
||||||
tokenType,
|
|
||||||
createAt: now,
|
|
||||||
updateAt: now,
|
|
||||||
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
|
||||||
});
|
|
||||||
return [str, res.ops[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a token.
|
|
||||||
* @param {string} tokenId token ID.
|
|
||||||
* @param {number} tokenType type of the token.
|
|
||||||
* @returns {Promise<object>} The token document, or null.
|
|
||||||
*/
|
|
||||||
async function get(tokenId, tokenType, doThrow = true) {
|
|
||||||
const res = await coll.findOne({ _id: tokenId, tokenType });
|
|
||||||
if (!res && doThrow) throw new ValidationError('token');
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a token.
|
|
||||||
* @param {string} tokenId token ID.
|
|
||||||
* @param {number} tokenType type of the token.
|
|
||||||
* @param {number} expireSeconds expire time, in seconds.
|
|
||||||
* @param {object} data extra data.
|
|
||||||
* @returns {Promise<object>} The token document, or null.
|
|
||||||
*/
|
|
||||||
async function update(tokenId, tokenType, expireSeconds, data) {
|
|
||||||
const now = new Date();
|
|
||||||
const res = await coll.findOneAndUpdate(
|
|
||||||
{ _id: tokenId, tokenType },
|
|
||||||
{
|
|
||||||
$set: {
|
|
||||||
...data,
|
|
||||||
updateAt: now,
|
|
||||||
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
|
||||||
tokenType,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ returnOriginal: false },
|
|
||||||
);
|
|
||||||
return res.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a token.
|
|
||||||
* @param {string} tokenId token ID.
|
|
||||||
* @param {int} tokenType type of the token.
|
|
||||||
* @returns {Promise<boolean>} true if deleted, or false.
|
|
||||||
*/
|
|
||||||
async function del(tokenId, tokenType) {
|
|
||||||
const result = await coll.deleteOne({ _id: tokenId, tokenType });
|
|
||||||
return !!result.deletedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a token(or create one)
|
|
||||||
* @param {number} tokenType type of the token.
|
|
||||||
* @param {number} expireSeconds expire time, in seconds.
|
|
||||||
* @param {object} data extra data.
|
|
||||||
* @returns {Array} token ID, token data
|
|
||||||
*/
|
|
||||||
async function createOrUpdate(tokenType, expireSeconds, data) {
|
|
||||||
const d = await coll.findOne({ tokenType, ...data });
|
|
||||||
if (!d) {
|
|
||||||
const res = await add(tokenType, expireSeconds, data);
|
|
||||||
return res[0];
|
|
||||||
}
|
|
||||||
await update(d._id, tokenType, expireSeconds, data);
|
|
||||||
return d._id;
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.model.token = module.exports = {
|
|
||||||
TYPE_SESSION: 0,
|
|
||||||
TYPE_CSRF_TOKEN: 1,
|
|
||||||
TYPE_REGISTER: 2,
|
|
||||||
TYPE_CHANGEMAIL: 3,
|
|
||||||
TYPE_OAUTH: 4,
|
|
||||||
|
|
||||||
ensureIndexes,
|
|
||||||
add,
|
|
||||||
createOrUpdate,
|
|
||||||
get,
|
|
||||||
update,
|
|
||||||
del,
|
|
||||||
delByUid(uid) {
|
|
||||||
return coll.deleteMany({ uid });
|
|
||||||
},
|
|
||||||
getMostRecentSessionByUid(uid) {
|
|
||||||
return coll.findOne({ uid, tokenType: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
|
|
||||||
},
|
|
||||||
getSessionListByUid(uid) {
|
|
||||||
return coll.find({ uid, tokenType: this.TYPE_SESSION }).sort('updateAt', -1).toArray();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { ValidationError } from '../error';
|
||||||
|
import * as db from '../service/db';
|
||||||
|
|
||||||
|
const coll = db.collection('token');
|
||||||
|
|
||||||
|
export const TYPE_SESSION = 0;
|
||||||
|
export const TYPE_CSRF_TOKEN = 1;
|
||||||
|
export const TYPE_REGISTRATION = 2;
|
||||||
|
export const TYPE_CHANGEMAIL = 3;
|
||||||
|
export const TYPE_OAUTH = 4;
|
||||||
|
export const TYPE_LOSTPASS = 5;
|
||||||
|
|
||||||
|
export function ensureIndexes() {
|
||||||
|
return Promise.all([
|
||||||
|
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
|
||||||
|
coll.createIndex('expireAt', { expireAfterSeconds: 0 }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function add(tokenType: number, expireSeconds: number, data: any) {
|
||||||
|
const now = new Date();
|
||||||
|
const str = String.random(32);
|
||||||
|
const res = await coll.insertOne({
|
||||||
|
...data,
|
||||||
|
_id: str,
|
||||||
|
tokenType,
|
||||||
|
createAt: now,
|
||||||
|
updateAt: now,
|
||||||
|
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
||||||
|
});
|
||||||
|
return [str, res.ops[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(tokenId: string, tokenType: number, doThrow = true) {
|
||||||
|
const res = await coll.findOne({ _id: tokenId, tokenType });
|
||||||
|
if (!res && doThrow) throw new ValidationError('token');
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(
|
||||||
|
tokenId: string, tokenType: number, expireSeconds: number,
|
||||||
|
data: object,
|
||||||
|
) {
|
||||||
|
const now = new Date();
|
||||||
|
const res = await coll.findOneAndUpdate(
|
||||||
|
{ _id: tokenId, tokenType },
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
...data,
|
||||||
|
updateAt: now,
|
||||||
|
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
||||||
|
tokenType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ returnOriginal: false },
|
||||||
|
);
|
||||||
|
return res.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function del(tokenId: string, tokenType: number) {
|
||||||
|
const result = await coll.deleteOne({ _id: tokenId, tokenType });
|
||||||
|
return !!result.deletedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createOrUpdate(tokenType: number, expireSeconds: number, data: object) {
|
||||||
|
const d = await coll.findOne({ tokenType, ...data });
|
||||||
|
if (!d) {
|
||||||
|
const res = await add(tokenType, expireSeconds, data);
|
||||||
|
return res[0];
|
||||||
|
}
|
||||||
|
await update(d._id, tokenType, expireSeconds, data);
|
||||||
|
return d._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSessionListByUid(uid: number) {
|
||||||
|
return coll.find({ uid, tokenType: this.TYPE_SESSION }).sort('updateAt', -1).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMostRecentSessionByUid(uid: number) {
|
||||||
|
return coll.findOne({ uid, tokenType: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delByUid(uid: number) {
|
||||||
|
return coll.deleteMany({ uid });
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.model.token = {
|
||||||
|
TYPE_SESSION,
|
||||||
|
TYPE_CHANGEMAIL,
|
||||||
|
TYPE_CSRF_TOKEN,
|
||||||
|
TYPE_OAUTH,
|
||||||
|
TYPE_REGISTRATION,
|
||||||
|
|
||||||
|
ensureIndexes,
|
||||||
|
add,
|
||||||
|
createOrUpdate,
|
||||||
|
get,
|
||||||
|
update,
|
||||||
|
del,
|
||||||
|
delByUid,
|
||||||
|
getMostRecentSessionByUid,
|
||||||
|
getSessionListByUid,
|
||||||
|
};
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
const document = require('./document');
|
|
||||||
const { TrainingNotFoundError, TrainingAlreadyEnrollError } = require('../error');
|
|
||||||
|
|
||||||
function getStatus(domainId, tid, uid) {
|
|
||||||
return document.getStatus(domainId, document.TYPE_TRAINING, tid, uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMultiStatus(domainId, query) {
|
|
||||||
return document.getMultiStatus(domainId, document.TYPE_TRAINING, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getListStatus(domainId, uid, tids) {
|
|
||||||
const tsdocs = await getMultiStatus(
|
|
||||||
domainId, { uid, tid: { $in: Array.from(new Set(tids)) } },
|
|
||||||
).toArray();
|
|
||||||
const r = {};
|
|
||||||
for (const tsdoc of tsdocs) r[tsdoc.pid] = tsdoc;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function enroll(domainId, tid, uid) {
|
|
||||||
try {
|
|
||||||
await document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, { enroll: 1 });
|
|
||||||
} catch (e) {
|
|
||||||
throw new TrainingAlreadyEnrollError(tid, uid);
|
|
||||||
}
|
|
||||||
return await document.inc(domainId, document.TYPE_TRAINING, tid, 'enroll', 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setStatus(domainId, tid, uid, $set) {
|
|
||||||
return document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, $set);
|
|
||||||
}
|
|
||||||
|
|
||||||
function add(domainId, title, content, owner, dag = [], description = '') {
|
|
||||||
return document.add(domainId, content, owner, document.TYPE_TRAINING, null, null, null, {
|
|
||||||
dag,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
enroll: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit(domainId, tid, $set) {
|
|
||||||
return document.set(domainId, document.TYPE_TRAINING, tid, $set);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.model.training = module.exports = {
|
|
||||||
getPids(tdoc) {
|
|
||||||
const pids = new Set();
|
|
||||||
for (const node of tdoc.dag) {
|
|
||||||
for (const pid of node.pids) pids.add(pid);
|
|
||||||
}
|
|
||||||
return Array.from(pids);
|
|
||||||
},
|
|
||||||
isDone(node, doneNids, donePids) {
|
|
||||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
|
||||||
&& Set.isSuperset(new Set(donePids), new Set(node.pids)));
|
|
||||||
},
|
|
||||||
isProgress(node, doneNids, donePids, progPids) {
|
|
||||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
|
||||||
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
|
||||||
&& Set.intersection(
|
|
||||||
Set.union(new Set(donePids), new Set(progPids)),
|
|
||||||
new Set(node.pids),
|
|
||||||
).size);
|
|
||||||
},
|
|
||||||
isOpen(node, doneNids, donePids, progPids) {
|
|
||||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
|
||||||
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
|
||||||
&& !Set.intersection(
|
|
||||||
Set.union(new Set(donePids), new Set(progPids)),
|
|
||||||
new Set(node.pids),
|
|
||||||
).size);
|
|
||||||
},
|
|
||||||
isInvalid(node, doneNids) {
|
|
||||||
return (!Set.isSuperset(new Set(doneNids), new Set(node.requireNids)));
|
|
||||||
},
|
|
||||||
add,
|
|
||||||
edit,
|
|
||||||
count: (domainId, query) => document.count(domainId, document.TYPE_TRAINING, query),
|
|
||||||
async get(domainId, tid) {
|
|
||||||
const tdoc = await document.get(domainId, document.TYPE_TRAINING, tid);
|
|
||||||
if (!tdoc) throw new TrainingNotFoundError(tid);
|
|
||||||
for (const i in tdoc.dag) {
|
|
||||||
for (const j in tdoc.dag[i].pids) {
|
|
||||||
if (Number.isSafeInteger(parseInt(tdoc.dag[i].pids[j], 10))) {
|
|
||||||
tdoc.dag[i].pids[j] = parseInt(tdoc.dag[i].pids[j], 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tdoc;
|
|
||||||
},
|
|
||||||
async getList(domainId, tids) {
|
|
||||||
const tdocs = await this.getMulti(
|
|
||||||
domainId, { _id: { $in: Array.from(new Set(tids)) } },
|
|
||||||
).toArray();
|
|
||||||
const r = {};
|
|
||||||
for (const tdoc of tdocs) r[tdoc.docId] = tdoc;
|
|
||||||
return r;
|
|
||||||
},
|
|
||||||
getMulti: (domainId, query) => document.getMulti(domainId, document.TYPE_TRAINING, query),
|
|
||||||
getMultiStatus,
|
|
||||||
getStatus,
|
|
||||||
enroll,
|
|
||||||
setStatus,
|
|
||||||
getListStatus,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { ObjectID } from 'mongodb';
|
||||||
|
import * as document from './document';
|
||||||
|
import { TrainingNotFoundError, TrainingAlreadyEnrollError } from '../error';
|
||||||
|
|
||||||
|
export function getStatus(domainId: string, tid: ObjectID, uid: number) {
|
||||||
|
return document.getStatus(domainId, document.TYPE_TRAINING, tid, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMultiStatus(domainId: string, query: any) {
|
||||||
|
return document.getMultiStatus(domainId, document.TYPE_TRAINING, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getListStatus(domainId: string, uid: number, tids: ObjectID[]) {
|
||||||
|
const tsdocs = await getMultiStatus(
|
||||||
|
domainId, { uid, tid: { $in: Array.from(new Set(tids)) } },
|
||||||
|
).toArray();
|
||||||
|
const r = {};
|
||||||
|
for (const tsdoc of tsdocs) r[tsdoc.pid] = tsdoc;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function enroll(domainId: string, tid: ObjectID, uid: number) {
|
||||||
|
try {
|
||||||
|
await document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, { enroll: 1 });
|
||||||
|
} catch (e) {
|
||||||
|
throw new TrainingAlreadyEnrollError(tid, uid);
|
||||||
|
}
|
||||||
|
return await document.inc(domainId, document.TYPE_TRAINING, tid, 'enroll', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setStatus(domainId: string, tid: ObjectID, uid: number, $set: any) {
|
||||||
|
return document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, $set);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function add(
|
||||||
|
domainId: string, title: string, content: string,
|
||||||
|
owner: number, dag = [], description = '',
|
||||||
|
) {
|
||||||
|
return document.add(domainId, content, owner, document.TYPE_TRAINING, null, null, null, {
|
||||||
|
dag,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
enroll: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function edit(domainId: string, tid: ObjectID, $set) {
|
||||||
|
return document.set(domainId, document.TYPE_TRAINING, tid, $set);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPids(tdoc): number[] {
|
||||||
|
const pids: Set<number> = new Set();
|
||||||
|
for (const node of tdoc.dag) {
|
||||||
|
for (const pid of node.pids) pids.add(pid);
|
||||||
|
}
|
||||||
|
return Array.from(pids);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDone(node, doneNids, donePids) {
|
||||||
|
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||||
|
&& Set.isSuperset(new Set(donePids), new Set(node.pids)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isProgress(node, doneNids, donePids, progPids) {
|
||||||
|
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||||
|
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
||||||
|
&& Set.intersection(
|
||||||
|
Set.union(new Set(donePids), new Set(progPids)),
|
||||||
|
new Set(node.pids),
|
||||||
|
).size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isOpen(node, doneNids, donePids, progPids) {
|
||||||
|
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||||
|
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
||||||
|
&& !Set.intersection(
|
||||||
|
Set.union(new Set(donePids), new Set(progPids)),
|
||||||
|
new Set(node.pids),
|
||||||
|
).size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isInvalid = (node, doneNids) =>
|
||||||
|
!Set.isSuperset(new Set(doneNids), new Set(node.requireNids));
|
||||||
|
|
||||||
|
export const count = (domainId: string, query: any) =>
|
||||||
|
document.count(domainId, document.TYPE_TRAINING, query);
|
||||||
|
|
||||||
|
export async function get(domainId, tid) {
|
||||||
|
const tdoc = await document.get(domainId, document.TYPE_TRAINING, tid);
|
||||||
|
if (!tdoc) throw new TrainingNotFoundError(tid);
|
||||||
|
for (const i in tdoc.dag) {
|
||||||
|
for (const j in tdoc.dag[i].pids) {
|
||||||
|
if (Number.isSafeInteger(parseInt(tdoc.dag[i].pids[j], 10))) {
|
||||||
|
tdoc.dag[i].pids[j] = parseInt(tdoc.dag[i].pids[j], 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tdoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getList(domainId: string, tids: ObjectID[]) {
|
||||||
|
const tdocs = await this.getMulti(
|
||||||
|
domainId, { _id: { $in: Array.from(new Set(tids)) } },
|
||||||
|
).toArray();
|
||||||
|
const r = {};
|
||||||
|
for (const tdoc of tdocs) r[tdoc.docId] = tdoc;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMulti = (domainId: string, query: any = {}) =>
|
||||||
|
document.getMulti(domainId, document.TYPE_TRAINING, query);
|
||||||
|
|
||||||
|
global.Hydro.model.training = {
|
||||||
|
getPids,
|
||||||
|
isDone,
|
||||||
|
isProgress,
|
||||||
|
isOpen,
|
||||||
|
isInvalid,
|
||||||
|
add,
|
||||||
|
edit,
|
||||||
|
count,
|
||||||
|
get,
|
||||||
|
getList,
|
||||||
|
getMulti,
|
||||||
|
getMultiStatus,
|
||||||
|
getStatus,
|
||||||
|
enroll,
|
||||||
|
setStatus,
|
||||||
|
getListStatus,
|
||||||
|
};
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
const { BUILTIN_ROLES, BUILTIN_USERS, PRIV } = require('./builtin');
|
import * as document from './document';
|
||||||
const document = require('./document');
|
import * as system from './system';
|
||||||
const system = require('./system');
|
import * as token from './token';
|
||||||
const token = require('./token');
|
import * as setting from './setting';
|
||||||
const setting = require('./setting');
|
import { BUILTIN_ROLES, BUILTIN_USERS, PRIV } from './builtin';
|
||||||
const { UserNotFoundError, UserAlreadyExistError, LoginError } = require('../error');
|
import { UserNotFoundError, UserAlreadyExistError, LoginError } from '../error';
|
||||||
const pwhash = require('../lib/hash.hydro');
|
import pwhash from '../lib/hash.hydro';
|
||||||
const db = require('../service/db');
|
import * as db from '../service/db';
|
||||||
|
|
||||||
const coll = db.collection('user');
|
const coll = db.collection('user');
|
||||||
|
|
||||||
function setPassword(uid, password) {
|
export function setPassword(uid: number, password: string) {
|
||||||
const salt = String.random();
|
const salt = String.random();
|
||||||
return coll.findOneAndUpdate(
|
return coll.findOneAndUpdate(
|
||||||
{ _id: uid },
|
{ _id: uid },
|
||||||
@@ -17,7 +17,33 @@ function setPassword(uid, password) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class USER {
|
export class User {
|
||||||
|
udoc: () => any;
|
||||||
|
|
||||||
|
dudoc: () => any;
|
||||||
|
|
||||||
|
_id: number;
|
||||||
|
|
||||||
|
mail: string;
|
||||||
|
|
||||||
|
uname: string;
|
||||||
|
|
||||||
|
salt: () => string;
|
||||||
|
|
||||||
|
hash: () => string;
|
||||||
|
|
||||||
|
hashType: string;
|
||||||
|
|
||||||
|
priv: number;
|
||||||
|
|
||||||
|
regat: Date;
|
||||||
|
|
||||||
|
loginat: Date;
|
||||||
|
|
||||||
|
perm: string;
|
||||||
|
|
||||||
|
role: string;
|
||||||
|
|
||||||
constructor(udoc, dudoc) {
|
constructor(udoc, dudoc) {
|
||||||
this.udoc = () => udoc;
|
this.udoc = () => udoc;
|
||||||
this.dudoc = () => dudoc;
|
this.dudoc = () => dudoc;
|
||||||
@@ -48,15 +74,15 @@ class USER {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPerm(p) {
|
hasPerm(p: string) {
|
||||||
return this.perm.includes(p);
|
return this.perm.includes(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPriv(p) {
|
hasPriv(p: number) {
|
||||||
return this.priv & p;
|
return this.priv & p;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPassword(password) {
|
checkPassword(password: string) {
|
||||||
const h = global.Hydro.lib[`hash.${this.hashType}`];
|
const h = global.Hydro.lib[`hash.${this.hashType}`];
|
||||||
if (!h) throw new Error('Unknown hash method');
|
if (!h) throw new Error('Unknown hash method');
|
||||||
if (!(h(password, this.salt(), this) === this.hash())) {
|
if (!(h(password, this.salt(), this) === this.hash())) {
|
||||||
@@ -67,7 +93,7 @@ class USER {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getInDomain(domainId, udoc) {
|
export async function getInDomain(domainId: string, udoc: User) {
|
||||||
let dudoc = await document.getStatus(domainId, document.TYPE_DOMAIN_USER, 0, udoc._id);
|
let dudoc = await document.getStatus(domainId, document.TYPE_DOMAIN_USER, 0, udoc._id);
|
||||||
dudoc = dudoc || {};
|
dudoc = dudoc || {};
|
||||||
if (udoc._id === 1) dudoc.role = 'guest';
|
if (udoc._id === 1) dudoc.role = 'guest';
|
||||||
@@ -77,7 +103,7 @@ async function getInDomain(domainId, udoc) {
|
|||||||
return dudoc;
|
return dudoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getById(domainId, _id, throwError = false) {
|
export async function getById(domainId: string, _id: number, throwError = false) {
|
||||||
const udoc = _id === 0 || _id === 1
|
const udoc = _id === 0 || _id === 1
|
||||||
? BUILTIN_USERS[_id]
|
? BUILTIN_USERS[_id]
|
||||||
: await coll.findOne({ _id });
|
: await coll.findOne({ _id });
|
||||||
@@ -86,18 +112,18 @@ async function getById(domainId, _id, throwError = false) {
|
|||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
const dudoc = await getInDomain(domainId, udoc);
|
const dudoc = await getInDomain(domainId, udoc);
|
||||||
return new USER(udoc, dudoc);
|
return new User(udoc, dudoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getList(domainId, uids) {
|
export async function getList(domainId: string, uids: number[]) {
|
||||||
uids = new Set(uids);
|
const _uids = new Set(uids);
|
||||||
const r = {};
|
const r = {};
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
for (const uid of uids) r[uid] = await getById(domainId, uid);
|
for (const uid of _uids) r[uid] = await getById(domainId, uid);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getByUname(domainId, uname, ignoreMissing = false) {
|
export async function getByUname(domainId: string, uname: string, ignoreMissing = false) {
|
||||||
const unameLower = uname.trim().toLowerCase();
|
const unameLower = uname.trim().toLowerCase();
|
||||||
const udoc = (unameLower === 'guest')
|
const udoc = (unameLower === 'guest')
|
||||||
? BUILTIN_USERS[0]
|
? BUILTIN_USERS[0]
|
||||||
@@ -109,10 +135,10 @@ async function getByUname(domainId, uname, ignoreMissing = false) {
|
|||||||
throw new UserNotFoundError(uname);
|
throw new UserNotFoundError(uname);
|
||||||
}
|
}
|
||||||
const dudoc = await getInDomain(domainId, udoc);
|
const dudoc = await getInDomain(domainId, udoc);
|
||||||
return new USER(udoc, dudoc);
|
return new User(udoc, dudoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getByEmail(domainId, mail, ignoreMissing = false) {
|
export async function getByEmail(domainId: string, mail: string, ignoreMissing = false) {
|
||||||
const mailLower = mail.trim().toLowerCase();
|
const mailLower = mail.trim().toLowerCase();
|
||||||
const udoc = (mailLower === 'guest@hydro.local')
|
const udoc = (mailLower === 'guest@hydro.local')
|
||||||
? BUILTIN_USERS[0]
|
? BUILTIN_USERS[0]
|
||||||
@@ -124,21 +150,21 @@ async function getByEmail(domainId, mail, ignoreMissing = false) {
|
|||||||
throw new UserNotFoundError(mail);
|
throw new UserNotFoundError(mail);
|
||||||
}
|
}
|
||||||
const dudoc = await getInDomain(domainId, udoc);
|
const dudoc = await getInDomain(domainId, udoc);
|
||||||
return new USER(udoc, dudoc);
|
return new User(udoc, dudoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setById(uid, $set, $unset) {
|
export function setById(uid: number, $set = {}, $unset = {}) {
|
||||||
const op = {};
|
const op: any = {};
|
||||||
if ($set && Object.keys($set).length) op.$set = $set;
|
if ($set && Object.keys($set).length) op.$set = $set;
|
||||||
if ($unset && Object.keys($unset).length) op.$unset = $unset;
|
if ($unset && Object.keys($unset).length) op.$unset = $unset;
|
||||||
return coll.findOneAndUpdate({ _id: uid }, op);
|
return coll.findOneAndUpdate({ _id: uid }, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setEmail(uid, mail) {
|
export function setEmail(uid: number, mail: string) {
|
||||||
return setById(uid, { mail, mailLower: mail.trim().toLowerCase() });
|
return setById(uid, { mail, mailLower: mail.trim().toLowerCase() });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changePassword(uid, currentPassword, newPassword) {
|
export async function changePassword(uid: number, currentPassword: string, newPassword: string) {
|
||||||
const udoc = await getById('system', uid);
|
const udoc = await getById('system', uid);
|
||||||
udoc.checkPassword(currentPassword);
|
udoc.checkPassword(currentPassword);
|
||||||
const salt = String.random();
|
const salt = String.random();
|
||||||
@@ -148,7 +174,7 @@ async function changePassword(uid, currentPassword, newPassword) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function inc(_id, field, n = 1) {
|
export async function inc(_id: number, field: string, n = 1) {
|
||||||
const udoc = await coll.findOne({ _id });
|
const udoc = await coll.findOne({ _id });
|
||||||
if (!udoc) throw new UserNotFoundError(_id);
|
if (!udoc) throw new UserNotFoundError(_id);
|
||||||
udoc[field] = udoc[field] + n || n;
|
udoc[field] = udoc[field] + n || n;
|
||||||
@@ -156,22 +182,20 @@ async function inc(_id, field, n = 1) {
|
|||||||
return udoc;
|
return udoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInDomain(domainId, uid, params) {
|
export function setInDomain(domainId: string, uid: number, params: any) {
|
||||||
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, params);
|
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function incDomain(domainId, uid, field, n = 1) {
|
export async function incDomain(domainId: string, uid: number, field: string, n = 1) {
|
||||||
|
// @ts-ignore
|
||||||
const dudoc = await getInDomain(domainId, { _id: uid });
|
const dudoc = await getInDomain(domainId, { _id: uid });
|
||||||
dudoc[field] = dudoc[field] + n || n;
|
dudoc[field] = dudoc[field] + n || n;
|
||||||
await setInDomain(domainId, uid, { [field]: dudoc[field] });
|
await setInDomain(domainId, uid, { [field]: dudoc[field] });
|
||||||
return dudoc;
|
return dudoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function create({
|
||||||
* @returns {Promise<number>} uid
|
uid = null, mail, uname, password, regip = '127.0.0.1', priv = PRIV.PRIV_DEFAULT,
|
||||||
*/
|
|
||||||
async function create({
|
|
||||||
uid, mail, uname, password, regip = '127.0.0.1', priv = PRIV.PRIV_DEFAULT,
|
|
||||||
}) {
|
}) {
|
||||||
const salt = String.random();
|
const salt = String.random();
|
||||||
if (!uid) uid = await system.inc('user');
|
if (!uid) uid = await system.inc('user');
|
||||||
@@ -198,30 +222,30 @@ async function create({
|
|||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMulti(params) {
|
export function getMulti(params) {
|
||||||
return coll.find(params);
|
return coll.find(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMultiInDomain(domainId, query, params) {
|
export function setMultiInDomain(domainId: string, query: any, params: any) {
|
||||||
return document.setMultiStatus(domainId, document.TYPE_DOMAIN_USER, query, params);
|
return document.setMultiStatus(domainId, document.TYPE_DOMAIN_USER, query, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPrefixList(prefix, limit = 50) {
|
export async function getPrefixList(prefix: string, limit = 50) {
|
||||||
prefix = prefix.toLowerCase();
|
prefix = prefix.toLowerCase();
|
||||||
const $regex = new RegExp(`\\A\\Q${prefix.replace(/\\E/gmi, /\\E\\E\\Q/gmi)}\\E`, 'gmi');
|
const $regex = new RegExp(`\\A\\Q${prefix}\\E`, 'gmi');
|
||||||
const udocs = await coll.find({ unameLower: { $regex } }).limit(limit).toArray();
|
const udocs = await coll.find({ unameLower: { $regex } }).limit(limit).toArray();
|
||||||
return udocs;
|
return udocs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPriv(uid, priv) {
|
export function setPriv(uid: number, priv: number) {
|
||||||
return coll.findOneAndUpdate({ _id: uid }, { $set: priv }, { returnOriginal: false });
|
return coll.findOneAndUpdate({ _id: uid }, { $set: priv }, { returnOriginal: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRole(domainId, uid, role) {
|
export function setRole(domainId: string, uid: number, role: string) {
|
||||||
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, { role });
|
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, { role });
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRoles(domainId, roles) {
|
export function setRoles(domainId: string, roles: any) {
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
for (const role in roles) {
|
for (const role in roles) {
|
||||||
tasks.push(document.set(
|
tasks.push(document.set(
|
||||||
@@ -231,7 +255,7 @@ function setRoles(domainId, roles) {
|
|||||||
return Promise.all(tasks);
|
return Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getRoles(domainId) {
|
export async function getRoles(domainId: string) {
|
||||||
const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_USER).sort('_id', 1).toArray();
|
const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_USER).sort('_id', 1).toArray();
|
||||||
const roles = [];
|
const roles = [];
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
@@ -240,40 +264,40 @@ async function getRoles(domainId) {
|
|||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRole(domainId, name) {
|
export function getRole(domainId: string, name: string) {
|
||||||
return document.get(domainId, document.TYPE_DOMAIN_USER, name);
|
return document.get(domainId, document.TYPE_DOMAIN_USER, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMultiInDomain(domainId, query) {
|
export function getMultiInDomain(domainId: string, query: any = {}) {
|
||||||
return document.getMultiStatus(domainId, document.TYPE_DOMAIN_USER, query);
|
return document.getMultiStatus(domainId, document.TYPE_DOMAIN_USER, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRole(domainId, name, permission) {
|
export function addRole(domainId: string, name: string, permission: string) {
|
||||||
return document.add(domainId, permission, 1, document.TYPE_DOMAIN_USER, name);
|
return document.add(domainId, permission, 1, document.TYPE_DOMAIN_USER, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteRoles(domainId, roles) {
|
export function deleteRoles(domainId: string, roles) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
document.deleteMulti(domainId, document.TYPE_DOMAIN_USER, { docId: { $in: roles } }),
|
document.deleteMulti(domainId, document.TYPE_DOMAIN_USER, { docId: { $in: roles } }),
|
||||||
document.deleteMultiStatus(domainId, document.TYPE_DOMAIN_USER, { role: { $in: roles } }),
|
document.deleteMultiStatus(domainId, document.TYPE_DOMAIN_USER, { role: { $in: roles } }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ban(uid) {
|
export function ban(uid: number) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
setPriv(uid, PRIV.PRIV_NONE),
|
setPriv(uid, PRIV.PRIV_NONE),
|
||||||
token.delByUid(uid),
|
token.delByUid(uid),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureIndexes() {
|
export function ensureIndexes() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
coll.createIndex('unameLower', { unique: true }),
|
coll.createIndex('unameLower', { unique: true }),
|
||||||
coll.createIndex('mailLower', { sparse: true }),
|
coll.createIndex('mailLower', { sparse: true }),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.model.user = module.exports = {
|
global.Hydro.model.user = {
|
||||||
changePassword,
|
changePassword,
|
||||||
create,
|
create,
|
||||||
getByEmail,
|
getByEmail,
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const { defaults } = require('lodash');
|
import { defaults } from 'lodash';
|
||||||
|
|
||||||
let options = {
|
let f = path.resolve(process.cwd(), 'config.json');
|
||||||
|
if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.config', 'hydro', 'config.json');
|
||||||
|
if (!fs.existsSync(f)) f = path.resolve('/config/config.json');
|
||||||
|
const t = JSON.parse(fs.readFileSync(f).toString());
|
||||||
|
|
||||||
|
export default defaults(t, {
|
||||||
name: 'hydro',
|
name: 'hydro',
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: 27017,
|
port: 27017,
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
};
|
});
|
||||||
|
|
||||||
let f = path.resolve(process.cwd(), 'config.json');
|
|
||||||
if (!fs.existsSync(f)) f = path.resolve(os.homedir(), '.config', 'hydro', 'config.json');
|
|
||||||
if (!fs.existsSync(f)) f = path.resolve('/config/config.json');
|
|
||||||
const t = JSON.parse(fs.readFileSync(f));
|
|
||||||
options = defaults(t, options);
|
|
||||||
|
|
||||||
module.exports = options;
|
|
||||||
@@ -7,7 +7,7 @@ const document = require('../model/document');
|
|||||||
const discussion = require('../model/discussion');
|
const discussion = require('../model/discussion');
|
||||||
const user = require('../model/user');
|
const user = require('../model/user');
|
||||||
const blacklist = require('../model/blacklist');
|
const blacklist = require('../model/blacklist');
|
||||||
const db = require('../service/db.js');
|
const db = require('../service/db');
|
||||||
|
|
||||||
async function _address(ip, bset, uset, dset, dryrun, report) {
|
async function _address(ip, bset, uset, dset, dryrun, report) {
|
||||||
if (bset.has(ip))
|
if (bset.has(ip))
|
||||||
|
|||||||
+23
-24
@@ -1,14 +1,10 @@
|
|||||||
const description = 'Install';
|
const description = 'Install';
|
||||||
|
|
||||||
const { defaults } = require('lodash');
|
|
||||||
const db = require('../service/db');
|
|
||||||
const { PRIV_ALL } = require('../model/builtin').PRIV;
|
const { PRIV_ALL } = require('../model/builtin').PRIV;
|
||||||
const system = require('../model/system');
|
const system = require('../model/system');
|
||||||
|
const user = require('../model/user');
|
||||||
const domain = require('../model/domain');
|
const domain = require('../model/domain');
|
||||||
const pwhash = require('../lib/hash.hydro');
|
const pwhash = require('../lib/hash.hydro');
|
||||||
const { udoc } = require('../interface');
|
|
||||||
|
|
||||||
const collUser = db.collection('user');
|
|
||||||
|
|
||||||
async function run({ username, password } = {}) {
|
async function run({ username, password } = {}) {
|
||||||
const def = {
|
const def = {
|
||||||
@@ -38,7 +34,7 @@ async function run({ username, password } = {}) {
|
|||||||
changemail_token_expire_seconds: 3600 * 24,
|
changemail_token_expire_seconds: 3600 * 24,
|
||||||
registration_token_expire_seconds: 600,
|
registration_token_expire_seconds: 600,
|
||||||
};
|
};
|
||||||
let tasks = [];
|
const tasks = [];
|
||||||
for (const key in def) {
|
for (const key in def) {
|
||||||
tasks.push(system.get(key).then((value) => {
|
tasks.push(system.get(key).then((value) => {
|
||||||
if (!value) return system.set(key, def[key]);
|
if (!value) return system.set(key, def[key]);
|
||||||
@@ -46,26 +42,29 @@ async function run({ username, password } = {}) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
tasks = [domain.add('system', 0, 'Hydro', true)];
|
await domain.add('system', 0, 'Hydro', true);
|
||||||
if (username && password) {
|
if (username && password) {
|
||||||
const salt = String.random();
|
const udoc = await user.getById('system', -1, false);
|
||||||
tasks.push(
|
if (!udoc) {
|
||||||
collUser.updateOne({ _id: -1 }, {
|
await user.create({
|
||||||
$set: defaults({
|
uid: -1,
|
||||||
_id: -1,
|
mail: 'root@hydro.local',
|
||||||
mail: 'root@hydro',
|
uname: username,
|
||||||
mailLower: 'root@hydro',
|
password,
|
||||||
uname: username,
|
regip: '127.0.0.1',
|
||||||
unameLower: username.trim().toLowerCase(),
|
priv: PRIV_ALL,
|
||||||
hash: pwhash(password, salt),
|
});
|
||||||
salt,
|
} else {
|
||||||
gravatar: 'root@hydro',
|
const salt = String.random();
|
||||||
priv: PRIV_ALL,
|
await user.setById(-1, {
|
||||||
}, udoc),
|
uname: username,
|
||||||
}, { upsert: true }),
|
unameLower: username.trim().toLowerCase(),
|
||||||
);
|
salt,
|
||||||
|
hash: pwhash.default(password, salt),
|
||||||
|
hashType: 'hydro',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await Promise.all(tasks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.script.install = module.exports = { run, description };
|
global.Hydro.script.install = module.exports = { run, description };
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
const { EventEmitter } = require('events');
|
import { EventEmitter } from 'events';
|
||||||
const cluster = require('cluster');
|
import cluster from 'cluster';
|
||||||
|
|
||||||
const bus = new EventEmitter();
|
const bus = new EventEmitter();
|
||||||
|
|
||||||
function subscribe(events, handler, funcName) {
|
export function subscribe(events, handler, funcName) {
|
||||||
if (!funcName) {
|
if (!funcName) {
|
||||||
for (const event of events) bus.on(event, handler);
|
for (const event of events) bus.on(event, handler);
|
||||||
} else {
|
} else {
|
||||||
@@ -14,7 +14,7 @@ function subscribe(events, handler, funcName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function unsubscribe(events, handler, funcName) {
|
export function unsubscribe(events, handler, funcName) {
|
||||||
if (!funcName) {
|
if (!funcName) {
|
||||||
for (const event of events) bus.off(event, handler);
|
for (const event of events) bus.off(event, handler);
|
||||||
} else {
|
} else {
|
||||||
@@ -27,7 +27,7 @@ function unsubscribe(events, handler, funcName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function publish(event, payload, isMaster = true) {
|
export function publish(event, payload, isMaster = true) {
|
||||||
// Process forked by pm2 would also have process.send
|
// Process forked by pm2 would also have process.send
|
||||||
if (isMaster && process.send && !cluster.isMaster) {
|
if (isMaster && process.send && !cluster.isMaster) {
|
||||||
process.send({
|
process.send({
|
||||||
@@ -40,6 +40,8 @@ function publish(event, payload, isMaster = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.service.bus = module.exports = {
|
global.Hydro.service.bus = {
|
||||||
subscribe, unsubscribe, publish,
|
subscribe, unsubscribe, publish,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default global.Hydro.service.bus;
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
const Mongo = require('mongodb');
|
|
||||||
const bus = require('./bus');
|
|
||||||
const options = require('../options');
|
|
||||||
|
|
||||||
let mongourl = 'mongodb://';
|
|
||||||
if (options.username) mongourl += `${options.username}:${options.password}@`;
|
|
||||||
mongourl += `${options.host}:${options.port}/${options.name}`;
|
|
||||||
/**
|
|
||||||
* @type {import('mongodb').Db}
|
|
||||||
*/
|
|
||||||
let db = null;
|
|
||||||
Mongo.MongoClient.connect(mongourl, { useNewUrlParser: true, useUnifiedTopology: true })
|
|
||||||
.then((Client) => {
|
|
||||||
db = Client.db(options.name);
|
|
||||||
db.Client = Client;
|
|
||||||
bus.publish('system_database_connected', null);
|
|
||||||
});
|
|
||||||
|
|
||||||
global.Hydro.service.db = module.exports = {
|
|
||||||
collection: (c) => db.collection(c),
|
|
||||||
dropDatabase: () => db.dropDatabase(),
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import mongodb from 'mongodb';
|
||||||
|
import * as bus from './bus';
|
||||||
|
import options from '../options';
|
||||||
|
|
||||||
|
let mongourl = 'mongodb://';
|
||||||
|
if (options.username) mongourl += `${options.username}:${options.password}@`;
|
||||||
|
mongourl += `${options.host}:${options.port}/${options.name}`;
|
||||||
|
|
||||||
|
export let db: mongodb.Db = null;
|
||||||
|
|
||||||
|
mongodb.MongoClient.connect(mongourl, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||||
|
.then((Client) => {
|
||||||
|
db = Client.db(options.name);
|
||||||
|
bus.publish('system_database_connected', null);
|
||||||
|
});
|
||||||
|
|
||||||
|
export function collection(c: string) {
|
||||||
|
return db.collection(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.service.db = { collection };
|
||||||
|
|
||||||
|
export default { collection };
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
const { GridFSBucket } = require('mongodb');
|
|
||||||
const db = require('./db');
|
|
||||||
|
|
||||||
if (!db.s) db.s = {};
|
|
||||||
|
|
||||||
global.Hydro.service.gridfs = module.exports = new GridFSBucket(db);
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { GridFSBucket } from 'mongodb';
|
||||||
|
import { db } from './db';
|
||||||
|
|
||||||
|
const exp = new GridFSBucket(db);
|
||||||
|
|
||||||
|
global.Hydro.service.gridfs = exp;
|
||||||
|
|
||||||
|
export default exp;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const db = require('./db');
|
import * as db from './db';
|
||||||
const sysinfo = require('../lib/sysinfo');
|
import * as sysinfo from '../lib/sysinfo';
|
||||||
|
|
||||||
const coll = db.collection('status');
|
const coll = db.collection('status');
|
||||||
|
|
||||||
@@ -23,4 +23,6 @@ async function postInit() {
|
|||||||
setInterval(update, 60 * 1000);
|
setInterval(update, 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.service.monitor = module.exports = { postInit };
|
global.Hydro.service.monitor = { postInit };
|
||||||
|
|
||||||
|
export default { postInit };
|
||||||
@@ -1,24 +1,26 @@
|
|||||||
const assert = require('assert');
|
import assert from 'assert';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const os = require('os');
|
import os from 'os';
|
||||||
const _ = require('lodash');
|
import http from 'http';
|
||||||
const { ObjectID } = require('mongodb');
|
import _ from 'lodash';
|
||||||
const Koa = require('koa');
|
import { ObjectID } from 'mongodb';
|
||||||
const yaml = require('js-yaml');
|
import Koa from 'koa';
|
||||||
const morgan = require('koa-morgan');
|
import yaml from 'js-yaml';
|
||||||
const Body = require('koa-body');
|
import morgan from 'koa-morgan';
|
||||||
const Router = require('koa-router');
|
import Body from 'koa-body';
|
||||||
const cache = require('koa-static-cache');
|
import Router from 'koa-router';
|
||||||
const sockjs = require('sockjs');
|
import cache from 'koa-static-cache';
|
||||||
const http = require('http');
|
import sockjs from 'sockjs';
|
||||||
const validator = require('../lib/validator');
|
|
||||||
const template = require('../lib/template');
|
import { render } from '../lib/template';
|
||||||
const misc = require('../lib/misc');
|
import * as validator from '../lib/validator';
|
||||||
const user = require('../model/user');
|
import * as misc from '../lib/misc';
|
||||||
const system = require('../model/system');
|
import * as user from '../model/user';
|
||||||
const blacklist = require('../model/blacklist');
|
import * as system from '../model/system';
|
||||||
const token = require('../model/token');
|
import * as blacklist from '../model/blacklist';
|
||||||
const opcount = require('../model/opcount');
|
import * as token from '../model/token';
|
||||||
|
import * as opcount from '../model/opcount';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
UserNotFoundError, BlacklistedError, PermissionError,
|
UserNotFoundError, BlacklistedError, PermissionError,
|
||||||
UserFacingError, ValidationError, PrivilegeError,
|
UserFacingError, ValidationError, PrivilegeError,
|
||||||
@@ -31,21 +33,21 @@ const app = new Koa();
|
|||||||
let server;
|
let server;
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
const _validateObjectId = (id, key) => {
|
const _validateObjectId = (id: string | ObjectID, key: string) => {
|
||||||
if (ObjectID.isValid(id)) return new ObjectID(id);
|
if (ObjectID.isValid(id)) return new ObjectID(id);
|
||||||
throw new ValidationError(key);
|
throw new ValidationError(key);
|
||||||
};
|
};
|
||||||
const _bool = (val) => !!val;
|
const _bool = (val: any) => !!val;
|
||||||
const _splitAndTrim = (val) => {
|
const _splitAndTrim = (val: string) => {
|
||||||
const t = val.split(',').map((i) => i.trim().split('+'));
|
const t = val.split(',').map((i) => i.trim().split('+'));
|
||||||
return _.flatten(t);
|
return _.flatten(t);
|
||||||
};
|
};
|
||||||
const _date = (val) => {
|
const _date = (val: string) => {
|
||||||
const d = val.split('-');
|
const d = val.split('-');
|
||||||
assert(d.length === 3);
|
assert(d.length === 3);
|
||||||
return `${d[0]}-${d[1].length === 1 ? '0' : ''}${d[1]}-${d[2].length === 1 ? '0' : ''}${d[2]}`;
|
return `${d[0]}-${d[1].length === 1 ? '0' : ''}${d[1]}-${d[2].length === 1 ? '0' : ''}${d[2]}`;
|
||||||
};
|
};
|
||||||
const _time = (val) => {
|
const _time = (val: string) => {
|
||||||
const t = val.split(':');
|
const t = val.split(':');
|
||||||
assert(t.length === 2);
|
assert(t.length === 2);
|
||||||
return `${(t[0].length === 1 ? '0' : '') + t[0]}:${t[1].length === 1 ? '0' : ''}${t[1]}`;
|
return `${(t[0].length === 1 ? '0' : '') + t[0]}:${t[1].length === 1 ? '0' : ''}${t[1]}`;
|
||||||
@@ -67,29 +69,32 @@ const validate = {
|
|||||||
tag: _splitAndTrim,
|
tag: _splitAndTrim,
|
||||||
beginAtDate: _date,
|
beginAtDate: _date,
|
||||||
beginAtTime: _time,
|
beginAtTime: _time,
|
||||||
pid: (pid) => (Number.isSafeInteger(parseInt(pid, 10)) ? parseInt(pid, 10) : pid),
|
pid: (pid: string) => (Number.isSafeInteger(parseInt(pid, 10)) ? parseInt(pid, 10) : pid),
|
||||||
content: validator.checkContent,
|
content: validator.checkContent,
|
||||||
title: validator.checkTitle,
|
title: validator.checkTitle,
|
||||||
uid: (uid) => parseInt(validator.checkUid(uid), 10),
|
uid: (uid: string) => parseInt(validator.checkUid(uid), 10),
|
||||||
password: validator.checkPassword,
|
password: validator.checkPassword,
|
||||||
mail: validator.checkEmail,
|
mail: validator.checkEmail,
|
||||||
uname: validator.checkUname,
|
uname: validator.checkUname,
|
||||||
page: (page) => {
|
page: (_page: string) => {
|
||||||
if (Number.isSafeInteger(parseInt(page, 10))) page = parseInt(page, 10);
|
let page: number;
|
||||||
|
if (Number.isSafeInteger(parseInt(_page, 10))) page = parseInt(_page, 10);
|
||||||
if (page <= 0) throw new ValidationError('page');
|
if (page <= 0) throw new ValidationError('page');
|
||||||
return page;
|
return page;
|
||||||
},
|
},
|
||||||
duration: (duration) => {
|
duration: (_duration: string) => {
|
||||||
if (!Number.isNaN(parseFloat(duration))) duration = parseFloat(duration);
|
let duration: number;
|
||||||
|
if (!Number.isNaN(parseFloat(_duration))) duration = parseFloat(_duration);
|
||||||
if (duration <= 0) throw new ValidationError('duration');
|
if (duration <= 0) throw new ValidationError('duration');
|
||||||
return duration;
|
return duration;
|
||||||
},
|
},
|
||||||
pids: (pids) => {
|
pids: (pids: string) => {
|
||||||
const res = pids.split(',').map((i) => i.trim());
|
const src = pids.split(',').map((i) => i.trim());
|
||||||
for (const i in res) {
|
const d: number[] = [];
|
||||||
if (Number.isSafeInteger(parseInt(res[i], 10))) res[i] = parseInt(res[i], 10);
|
for (const i in src) {
|
||||||
|
if (Number.isSafeInteger(parseInt(src[i], 10))) d[i] = parseInt(src[i], 10);
|
||||||
}
|
}
|
||||||
return res;
|
return d;
|
||||||
},
|
},
|
||||||
role: validator.checkRole,
|
role: validator.checkRole,
|
||||||
penaltyRules: (penaltyRules) => {
|
penaltyRules: (penaltyRules) => {
|
||||||
@@ -122,15 +127,16 @@ async function prepare() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HandlerMixin = (Class) => class extends Class {
|
const HandlerMixin = (Class) => class extends Class {
|
||||||
async renderHTML(name, context) {
|
async renderHTML(name: string, context: any): Promise<string> {
|
||||||
if (enableLog) console.time(name);
|
if (enableLog) console.time(name);
|
||||||
this.hasPerm = (perm) => this.user.hasPerm(perm);
|
this.hasPerm = (perm) => this.user.hasPerm(perm);
|
||||||
// FIXME fix this ugly hack
|
// FIXME fix this ugly hack
|
||||||
this._user = { ...this.user, gravatar: misc.gravatar(this.user.gravatar, 128) };
|
this._user = { ...this.user, gravatar: misc.gravatar(this.user.gravatar, 128) };
|
||||||
const res = await template.render(name, Object.assign(context, {
|
const res = await render(name, Object.assign(context, {
|
||||||
handler: this,
|
handler: this,
|
||||||
|
// @ts-ignore
|
||||||
url: (...args) => this.url(...args),
|
url: (...args) => this.url(...args),
|
||||||
_: (str) => (str ? str.toString().translate(this.user.viewLang || this.session.viewLang) : ''),
|
_: (str: string) => (str ? str.toString().translate(this.user.viewLang || this.session.viewLang) : ''),
|
||||||
user: this.user,
|
user: this.user,
|
||||||
}));
|
}));
|
||||||
if (enableLog) console.timeEnd(name);
|
if (enableLog) console.timeEnd(name);
|
||||||
@@ -185,7 +191,7 @@ const HandlerMixin = (Class) => class extends Class {
|
|||||||
|
|
||||||
url(name, kwargs = {}) {
|
url(name, kwargs = {}) {
|
||||||
let res = '#';
|
let res = '#';
|
||||||
const args = { ...kwargs };
|
const args: any = { ...kwargs };
|
||||||
try {
|
try {
|
||||||
if (this.args.domainId !== 'system') {
|
if (this.args.domainId !== 'system') {
|
||||||
name += '_with_domainId';
|
name += '_with_domainId';
|
||||||
@@ -203,11 +209,45 @@ const HandlerMixin = (Class) => class extends Class {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Handler = HandlerMixin(class {
|
export const Handler = HandlerMixin(class {
|
||||||
/**
|
UIContext: any;
|
||||||
* @param {import('koa').Context} ctx
|
|
||||||
*/
|
args: any;
|
||||||
constructor(ctx, args) {
|
|
||||||
|
ctx: Koa.Context;
|
||||||
|
|
||||||
|
request: {
|
||||||
|
host: string,
|
||||||
|
hostname: string,
|
||||||
|
ip: string,
|
||||||
|
headers: any,
|
||||||
|
cookies: any,
|
||||||
|
body: any,
|
||||||
|
files: any,
|
||||||
|
query: any,
|
||||||
|
path: string,
|
||||||
|
params: any,
|
||||||
|
referer: any,
|
||||||
|
json: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
response: {
|
||||||
|
body: any,
|
||||||
|
type: string,
|
||||||
|
status: number,
|
||||||
|
template: string | undefined,
|
||||||
|
redirect: string | undefined,
|
||||||
|
disposition: string | undefined,
|
||||||
|
attachment: (name: string) => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
session: any;
|
||||||
|
|
||||||
|
csrfToken: string;
|
||||||
|
|
||||||
|
user: any;
|
||||||
|
|
||||||
|
constructor(ctx: Koa.Context, args: any) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.request = {
|
this.request = {
|
||||||
@@ -222,6 +262,7 @@ const Handler = HandlerMixin(class {
|
|||||||
path: ctx.path,
|
path: ctx.path,
|
||||||
params: ctx.params,
|
params: ctx.params,
|
||||||
referer: ctx.request.headers.referer || '/',
|
referer: ctx.request.headers.referer || '/',
|
||||||
|
json: (ctx.request.headers.accept || '').includes('application/json'),
|
||||||
};
|
};
|
||||||
this.response = {
|
this.response = {
|
||||||
body: {},
|
body: {},
|
||||||
@@ -230,6 +271,7 @@ const Handler = HandlerMixin(class {
|
|||||||
template: null,
|
template: null,
|
||||||
redirect: null,
|
redirect: null,
|
||||||
attachment: (name) => ctx.attachment(name),
|
attachment: (name) => ctx.attachment(name),
|
||||||
|
disposition: null,
|
||||||
};
|
};
|
||||||
this.UIContext = {
|
this.UIContext = {
|
||||||
cdn_prefix: '/',
|
cdn_prefix: '/',
|
||||||
@@ -238,17 +280,18 @@ const Handler = HandlerMixin(class {
|
|||||||
this.session = {};
|
this.session = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(name, context) {
|
async render(name: string, context: any) {
|
||||||
|
// @ts-ignore
|
||||||
this.response.body = await this.renderHTML(name, context);
|
this.response.body = await this.renderHTML(name, context);
|
||||||
this.response.type = 'text/html';
|
this.response.type = 'text/html';
|
||||||
}
|
}
|
||||||
|
|
||||||
back(body) {
|
back(body: any) {
|
||||||
this.response.body = body || this.response.body || {};
|
this.response.body = body || this.response.body || {};
|
||||||
this.response.redirect = this.request.headers.referer || '/';
|
this.response.redirect = this.request.headers.referer || '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
binary(data, name) {
|
binary(data: any, name: string) {
|
||||||
this.response.body = data;
|
this.response.body = data;
|
||||||
this.response.template = null;
|
this.response.template = null;
|
||||||
this.response.type = 'application/octet-stream';
|
this.response.type = 'application/octet-stream';
|
||||||
@@ -275,7 +318,6 @@ const Handler = HandlerMixin(class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init({ domainId }) {
|
async init({ domainId }) {
|
||||||
this.preferJson = (this.request.headers.accept || '').includes('application/json');
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.getSession(),
|
this.getSession(),
|
||||||
this.getBdoc(),
|
this.getBdoc(),
|
||||||
@@ -286,7 +328,7 @@ const Handler = HandlerMixin(class {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkCsrfToken(csrfToken) {
|
async checkCsrfToken(csrfToken: any) {
|
||||||
const sdoc = await token.get(csrfToken, token.TYPE_CSRF_TOKEN, false);
|
const sdoc = await token.get(csrfToken, token.TYPE_CSRF_TOKEN, false);
|
||||||
if (!sdoc || sdoc.uid !== this.user._id) throw new CsrfTokenError(csrfToken);
|
if (!sdoc || sdoc.uid !== this.user._id) throw new CsrfTokenError(csrfToken);
|
||||||
}
|
}
|
||||||
@@ -296,7 +338,7 @@ const Handler = HandlerMixin(class {
|
|||||||
await this.renderBody();
|
await this.renderBody();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.response.status = error instanceof UserFacingError ? error.code : 500;
|
this.response.status = error instanceof UserFacingError ? error.code : 500;
|
||||||
if (this.preferJson) this.response.body = { error };
|
if (this.request.json) this.response.body = { error };
|
||||||
else await this.render(error instanceof UserFacingError ? 'error.html' : 'bsod.html', { error });
|
else await this.render(error instanceof UserFacingError ? 'error.html' : 'bsod.html', { error });
|
||||||
}
|
}
|
||||||
await this.putResponse();
|
await this.putResponse();
|
||||||
@@ -304,9 +346,9 @@ const Handler = HandlerMixin(class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async renderBody() {
|
async renderBody() {
|
||||||
if (!this.response.redirect && !this.preferJson) {
|
if (!this.response.redirect && !this.request.json) {
|
||||||
if (this.response.body || this.response.template) {
|
if (this.response.body || this.response.template) {
|
||||||
if (this.request.query.noTemplate || this.preferJson) return;
|
if (this.request.query.noTemplate || this.request.json) return;
|
||||||
const templateName = this.request.query.template || this.response.template;
|
const templateName = this.request.query.template || this.response.template;
|
||||||
if (templateName) {
|
if (templateName) {
|
||||||
this.response.body = this.response.body || {};
|
this.response.body = this.response.body || {};
|
||||||
@@ -318,7 +360,7 @@ const Handler = HandlerMixin(class {
|
|||||||
|
|
||||||
async putResponse() {
|
async putResponse() {
|
||||||
if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition);
|
if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition);
|
||||||
if (this.response.redirect && !this.preferJson) {
|
if (this.response.redirect && !this.request.json) {
|
||||||
this.ctx.response.type = 'application/octet-stream';
|
this.ctx.response.type = 'application/octet-stream';
|
||||||
this.ctx.response.status = 302;
|
this.ctx.response.status = 302;
|
||||||
this.ctx.redirect(this.response.redirect);
|
this.ctx.redirect(this.response.redirect);
|
||||||
@@ -331,7 +373,7 @@ const Handler = HandlerMixin(class {
|
|||||||
this.ctx.response.body = this.response.body;
|
this.ctx.response.body = this.response.body;
|
||||||
this.ctx.response.status = this.response.status || 200;
|
this.ctx.response.status = this.response.status || 200;
|
||||||
}
|
}
|
||||||
this.ctx.response.type = this.preferJson
|
this.ctx.response.type = this.request.json
|
||||||
? 'application/json'
|
? 'application/json'
|
||||||
: this.response.type
|
: this.response.type
|
||||||
? this.response.type
|
? this.response.type
|
||||||
@@ -367,7 +409,7 @@ const Handler = HandlerMixin(class {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const cookie = { secure: await system.get('session.secure') };
|
const cookie: any = { secure: await system.get('session.secure') };
|
||||||
if (this.session.save) {
|
if (this.session.save) {
|
||||||
cookie.expires = this.session.expireAt;
|
cookie.expires = this.session.expireAt;
|
||||||
cookie.maxAge = expireSeconds;
|
cookie.maxAge = expireSeconds;
|
||||||
@@ -469,36 +511,47 @@ const Checker = (permPrivChecker) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function Route(name, route, RouteHandler, ...permPrivChecker) {
|
export function Route(name, route, RouteHandler, ...permPrivChecker) {
|
||||||
const checker = Checker(permPrivChecker);
|
const checker = Checker(permPrivChecker);
|
||||||
router.all(name, route, (ctx) => handle(ctx, RouteHandler, checker));
|
router.all(name, route, (ctx) => handle(ctx, RouteHandler, checker));
|
||||||
router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, checker));
|
router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, checker));
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectionHandler = HandlerMixin(class {
|
export const ConnectionHandler = HandlerMixin(class {
|
||||||
/**
|
conn: sockjs.Connection;
|
||||||
* @param {import('sockjs').Connection} conn
|
|
||||||
*/
|
request: {
|
||||||
constructor(conn) {
|
params: any
|
||||||
|
headers: any
|
||||||
|
ip: string
|
||||||
|
}
|
||||||
|
|
||||||
|
session: any
|
||||||
|
|
||||||
|
user: any
|
||||||
|
|
||||||
|
constructor(conn: sockjs.Connection) {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.request = {
|
this.request = {
|
||||||
params: {},
|
params: {},
|
||||||
headers: conn.headers,
|
headers: conn.headers,
|
||||||
|
ip: this.conn.remoteAddress,
|
||||||
};
|
};
|
||||||
const p = (conn.url.split('?')[1] || '').split('&');
|
this.session = {};
|
||||||
|
const p: any = (conn.url.split('?')[1] || '').split('&');
|
||||||
for (const i in p) p[i] = p[i].split('=');
|
for (const i in p) p[i] = p[i].split('=');
|
||||||
for (const i in p) this.request.params[p[i][0]] = decodeURIComponent(p[i][1]);
|
for (const i in p) this.request.params[p[i][0]] = decodeURIComponent(p[i][1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(data) {
|
send(data: JSON) {
|
||||||
this.conn.write(JSON.stringify(data));
|
this.conn.write(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
close(code, reason) {
|
close(code: number, reason: string) {
|
||||||
this.conn.close(code, reason);
|
this.conn.close(code.toString(), reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
onerror(err) {
|
onerror(err: Error) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.close(1001, err.toString());
|
this.close(1001, err.toString());
|
||||||
}
|
}
|
||||||
@@ -516,7 +569,16 @@ const ConnectionHandler = HandlerMixin(class {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function Connection(name, prefix, RouteConnHandler, ...permPrivChecker) {
|
interface IConnectionHandler {
|
||||||
|
// @ts-ignore
|
||||||
|
new(conn: sockjs.Connection): ConnectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Connection(
|
||||||
|
name: string, prefix: string,
|
||||||
|
RouteConnHandler: IConnectionHandler,
|
||||||
|
...permPrivChecker: Array<number | string | Function>
|
||||||
|
) {
|
||||||
const sock = sockjs.createServer({ prefix });
|
const sock = sockjs.createServer({ prefix });
|
||||||
const checker = Checker(permPrivChecker);
|
const checker = Checker(permPrivChecker);
|
||||||
sock.on('connection', async (conn) => {
|
sock.on('connection', async (conn) => {
|
||||||
@@ -557,16 +619,16 @@ function Connection(name, prefix, RouteConnHandler, ...permPrivChecker) {
|
|||||||
sock.installHandlers(server);
|
sock.installHandlers(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Middleware(middleware) {
|
export function Middleware(middleware: Koa.Middleware) {
|
||||||
app.use(middleware);
|
app.use(middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Validate(key, func) {
|
export function Validate(key: string, func: Function) {
|
||||||
if (validate[key]) validate[key].push(func);
|
if (validate[key]) validate[key].push(func);
|
||||||
else validate[key] = [func];
|
else validate[key] = [func];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start() {
|
export async function start() {
|
||||||
const [disableLog, port] = await system.getMany(['server.log', 'server.port']);
|
const [disableLog, port] = await system.getMany(['server.log', 'server.port']);
|
||||||
if (!disableLog) {
|
if (!disableLog) {
|
||||||
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
||||||
@@ -577,6 +639,8 @@ async function start() {
|
|||||||
console.log('Server listening at: %s', port);
|
console.log('Server listening at: %s', port);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.service.server = module.exports = {
|
global.Hydro.service.server = {
|
||||||
Handler, ConnectionHandler, Route, Connection, Middleware, Validate, prepare, start,
|
Handler, ConnectionHandler, Route, Connection, Middleware, Validate, prepare, start,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default global.Hydro.service.server;
|
||||||
@@ -1,12 +1,33 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const cluster = require('cluster');
|
import cluster from 'cluster';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const superagent = require('superagent');
|
import * as superagent from 'superagent';
|
||||||
const proxy = require('superagent-proxy');
|
import proxy from 'superagent-proxy';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface StringConstructor {
|
||||||
|
random: (digit?: number) => string;
|
||||||
|
}
|
||||||
|
interface ArrayConstructor {
|
||||||
|
isDiff: (a: any[], b: any[]) => boolean;
|
||||||
|
}
|
||||||
|
interface Date {
|
||||||
|
format: (fmt?: string) => string;
|
||||||
|
}
|
||||||
|
interface Math {
|
||||||
|
sum: (...args: Array<number[] | number>) => number;
|
||||||
|
}
|
||||||
|
interface SetConstructor {
|
||||||
|
isSuperset: (set: Set<any>, subset: Set<any>) => boolean;
|
||||||
|
intersection: <T>(setA: Set<T>, setB: Set<T>) => Set<T>;
|
||||||
|
union: <T>(setA: Set<T>, setB: Set<T>) => Set<T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proxy(superagent);
|
proxy(superagent);
|
||||||
|
|
||||||
if (!cluster.worker) {
|
if (!cluster.worker) {
|
||||||
|
// @ts-ignore
|
||||||
cluster.worker = { id: 0 };
|
cluster.worker = { id: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,14 +43,12 @@ Array.isDiff = function isDiff(a, b) {
|
|||||||
if (a.length !== b.length) return true;
|
if (a.length !== b.length) return true;
|
||||||
a.sort();
|
a.sort();
|
||||||
b.sort();
|
b.sort();
|
||||||
for (const i in a) { if (a[i] !== b[i]) return true; }
|
for (const i in a) {
|
||||||
|
if (a[i] !== b[i]) return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} format %Y-%m-%d %H:%M:%S
|
|
||||||
* @returns {string} Formatted date string
|
|
||||||
*/
|
|
||||||
Date.prototype.format = function formatDate(fmt = '%Y-%m-%d %H:%M:%S') {
|
Date.prototype.format = function formatDate(fmt = '%Y-%m-%d %H:%M:%S') {
|
||||||
let h = this.getHours();
|
let h = this.getHours();
|
||||||
if (h < 10) h = `0${h}`;
|
if (h < 10) h = `0${h}`;
|
||||||
@@ -46,17 +65,16 @@ Date.prototype.format = function formatDate(fmt = '%Y-%m-%d %H:%M:%S') {
|
|||||||
.replace('%S', s);
|
.replace('%S', s);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
Math.sum = function sum(...args) {
|
||||||
* @param {object} param0
|
let s = 0;
|
||||||
* @returns {Date}
|
for (const i of args) {
|
||||||
*/
|
if (i instanceof Array) {
|
||||||
Date.prototype.delta = function delta({
|
for (const j of i) {
|
||||||
year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0,
|
s += j;
|
||||||
}) {
|
}
|
||||||
let ts = this.getTime();
|
} else s += i;
|
||||||
ts += second * 1000 + minute * 60000 + hour * 60 * 60000;
|
}
|
||||||
ts += day * 24 * 60 * 60000 + month * 30 * 24 * 60 * 60000 + year * 365 * 24 * 60 * 60000;
|
return s;
|
||||||
return new Date(ts);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Set.isSuperset = function isSuperset(set, subset) {
|
Set.isSuperset = function isSuperset(set, subset) {
|
||||||
@@ -66,27 +84,22 @@ Set.isSuperset = function isSuperset(set, subset) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
Set.union = function Union(setA, setB) {
|
Set.union = function Union<T>(setA: Set<T>, setB: Set<T>) {
|
||||||
const union = new Set(setA);
|
const union = new Set(setA);
|
||||||
for (const elem of setB) union.add(elem);
|
for (const elem of setB) union.add(elem);
|
||||||
return union;
|
return union;
|
||||||
};
|
};
|
||||||
|
|
||||||
Set.intersection = function Intersection(setA, setB) {
|
Set.intersection = function Intersection<T>(setA: Set<T>, setB: Set<T>): Set<T> {
|
||||||
const intersection = new Set();
|
const intersection = new Set();
|
||||||
for (const elem of setB) {
|
for (const elem of setB) {
|
||||||
if (setA.has(elem)) intersection.add(elem);
|
if (setA.has(elem)) intersection.add(elem);
|
||||||
}
|
}
|
||||||
|
// @ts-ignore
|
||||||
return intersection;
|
return intersection;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export function folderSize(folderPath: string) {
|
||||||
* calculate size of a file or directory synchronously
|
|
||||||
* @param {String} folderPath path of the file or folder
|
|
||||||
* @param {fsizeOptions} opts additional options
|
|
||||||
* @returns {Number}
|
|
||||||
*/
|
|
||||||
function folderSize(folderPath) {
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
const _next = function a(p) {
|
const _next = function a(p) {
|
||||||
if (p) {
|
if (p) {
|
||||||
@@ -108,36 +121,24 @@ function folderSize(folderPath) {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.folderSize = folderSize;
|
|
||||||
|
|
||||||
const TIME_RE = /^([0-9]+(?:\.[0-9]*)?)([mu]?)s?$/i;
|
const TIME_RE = /^([0-9]+(?:\.[0-9]*)?)([mu]?)s?$/i;
|
||||||
const TIME_UNITS = { '': 1000, m: 1, u: 0.001 };
|
const TIME_UNITS = { '': 1000, m: 1, u: 0.001 };
|
||||||
const MEMORY_RE = /^([0-9]+(?:\.[0-9]*)?)([kmg])b?$/i;
|
const MEMORY_RE = /^([0-9]+(?:\.[0-9]*)?)([kmg])b?$/i;
|
||||||
const MEMORY_UNITS = { k: 0.1, m: 1, g: 1024 };
|
const MEMORY_UNITS = { k: 0.1, m: 1, g: 1024 };
|
||||||
|
|
||||||
function parseTimeMS(str) {
|
export function parseTimeMS(str) {
|
||||||
const match = TIME_RE.exec(str);
|
const match = TIME_RE.exec(str);
|
||||||
if (!match) throw new Error(str, 'error parsing time');
|
if (!match) throw new Error(`${str} error parsing time`);
|
||||||
return parseInt(parseFloat(match[1], 10) * TIME_UNITS[match[2]], 10);
|
return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.parseTimeMS = parseTimeMS;
|
export function parseMemoryMB(str) {
|
||||||
|
|
||||||
function parseMemoryMB(str) {
|
|
||||||
const match = MEMORY_RE.exec(str);
|
const match = MEMORY_RE.exec(str);
|
||||||
if (!match) throw new Error(str, 'error parsing memory');
|
if (!match) throw new Error(`${str} error parsing memory`);
|
||||||
return parseInt(parseFloat(match[1], 10) * MEMORY_UNITS[match[2]], 10);
|
return Math.floor(parseFloat(match[1]) * MEMORY_UNITS[match[2]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.parseMemoryMB = parseMemoryMB;
|
export function isClass(obj: any, strict = false) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an object could be an instantiable class.
|
|
||||||
* @param {any} obj
|
|
||||||
* @param {boolean} strict
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
function isClass(obj, strict = false) {
|
|
||||||
if (typeof obj !== 'function') return false;
|
if (typeof obj !== 'function') return false;
|
||||||
const str = obj.toString();
|
const str = obj.toString();
|
||||||
if (obj.prototype === undefined) return false;
|
if (obj.prototype === undefined) return false;
|
||||||
@@ -152,5 +153,3 @@ function isClass(obj, strict = false) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.isClass = isClass;
|
|
||||||
+34
-11
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.5.0",
|
"version": "2.6.0",
|
||||||
"main": "hydro/loader.js",
|
"main": "hydro/loader.js",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
"author": "masnn",
|
"author": "masnn",
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
"adm-zip": "^0.4.16",
|
"adm-zip": "^0.4.16",
|
||||||
"ansi_up": "^4.0.4",
|
"ansi_up": "^4.0.4",
|
||||||
"detect-browser": "^5.1.1",
|
"detect-browser": "^5.1.1",
|
||||||
"eslint": "7.2.0",
|
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"koa": "^2.13.0",
|
"koa": "^2.13.0",
|
||||||
"koa-body": "^4.2.0",
|
"koa-body": "^4.2.0",
|
||||||
@@ -30,26 +29,50 @@
|
|||||||
"nodemailer": "^6.4.10",
|
"nodemailer": "^6.4.10",
|
||||||
"nunjucks": "^3.2.1",
|
"nunjucks": "^3.2.1",
|
||||||
"prismjs": "^1.20.0",
|
"prismjs": "^1.20.0",
|
||||||
"snyk": "^1.354.0",
|
|
||||||
"sockjs": "^0.3.20",
|
"sockjs": "^0.3.20",
|
||||||
"superagent": "^5.2.2",
|
"superagent": "^5.2.2",
|
||||||
"superagent-proxy": "^2.0.0",
|
"superagent-proxy": "^2.0.0",
|
||||||
"systeminformation": "^4.26.5",
|
"systeminformation": "^4.26.5",
|
||||||
"yargs": "^15.3.1"
|
"yargs": "^15.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/adm-zip": "^0.4.33",
|
||||||
|
"@types/koa": "^2.11.3",
|
||||||
|
"@types/koa-morgan": "^1.0.4",
|
||||||
|
"@types/koa-router": "^7.4.1",
|
||||||
|
"@types/koa-static-cache": "^5.1.0",
|
||||||
|
"@types/lodash": "^4.14.157",
|
||||||
|
"@types/markdown-it": "^10.0.1",
|
||||||
|
"@types/mongodb": "^3.5.25",
|
||||||
|
"@types/node": "^14.0.18",
|
||||||
|
"@types/nodemailer": "^6.4.0",
|
||||||
|
"@types/nunjucks": "^3.1.3",
|
||||||
|
"@types/prismjs": "^1.16.1",
|
||||||
|
"@types/sockjs": "^0.3.32",
|
||||||
|
"@types/superagent": "^4.1.8",
|
||||||
|
"@types/superagent-proxy": "^2.0.0",
|
||||||
|
"@types/yargs": "^15.0.5",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^3.6.0",
|
||||||
|
"@typescript-eslint/parser": "^3.6.0",
|
||||||
|
"eslint": "^7.4.0",
|
||||||
"eslint-config-airbnb-base": "^14.2.0",
|
"eslint-config-airbnb-base": "^14.2.0",
|
||||||
|
"eslint-import-resolver-typescript": "^2.0.0",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||||
|
"snyk": "^1.354.0",
|
||||||
|
"ts-node": "^8.10.2",
|
||||||
|
"tsc": "^1.20150623.0",
|
||||||
|
"typescript": "^3.9.6",
|
||||||
"webpack": "^4.42.1"
|
"webpack": "^4.42.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node build/development.js",
|
"tsc": "tsc",
|
||||||
"build:development": "node build/development.js",
|
"build": "tsc && cp -r hydro/script dist",
|
||||||
"build:production": "node build/production.js",
|
"pack:development": "node build/development.js",
|
||||||
"build:all": "cd ui && yarn && yarn build:production && cd .. && yarn && yarn build:production",
|
"pack:production": "node build/production.js",
|
||||||
"start": "node hydro/loader.js",
|
"build:all": "cd ui && yarn && yarn build:production && cd .. && yarn && yarn build && yarn pack:production",
|
||||||
"pack": "pkg .",
|
"start": "ts-node hydro/loader.ts",
|
||||||
|
"pkg": "pkg .",
|
||||||
"lint": "eslint build hydro module tool --fix",
|
"lint": "eslint build hydro module tool --fix",
|
||||||
"snyk-protect": "snyk protect",
|
"snyk-protect": "snyk protect",
|
||||||
"prepare": "yarn run snyk-protect"
|
"prepare": "yarn run snyk-protect"
|
||||||
@@ -63,4 +86,4 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"snyk": true
|
"snyk": true
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
{{ _('Open Since') }}: {{ datetimeSpan(tdoc['beginAt'])|safe }}
|
{{ _('Open Since') }}: {{ datetimeSpan(tdoc['beginAt'])|safe }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{% if model.contest.isHomeworkExtended(tdoc) or model.contest.isDone(tdoc) %}
|
{% if model.contest.isExtended(tdoc) or model.contest.isDone(tdoc) %}
|
||||||
{{ _('Hard Deadline') }}: {{ datetimeSpan(tdoc['endAt'])|safe }}
|
{{ _('Hard Deadline') }}: {{ datetimeSpan(tdoc['endAt'])|safe }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ _('Deadline') }}: {{ datetimeSpan(tdoc['penaltySince'])|safe }}
|
{{ _('Deadline') }}: {{ datetimeSpan(tdoc['penaltySince'])|safe }}
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@
|
|||||||
{{ _('Open Since') }}: {{ datetimeSpan(tdoc['beginAt'])|safe }}
|
{{ _('Open Since') }}: {{ datetimeSpan(tdoc['beginAt'])|safe }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{% if model.contest.isHomeworkExtended(tdoc) or model.contest.isDone(tdoc) %}
|
{% if model.contest.isExtended(tdoc) or model.contest.isDone(tdoc) %}
|
||||||
{{ _('Hard Deadline') }}: {{ datetimeSpan(tdoc['endAt'])|safe }}
|
{{ _('Hard Deadline') }}: {{ datetimeSpan(tdoc['endAt'])|safe }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ _('Deadline') }}: {{ datetimeSpan(tdoc['penaltySince'])|safe }}
|
{{ _('Deadline') }}: {{ datetimeSpan(tdoc['penaltySince'])|safe }}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div>
|
<div>
|
||||||
<ol class="menu">
|
<ol class="menu">
|
||||||
{% if model.contest.isOngoing(tdoc) or model.contest.isHomeworkExtended(tdoc) %}
|
{% if model.contest.isOngoing(tdoc) or model.contest.isExtended(tdoc) %}
|
||||||
{% if page_name == 'homework_detail_problem' %}
|
{% if page_name == 'homework_detail_problem' %}
|
||||||
<li class="menu__item scratchpad--hide"><a class="menu__link highlight" name="problem-sidebar__open-scratchpad" href="javascript:;" data-global-hotkey="alt+e">
|
<li class="menu__item scratchpad--hide"><a class="menu__link highlight" name="problem-sidebar__open-scratchpad" href="javascript:;" data-global-hotkey="alt+e">
|
||||||
<span class="icon icon-enlarge"></span> {{ _('Open Scratchpad') }} (Alt+E)
|
<span class="icon icon-enlarge"></span> {{ _('Open Scratchpad') }} (Alt+E)
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2018",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"rootDir": "./hydro/",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"experimentalDecorators": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"ui",
|
||||||
|
"build"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ clone 本项目到一个空文件夹,并进行编译:
|
|||||||
git clone https://github.com/hydro-dev/Hydro.git
|
git clone https://github.com/hydro-dev/Hydro.git
|
||||||
cd Hydro
|
cd Hydro
|
||||||
yarn build:all
|
yarn build:all
|
||||||
node hydro/loader.js
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
注意:单个Hydro实例需要约100MB的运行内存,但在载入时可能需要较多内存(取决于安装的模块数量与大小)
|
注意:单个Hydro实例需要约100MB的运行内存,但在载入时可能需要较多内存(取决于安装的模块数量与大小)
|
||||||
|
|||||||
@@ -23,6 +23,14 @@
|
|||||||
chalk "^2.0.0"
|
chalk "^2.0.0"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
|
"@babel/runtime-corejs3@^7.8.3":
|
||||||
|
version "7.10.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz#f29fc1990307c4c57b10dbd6ce667b27159d9e0d"
|
||||||
|
integrity sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw==
|
||||||
|
dependencies:
|
||||||
|
core-js-pure "^3.0.0"
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@hydrooj/markdown-it-katex@^2.1.1":
|
"@hydrooj/markdown-it-katex@^2.1.1":
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@hydrooj/markdown-it-katex/-/markdown-it-katex-2.1.1.tgz#0b656483b912593e2a927360592462770e329f25"
|
resolved "https://registry.yarnpkg.com/@hydrooj/markdown-it-katex/-/markdown-it-katex-2.1.1.tgz#0b656483b912593e2a927360592462770e329f25"
|
||||||
@@ -281,6 +289,35 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
defer-to-connect "^2.0.0"
|
defer-to-connect "^2.0.0"
|
||||||
|
|
||||||
|
"@types/accepts@*":
|
||||||
|
version "1.3.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575"
|
||||||
|
integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/adm-zip@^0.4.33":
|
||||||
|
version "0.4.33"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/adm-zip/-/adm-zip-0.4.33.tgz#ea5b94f771443f655613b64f920c0555867200dd"
|
||||||
|
integrity sha512-WM0DCWFLjXtddl0fu0+iN2ZF+qz8RF9RddG5OSy/S90AQz01Fu8lHn/3oTIZDxvG8gVcnBLAHMHOdBLbV6m6Mw==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/body-parser@*":
|
||||||
|
version "1.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f"
|
||||||
|
integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/connect" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/bson@*":
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/bson/-/bson-4.0.2.tgz#7accb85942fc39bbdb7515d4de437c04f698115f"
|
||||||
|
integrity sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/cacheable-request@^6.0.1":
|
"@types/cacheable-request@^6.0.1":
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976"
|
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976"
|
||||||
@@ -296,6 +333,33 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||||
|
|
||||||
|
"@types/connect@*":
|
||||||
|
version "3.4.33"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546"
|
||||||
|
integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/content-disposition@*":
|
||||||
|
version "0.5.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.3.tgz#0aa116701955c2faa0717fc69cd1596095e49d96"
|
||||||
|
integrity sha512-P1bffQfhD3O4LW0ioENXUhZ9OIa0Zn+P7M+pWgkCKaT53wVLSq0mrKksCID/FGHpFhRSxRGhgrQmfhRuzwtKdg==
|
||||||
|
|
||||||
|
"@types/cookiejar@*":
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.1.tgz#90b68446364baf9efd8e8349bb36bd3852b75b80"
|
||||||
|
integrity sha512-aRnpPa7ysx3aNW60hTiCtLHlQaIFsXFCgQlpakNgDNVFzbtusSY8PwjAQgRWfSk0ekNoBjO51eQRB6upA9uuyw==
|
||||||
|
|
||||||
|
"@types/cookies@*":
|
||||||
|
version "0.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.4.tgz#26dedf791701abc0e36b5b79a5722f40e455f87b"
|
||||||
|
integrity sha512-oTGtMzZZAVuEjTwCjIh8T8FrC8n/uwy+PG0yTvQcdZ7etoel7C7/3MSd7qrukENTgQtotG7gvBlBojuVs7X5rw==
|
||||||
|
dependencies:
|
||||||
|
"@types/connect" "*"
|
||||||
|
"@types/express" "*"
|
||||||
|
"@types/keygrip" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/debug@^4.1.4":
|
"@types/debug@^4.1.4":
|
||||||
version "4.1.5"
|
version "4.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
|
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
|
||||||
@@ -306,11 +370,35 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.4.tgz#d61990c0cee72c4e475de737a140b51fe925a2c8"
|
resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.4.tgz#d61990c0cee72c4e475de737a140b51fe925a2c8"
|
||||||
integrity sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ==
|
integrity sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ==
|
||||||
|
|
||||||
|
"@types/eslint-visitor-keys@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||||
|
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
|
||||||
|
|
||||||
"@types/events@*":
|
"@types/events@*":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||||
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
||||||
|
|
||||||
|
"@types/express-serve-static-core@*":
|
||||||
|
version "4.17.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz#b8f7b714138536742da222839892e203df569d1c"
|
||||||
|
integrity sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
"@types/qs" "*"
|
||||||
|
"@types/range-parser" "*"
|
||||||
|
|
||||||
|
"@types/express@*":
|
||||||
|
version "4.17.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.6.tgz#6bce49e49570507b86ea1b07b806f04697fac45e"
|
||||||
|
integrity sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==
|
||||||
|
dependencies:
|
||||||
|
"@types/body-parser" "*"
|
||||||
|
"@types/express-serve-static-core" "*"
|
||||||
|
"@types/qs" "*"
|
||||||
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
"@types/formidable@^1.0.31":
|
"@types/formidable@^1.0.31":
|
||||||
version "1.0.31"
|
version "1.0.31"
|
||||||
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.0.31.tgz#274f9dc2d0a1a9ce1feef48c24ca0859e7ec947b"
|
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.0.31.tgz#274f9dc2d0a1a9ce1feef48c24ca0859e7ec947b"
|
||||||
@@ -332,6 +420,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/hosted-git-info/-/hosted-git-info-2.7.0.tgz#5199b4df62d53f51d60520d61513eaa4b2dd3d3d"
|
resolved "https://registry.yarnpkg.com/@types/hosted-git-info/-/hosted-git-info-2.7.0.tgz#5199b4df62d53f51d60520d61513eaa4b2dd3d3d"
|
||||||
integrity sha512-OW/D8GqCyQtH8F7xDdDxzPJTBgknZeZhlCakUcBCya2rYPRN53F+0YJVwSPyiyAhrknnjkl3P9qVk0oBI4S1qw==
|
integrity sha512-OW/D8GqCyQtH8F7xDdDxzPJTBgknZeZhlCakUcBCya2rYPRN53F+0YJVwSPyiyAhrknnjkl3P9qVk0oBI4S1qw==
|
||||||
|
|
||||||
|
"@types/http-assert@*":
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
|
||||||
|
integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==
|
||||||
|
|
||||||
"@types/http-cache-semantics@*":
|
"@types/http-cache-semantics@*":
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a"
|
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a"
|
||||||
@@ -342,11 +435,21 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb"
|
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb"
|
||||||
integrity sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww==
|
integrity sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww==
|
||||||
|
|
||||||
|
"@types/json-schema@^7.0.3":
|
||||||
|
version "7.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
|
||||||
|
integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
|
||||||
|
|
||||||
"@types/json5@^0.0.29":
|
"@types/json5@^0.0.29":
|
||||||
version "0.0.29"
|
version "0.0.29"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||||
|
|
||||||
|
"@types/keygrip@*":
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72"
|
||||||
|
integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==
|
||||||
|
|
||||||
"@types/keyv@*":
|
"@types/keyv@*":
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7"
|
resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7"
|
||||||
@@ -354,11 +457,96 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/koa-compose@*":
|
||||||
|
version "3.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d"
|
||||||
|
integrity sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/koa" "*"
|
||||||
|
|
||||||
|
"@types/koa-morgan@^1.0.4":
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/koa-morgan/-/koa-morgan-1.0.4.tgz#69d67084521425859ae275370b8584076cf44aec"
|
||||||
|
integrity sha512-XtZu6La4TopP6ZlCGNqg0hTsBAwbTP2tMb93cuDdE285WofP/Ould/tFwSvSHEr0Dxger2P+ovI1uvF3XZev8Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/koa" "*"
|
||||||
|
"@types/morgan" "*"
|
||||||
|
|
||||||
|
"@types/koa-router@^7.4.1":
|
||||||
|
version "7.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/koa-router/-/koa-router-7.4.1.tgz#3702a4cabe4558cc4eec70d5574acc04beecff7c"
|
||||||
|
integrity sha512-Hg78TXz78QYfEgdq3nTeRmQFEwJKZljsXb/DhtexmyrpRDRnl59oMglh9uPj3/WgKor0woANrYTnxA8gaWGK2A==
|
||||||
|
dependencies:
|
||||||
|
"@types/koa" "*"
|
||||||
|
|
||||||
|
"@types/koa-static-cache@^5.1.0":
|
||||||
|
version "5.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/koa-static-cache/-/koa-static-cache-5.1.0.tgz#37c33f877ecd92c13065ed402184632ab18da326"
|
||||||
|
integrity sha512-9n89fpWYyhfdpjDmHYJikeah6r2F3LC8F+dm+nuCrKd+7R4CiCnlYbNR1AfB3H/1b7Ndz+uucrAycwi9u7QtRA==
|
||||||
|
dependencies:
|
||||||
|
"@types/koa" "*"
|
||||||
|
|
||||||
|
"@types/koa@*", "@types/koa@^2.11.3":
|
||||||
|
version "2.11.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.11.3.tgz#540ece376581b12beadf9a417dd1731bc31c16ce"
|
||||||
|
integrity sha512-ABxVkrNWa4O/Jp24EYI/hRNqEVRlhB9g09p48neQp4m3xL1TJtdWk2NyNQSMCU45ejeELMQZBYyfstyVvO2H3Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/accepts" "*"
|
||||||
|
"@types/content-disposition" "*"
|
||||||
|
"@types/cookies" "*"
|
||||||
|
"@types/http-assert" "*"
|
||||||
|
"@types/keygrip" "*"
|
||||||
|
"@types/koa-compose" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/linkify-it@*":
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
|
||||||
|
integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
|
||||||
|
|
||||||
|
"@types/lodash@^4.14.157":
|
||||||
|
version "4.14.157"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.157.tgz#fdac1c52448861dfde1a2e1515dbc46e54926dc8"
|
||||||
|
integrity sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==
|
||||||
|
|
||||||
|
"@types/markdown-it@^10.0.1":
|
||||||
|
version "10.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-10.0.1.tgz#94e252ab689c8e9ceb9aff2946e0a458390105eb"
|
||||||
|
integrity sha512-L1ibTdA5IUe/cRBlf3N3syAOBQSN1WCMGtAWir6mKxibiRl4LmpZM4jLz+7zAqiMnhQuAP1sqZOF9wXgn2kpEg==
|
||||||
|
dependencies:
|
||||||
|
"@types/linkify-it" "*"
|
||||||
|
"@types/mdurl" "*"
|
||||||
|
|
||||||
|
"@types/mdurl@*":
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
|
||||||
|
integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
|
||||||
|
|
||||||
|
"@types/mime@*":
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.2.tgz#857a118d8634c84bba7ae14088e4508490cd5da5"
|
||||||
|
integrity sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==
|
||||||
|
|
||||||
"@types/minimatch@*":
|
"@types/minimatch@*":
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||||
|
|
||||||
|
"@types/mongodb@^3.5.25":
|
||||||
|
version "3.5.25"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.5.25.tgz#ab187db04d79f8e3f15af236327dc9139d9d4736"
|
||||||
|
integrity sha512-2H/Owt+pHCl9YmBOYnXc3VdnxejJEjVdH+QCWL5ZAfPehEn3evygKBX3/vKRv7aTwfNbUd0E5vjJdQklH/9a6w==
|
||||||
|
dependencies:
|
||||||
|
"@types/bson" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/morgan@*":
|
||||||
|
version "1.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.1.tgz#6457872df95647c1dbc6b3741e8146b71ece74bf"
|
||||||
|
integrity sha512-2j5IKrgJpEP6xw/uiVb2Xfga0W0sSVD9JP9t7EZLvpBENdB0OKgcnoKS8IsjNeNnZ/86robdZ61Orl0QCFGOXg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "14.0.14"
|
version "14.0.14"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.14.tgz#24a0b5959f16ac141aeb0c5b3cd7a15b7c64cbce"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.14.tgz#24a0b5959f16ac141aeb0c5b3cd7a15b7c64cbce"
|
||||||
@@ -369,11 +557,43 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.12.tgz#9c72e865380a7dc99999ea0ef20fc9635b503d20"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.12.tgz#9c72e865380a7dc99999ea0ef20fc9635b503d20"
|
||||||
integrity sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==
|
integrity sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==
|
||||||
|
|
||||||
|
"@types/node@^14.0.18":
|
||||||
|
version "14.0.18"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.18.tgz#5111b2285659442f9f95697386a2b42b875bd7e9"
|
||||||
|
integrity sha512-0Z3nS5acM0cIV4JPzrj9g/GH0Et5vmADWtip3YOXOp1NpOLU8V3KoZDc8ny9c1pe/YSYYzQkAWob6dyV/EWg4g==
|
||||||
|
|
||||||
"@types/node@^6.14.4", "@types/node@^6.14.7":
|
"@types/node@^6.14.4", "@types/node@^6.14.7":
|
||||||
version "6.14.10"
|
version "6.14.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.14.10.tgz#d9ce598127eb0cc02821476862d11389cb01f6a4"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.14.10.tgz#d9ce598127eb0cc02821476862d11389cb01f6a4"
|
||||||
integrity sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==
|
integrity sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==
|
||||||
|
|
||||||
|
"@types/nodemailer@^6.4.0":
|
||||||
|
version "6.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.0.tgz#d8c039be3ed685c4719a026455555be82c124b74"
|
||||||
|
integrity sha512-KY7bFWB0MahRZvVW4CuW83qcCDny59pJJ0MQ5ifvfcjNwPlIT0vW4uARO4u1gtkYnWdhSvURegecY/tzcukJcA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/nunjucks@^3.1.3":
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/nunjucks/-/nunjucks-3.1.3.tgz#55fa2bf6fd34641545a6686217324fde66d31164"
|
||||||
|
integrity sha512-42IiIIBdoB7ZDwCVhCWYT4fMCj+4TeacuVgh7xyT2du5EhkpA+OFeeDdYTFCUt1MrHb8Aw7ZqFvr8s1bwP9l8w==
|
||||||
|
|
||||||
|
"@types/prismjs@^1.16.1":
|
||||||
|
version "1.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.16.1.tgz#50b82947207847db6abcbcd14caa89e3b897c259"
|
||||||
|
integrity sha512-RNgcK3FEc1GpeOkamGDq42EYkb6yZW5OWQwTS56NJIB8WL0QGISQglA7En7NUx9RGP8AC52DOe+squqbAckXlA==
|
||||||
|
|
||||||
|
"@types/qs@*":
|
||||||
|
version "6.9.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.3.tgz#b755a0934564a200d3efdf88546ec93c369abd03"
|
||||||
|
integrity sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==
|
||||||
|
|
||||||
|
"@types/range-parser@*":
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
|
||||||
|
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
|
||||||
|
|
||||||
"@types/responselike@*", "@types/responselike@^1.0.0":
|
"@types/responselike@*", "@types/responselike@^1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
||||||
@@ -386,6 +606,36 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
|
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
|
||||||
integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==
|
integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==
|
||||||
|
|
||||||
|
"@types/serve-static@*":
|
||||||
|
version "1.13.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.4.tgz#6662a93583e5a6cabca1b23592eb91e12fa80e7c"
|
||||||
|
integrity sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==
|
||||||
|
dependencies:
|
||||||
|
"@types/express-serve-static-core" "*"
|
||||||
|
"@types/mime" "*"
|
||||||
|
|
||||||
|
"@types/sockjs@^0.3.32":
|
||||||
|
version "0.3.32"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.32.tgz#922cdb2f16bf0387410414407df93a55a884c1a8"
|
||||||
|
integrity sha512-KivrihaB55i/6fzR7vvE7XdpYTEAvP7I1/pJeq2bev2lJlb2SY8+3c5Nz3ICrRroehV3GezLK96B05Kx9+d0DA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/superagent-proxy@^2.0.0":
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/superagent-proxy/-/superagent-proxy-2.0.0.tgz#9dd72c2b875ab56c4e6bd42435542fb040aa75b8"
|
||||||
|
integrity sha512-EiC8z93IkQhTYtmQ4/g/CoXUlZRmrmfOwMkjO3/lzxEmfShaPYlkk7vtgMpFkKw8V/1g13fPwR98dZuieHeBUw==
|
||||||
|
dependencies:
|
||||||
|
"@types/superagent" "*"
|
||||||
|
|
||||||
|
"@types/superagent@*", "@types/superagent@^4.1.8":
|
||||||
|
version "4.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.8.tgz#f663dcdd24705e07fce055003ace9b824f2a51c9"
|
||||||
|
integrity sha512-iol9KxQ7SLHatBJUiZ4uABrS4VS1frLjqPednxZz82eoCzo3Uy3TOH0p0ZIBbfBj8E/xqOtvizjBs9h7xi/l2g==
|
||||||
|
dependencies:
|
||||||
|
"@types/cookiejar" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/xml2js@0.4.5":
|
"@types/xml2js@0.4.5":
|
||||||
version "0.4.5"
|
version "0.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.5.tgz#d21759b056f282d9c7066f15bbf5c19b908f22fa"
|
resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.5.tgz#d21759b056f282d9c7066f15bbf5c19b908f22fa"
|
||||||
@@ -393,6 +643,78 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/yargs-parser@*":
|
||||||
|
version "15.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
|
||||||
|
integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
|
||||||
|
|
||||||
|
"@types/yargs@^15.0.5":
|
||||||
|
version "15.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.5.tgz#947e9a6561483bdee9adffc983e91a6902af8b79"
|
||||||
|
integrity sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==
|
||||||
|
dependencies:
|
||||||
|
"@types/yargs-parser" "*"
|
||||||
|
|
||||||
|
"@typescript-eslint/eslint-plugin@^3.6.0":
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz#ba2b6cae478b8fca3f2e58ff1313e4198eea2d8a"
|
||||||
|
integrity sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/experimental-utils" "3.6.0"
|
||||||
|
debug "^4.1.1"
|
||||||
|
functional-red-black-tree "^1.0.1"
|
||||||
|
regexpp "^3.0.0"
|
||||||
|
semver "^7.3.2"
|
||||||
|
tsutils "^3.17.1"
|
||||||
|
|
||||||
|
"@typescript-eslint/experimental-utils@3.6.0":
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz#0138152d66e3e53a6340f606793fb257bf2d76a1"
|
||||||
|
integrity sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/json-schema" "^7.0.3"
|
||||||
|
"@typescript-eslint/types" "3.6.0"
|
||||||
|
"@typescript-eslint/typescript-estree" "3.6.0"
|
||||||
|
eslint-scope "^5.0.0"
|
||||||
|
eslint-utils "^2.0.0"
|
||||||
|
|
||||||
|
"@typescript-eslint/parser@^3.6.0":
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.6.0.tgz#79b5232e1a2d06f1fc745942b690cd87aca7b60e"
|
||||||
|
integrity sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw==
|
||||||
|
dependencies:
|
||||||
|
"@types/eslint-visitor-keys" "^1.0.0"
|
||||||
|
"@typescript-eslint/experimental-utils" "3.6.0"
|
||||||
|
"@typescript-eslint/types" "3.6.0"
|
||||||
|
"@typescript-eslint/typescript-estree" "3.6.0"
|
||||||
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
|
"@typescript-eslint/types@3.6.0":
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.0.tgz#4bd6eee55d2f9d35a4b36c4804be1880bf68f7bc"
|
||||||
|
integrity sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg==
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree@3.6.0":
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.0.tgz#9b4cab43f1192b64ff51530815b8919f166ce177"
|
||||||
|
integrity sha512-G57NDSABHjvob7zVV09ehWyD1K6/YUKjz5+AufObFyjNO4DVmKejj47MHjVHHlZZKgmpJD2yyH9lfCXHrPITFg==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/types" "3.6.0"
|
||||||
|
"@typescript-eslint/visitor-keys" "3.6.0"
|
||||||
|
debug "^4.1.1"
|
||||||
|
glob "^7.1.6"
|
||||||
|
is-glob "^4.0.1"
|
||||||
|
lodash "^4.17.15"
|
||||||
|
semver "^7.3.2"
|
||||||
|
tsutils "^3.17.1"
|
||||||
|
|
||||||
|
"@typescript-eslint/visitor-keys@3.6.0":
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz#44185eb0cc47651034faa95c5e2e8b314ecebb26"
|
||||||
|
integrity sha512-p1izllL2Ubwunite0ITjubuMQRBGgjdVYwyG7lXPX8GbrA6qF0uwSRz9MnXZaHMxID4948gX0Ez8v9tUDi/KfQ==
|
||||||
|
dependencies:
|
||||||
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
"@webassemblyjs/ast@1.9.0":
|
"@webassemblyjs/ast@1.9.0":
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
|
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
|
||||||
@@ -701,7 +1023,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.0.tgz#5c894537098785926d71e696114a53ce768ed773"
|
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.0.tgz#5c894537098785926d71e696114a53ce768ed773"
|
||||||
integrity sha512-eyoaac3btgU8eJlvh01En8OCKzRqlLe2G5jDsCr3RiE2uLGMEEB1aaGwVVpwR8M95956tGH6R+9edC++OvzaVw==
|
integrity sha512-eyoaac3btgU8eJlvh01En8OCKzRqlLe2G5jDsCr3RiE2uLGMEEB1aaGwVVpwR8M95956tGH6R+9edC++OvzaVw==
|
||||||
|
|
||||||
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2:
|
ajv@^6.1.0, ajv@^6.10.2:
|
||||||
version "6.12.2"
|
version "6.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
|
||||||
integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
|
integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
|
||||||
@@ -711,6 +1033,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2:
|
|||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
ajv@^6.10.0:
|
||||||
|
version "6.12.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706"
|
||||||
|
integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal "^3.1.1"
|
||||||
|
fast-json-stable-stringify "^2.0.0"
|
||||||
|
json-schema-traverse "^0.4.1"
|
||||||
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ansi-align@^3.0.0:
|
ansi-align@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
|
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
|
||||||
@@ -718,18 +1050,16 @@ ansi-align@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
string-width "^3.0.0"
|
string-width "^3.0.0"
|
||||||
|
|
||||||
|
ansi-colors@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||||
|
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
||||||
|
|
||||||
ansi-escapes@3.2.0, ansi-escapes@^3.2.0:
|
ansi-escapes@3.2.0, ansi-escapes@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
||||||
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
|
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
|
||||||
|
|
||||||
ansi-escapes@^4.2.1:
|
|
||||||
version "4.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
|
|
||||||
integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==
|
|
||||||
dependencies:
|
|
||||||
type-fest "^0.11.0"
|
|
||||||
|
|
||||||
ansi-regex@^2.0.0:
|
ansi-regex@^2.0.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||||
@@ -811,6 +1141,11 @@ archy@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
|
resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
|
||||||
integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=
|
integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=
|
||||||
|
|
||||||
|
arg@^4.1.0:
|
||||||
|
version "4.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||||
|
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
|
||||||
|
|
||||||
argparse@^1.0.7:
|
argparse@^1.0.7:
|
||||||
version "1.0.10"
|
version "1.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||||
@@ -1387,13 +1722,6 @@ cli-cursor@^2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
restore-cursor "^2.0.0"
|
restore-cursor "^2.0.0"
|
||||||
|
|
||||||
cli-cursor@^3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
|
|
||||||
integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
|
|
||||||
dependencies:
|
|
||||||
restore-cursor "^3.1.0"
|
|
||||||
|
|
||||||
cli-spinner@0.2.10:
|
cli-spinner@0.2.10:
|
||||||
version "0.2.10"
|
version "0.2.10"
|
||||||
resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"
|
resolved "https://registry.yarnpkg.com/cli-spinner/-/cli-spinner-0.2.10.tgz#f7d617a36f5c47a7bc6353c697fc9338ff782a47"
|
||||||
@@ -1618,6 +1946,11 @@ copy-descriptor@^0.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||||
|
|
||||||
|
core-js-pure@^3.0.0:
|
||||||
|
version "3.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||||
|
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
@@ -1739,6 +2072,13 @@ decamelize@^1.1.1, decamelize@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||||
|
|
||||||
|
decamelize@^3.2.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-3.2.0.tgz#84b8e8f4f8c579f938e35e2cc7024907e0090851"
|
||||||
|
integrity sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==
|
||||||
|
dependencies:
|
||||||
|
xregexp "^4.2.4"
|
||||||
|
|
||||||
decode-uri-component@^0.2.0:
|
decode-uri-component@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||||
@@ -2026,6 +2366,13 @@ enhanced-resolve@^4.1.0:
|
|||||||
memory-fs "^0.5.0"
|
memory-fs "^0.5.0"
|
||||||
tapable "^1.0.0"
|
tapable "^1.0.0"
|
||||||
|
|
||||||
|
enquirer@^2.3.5:
|
||||||
|
version "2.3.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
||||||
|
integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
|
||||||
|
dependencies:
|
||||||
|
ansi-colors "^4.1.1"
|
||||||
|
|
||||||
entities@~2.0.0:
|
entities@~2.0.0:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
||||||
@@ -2134,6 +2481,17 @@ eslint-import-resolver-node@^0.3.3:
|
|||||||
debug "^2.6.9"
|
debug "^2.6.9"
|
||||||
resolve "^1.13.1"
|
resolve "^1.13.1"
|
||||||
|
|
||||||
|
eslint-import-resolver-typescript@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.0.0.tgz#e95f126cc12d3018b9cc11692b4dbfd3e17d3ea6"
|
||||||
|
integrity sha512-bT5Frpl8UWoHBtY25vKUOMoVIMlJQOMefHLyQ4Tz3MQpIZ2N6yYKEEIHMo38bszBNUuMBW6M3+5JNYxeiGFH4w==
|
||||||
|
dependencies:
|
||||||
|
debug "^4.1.1"
|
||||||
|
is-glob "^4.0.1"
|
||||||
|
resolve "^1.12.0"
|
||||||
|
tiny-glob "^0.2.6"
|
||||||
|
tsconfig-paths "^3.9.0"
|
||||||
|
|
||||||
eslint-module-utils@^2.6.0:
|
eslint-module-utils@^2.6.0:
|
||||||
version "2.6.0"
|
version "2.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6"
|
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6"
|
||||||
@@ -2169,7 +2527,7 @@ eslint-scope@^4.0.3:
|
|||||||
esrecurse "^4.1.0"
|
esrecurse "^4.1.0"
|
||||||
estraverse "^4.1.1"
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
eslint-scope@^5.1.0:
|
eslint-scope@^5.0.0, eslint-scope@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5"
|
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5"
|
||||||
integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==
|
integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==
|
||||||
@@ -2189,10 +2547,10 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
|
||||||
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
|
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
|
||||||
|
|
||||||
eslint@7.2.0:
|
eslint@^7.4.0:
|
||||||
version "7.2.0"
|
version "7.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.2.0.tgz#d41b2e47804b30dbabb093a967fb283d560082e6"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.4.0.tgz#4e35a2697e6c1972f9d6ef2b690ad319f80f206f"
|
||||||
integrity sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ==
|
integrity sha512-gU+lxhlPHu45H3JkEGgYhWhkR9wLHHEXC9FbWFnTlEkbKyZKWgWRLgf61E8zWmBuI6g5xKBph9ltg3NtZMVF8g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.0.0"
|
"@babel/code-frame" "^7.0.0"
|
||||||
ajv "^6.10.0"
|
ajv "^6.10.0"
|
||||||
@@ -2200,6 +2558,7 @@ eslint@7.2.0:
|
|||||||
cross-spawn "^7.0.2"
|
cross-spawn "^7.0.2"
|
||||||
debug "^4.0.1"
|
debug "^4.0.1"
|
||||||
doctrine "^3.0.0"
|
doctrine "^3.0.0"
|
||||||
|
enquirer "^2.3.5"
|
||||||
eslint-scope "^5.1.0"
|
eslint-scope "^5.1.0"
|
||||||
eslint-utils "^2.0.0"
|
eslint-utils "^2.0.0"
|
||||||
eslint-visitor-keys "^1.2.0"
|
eslint-visitor-keys "^1.2.0"
|
||||||
@@ -2213,7 +2572,6 @@ eslint@7.2.0:
|
|||||||
ignore "^4.0.6"
|
ignore "^4.0.6"
|
||||||
import-fresh "^3.0.0"
|
import-fresh "^3.0.0"
|
||||||
imurmurhash "^0.1.4"
|
imurmurhash "^0.1.4"
|
||||||
inquirer "^7.0.0"
|
|
||||||
is-glob "^4.0.0"
|
is-glob "^4.0.0"
|
||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
@@ -2433,13 +2791,6 @@ figures@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
|
|
||||||
figures@^3.0.0:
|
|
||||||
version "3.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
|
|
||||||
integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
|
|
||||||
dependencies:
|
|
||||||
escape-string-regexp "^1.0.5"
|
|
||||||
|
|
||||||
file-entry-cache@^5.0.1:
|
file-entry-cache@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
|
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
|
||||||
@@ -2710,6 +3061,11 @@ globals@^12.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.8.1"
|
type-fest "^0.8.1"
|
||||||
|
|
||||||
|
globalyzer@^0.1.0:
|
||||||
|
version "0.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.4.tgz#bc8e273afe1ac7c24eea8def5b802340c5cc534f"
|
||||||
|
integrity sha512-LeguVWaxgHN0MNbWC6YljNMzHkrCny9fzjmEUdnF1kQ7wATFD1RHFRqA1qxaX2tgxGENlcxjOflopBwj3YZiXA==
|
||||||
|
|
||||||
globby@^10.0.1:
|
globby@^10.0.1:
|
||||||
version "10.0.2"
|
version "10.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
|
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
|
||||||
@@ -2724,6 +3080,11 @@ globby@^10.0.1:
|
|||||||
merge2 "^1.2.3"
|
merge2 "^1.2.3"
|
||||||
slash "^3.0.0"
|
slash "^3.0.0"
|
||||||
|
|
||||||
|
globrex@^0.1.1:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
|
||||||
|
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||||
|
|
||||||
good-listener@^1.2.2:
|
good-listener@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
|
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
|
||||||
@@ -3043,25 +3404,6 @@ ini@^1.3.0, ini@^1.3.5, ini@~1.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||||
|
|
||||||
inquirer@^7.0.0:
|
|
||||||
version "7.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.2.0.tgz#63ce99d823090de7eb420e4bb05e6f3449aa389a"
|
|
||||||
integrity sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ==
|
|
||||||
dependencies:
|
|
||||||
ansi-escapes "^4.2.1"
|
|
||||||
chalk "^3.0.0"
|
|
||||||
cli-cursor "^3.1.0"
|
|
||||||
cli-width "^2.0.0"
|
|
||||||
external-editor "^3.0.3"
|
|
||||||
figures "^3.0.0"
|
|
||||||
lodash "^4.17.15"
|
|
||||||
mute-stream "0.0.8"
|
|
||||||
run-async "^2.4.0"
|
|
||||||
rxjs "^6.5.3"
|
|
||||||
string-width "^4.1.0"
|
|
||||||
strip-ansi "^6.0.0"
|
|
||||||
through "^2.3.6"
|
|
||||||
|
|
||||||
invert-kv@^1.0.0:
|
invert-kv@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||||
@@ -3851,6 +4193,11 @@ make-dir@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver "^6.0.0"
|
semver "^6.0.0"
|
||||||
|
|
||||||
|
make-error@^1.1.1:
|
||||||
|
version "1.3.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||||
|
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||||
|
|
||||||
map-cache@^0.2.2:
|
map-cache@^0.2.2:
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
||||||
@@ -4011,11 +4358,6 @@ mimic-fn@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||||
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
|
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
|
||||||
|
|
||||||
mimic-fn@^2.1.0:
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
|
||||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
|
||||||
|
|
||||||
mimic-response@^1.0.0, mimic-response@^1.0.1:
|
mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||||
@@ -4157,11 +4499,6 @@ mute-stream@0.0.7:
|
|||||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||||
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
||||||
|
|
||||||
mute-stream@0.0.8:
|
|
||||||
version "0.0.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
|
||||||
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
|
||||||
|
|
||||||
mz@^2.7.0:
|
mz@^2.7.0:
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
||||||
@@ -4434,13 +4771,6 @@ onetime@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn "^1.0.0"
|
mimic-fn "^1.0.0"
|
||||||
|
|
||||||
onetime@^5.1.0:
|
|
||||||
version "5.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5"
|
|
||||||
integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==
|
|
||||||
dependencies:
|
|
||||||
mimic-fn "^2.1.0"
|
|
||||||
|
|
||||||
only@~0.0.2:
|
only@~0.0.2:
|
||||||
version "0.0.2"
|
version "0.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4"
|
resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4"
|
||||||
@@ -5053,6 +5383,11 @@ readdirp@~3.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.4:
|
||||||
|
version "0.13.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
|
||||||
|
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
|
||||||
|
|
||||||
regex-not@^1.0.0, regex-not@^1.0.2:
|
regex-not@^1.0.0, regex-not@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
|
||||||
@@ -5061,7 +5396,7 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
|||||||
extend-shallow "^3.0.2"
|
extend-shallow "^3.0.2"
|
||||||
safe-regex "^1.1.0"
|
safe-regex "^1.1.0"
|
||||||
|
|
||||||
regexpp@^3.1.0:
|
regexpp@^3.0.0, regexpp@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
|
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
|
||||||
integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
|
integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
|
||||||
@@ -5133,7 +5468,7 @@ resolve-url@^0.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||||
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
|
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
|
||||||
|
|
||||||
resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0:
|
resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0:
|
||||||
version "1.17.0"
|
version "1.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
|
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
|
||||||
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
|
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
|
||||||
@@ -5162,14 +5497,6 @@ restore-cursor@^2.0.0:
|
|||||||
onetime "^2.0.0"
|
onetime "^2.0.0"
|
||||||
signal-exit "^3.0.2"
|
signal-exit "^3.0.2"
|
||||||
|
|
||||||
restore-cursor@^3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
|
|
||||||
integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
|
|
||||||
dependencies:
|
|
||||||
onetime "^5.1.0"
|
|
||||||
signal-exit "^3.0.2"
|
|
||||||
|
|
||||||
ret@~0.1.10:
|
ret@~0.1.10:
|
||||||
version "0.1.15"
|
version "0.1.15"
|
||||||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||||
@@ -5209,7 +5536,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
|||||||
hash-base "^3.0.0"
|
hash-base "^3.0.0"
|
||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
|
|
||||||
run-async@^2.2.0, run-async@^2.4.0:
|
run-async@^2.2.0:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
|
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
|
||||||
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
|
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
|
||||||
@@ -5226,7 +5553,7 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
aproba "^1.1.1"
|
aproba "^1.1.1"
|
||||||
|
|
||||||
rxjs@^6.4.0, rxjs@^6.5.3:
|
rxjs@^6.4.0:
|
||||||
version "6.6.0"
|
version "6.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84"
|
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84"
|
||||||
integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==
|
integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==
|
||||||
@@ -5777,7 +6104,7 @@ source-map-resolve@^0.5.0:
|
|||||||
source-map-url "^0.4.0"
|
source-map-url "^0.4.0"
|
||||||
urix "^0.1.0"
|
urix "^0.1.0"
|
||||||
|
|
||||||
source-map-support@^0.5.11, source-map-support@^0.5.19, source-map-support@^0.5.7, source-map-support@~0.5.12:
|
source-map-support@^0.5.11, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.7, source-map-support@~0.5.12:
|
||||||
version "0.5.19"
|
version "0.5.19"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||||
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
|
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
|
||||||
@@ -6258,6 +6585,14 @@ tiny-emitter@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
||||||
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
||||||
|
|
||||||
|
tiny-glob@^0.2.6:
|
||||||
|
version "0.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.6.tgz#9e056e169d9788fe8a734dfa1ff02e9b92ed7eda"
|
||||||
|
integrity sha512-A7ewMqPu1B5PWwC3m7KVgAu96Ch5LA0w4SnEN/LbDREj/gAD0nPWboRbn8YoP9ISZXqeNAlMvKSKoEuhcfK3Pw==
|
||||||
|
dependencies:
|
||||||
|
globalyzer "^0.1.0"
|
||||||
|
globrex "^0.1.1"
|
||||||
|
|
||||||
tmp@0.0.33, tmp@^0.0.33:
|
tmp@0.0.33, tmp@^0.0.33:
|
||||||
version "0.0.33"
|
version "0.0.33"
|
||||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||||
@@ -6336,6 +6671,22 @@ tree-kill@^1.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||||
|
|
||||||
|
ts-node@^8.10.2:
|
||||||
|
version "8.10.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d"
|
||||||
|
integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==
|
||||||
|
dependencies:
|
||||||
|
arg "^4.1.0"
|
||||||
|
diff "^4.0.1"
|
||||||
|
make-error "^1.1.1"
|
||||||
|
source-map-support "^0.5.17"
|
||||||
|
yn "3.1.1"
|
||||||
|
|
||||||
|
tsc@^1.20150623.0:
|
||||||
|
version "1.20150623.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsc/-/tsc-1.20150623.0.tgz#4ebc3c774e169148cbc768a7342533f082c7a6e5"
|
||||||
|
integrity sha1-Trw8d04WkUjLx2inNCUz8ILHpuU=
|
||||||
|
|
||||||
tsconfig-paths@^3.9.0:
|
tsconfig-paths@^3.9.0:
|
||||||
version "3.9.0"
|
version "3.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
|
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
|
||||||
@@ -6351,7 +6702,7 @@ tslib@1.11.1:
|
|||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
|
||||||
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
|
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
|
||||||
|
|
||||||
tslib@^1, tslib@^1.10.0, tslib@^1.11.1, tslib@^1.11.2, tslib@^1.13.0, tslib@^1.9.0, tslib@^1.9.3:
|
tslib@^1, tslib@^1.10.0, tslib@^1.11.1, tslib@^1.11.2, tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||||
version "1.13.0"
|
version "1.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
||||||
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
||||||
@@ -6366,6 +6717,13 @@ tsscmp@1.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb"
|
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb"
|
||||||
integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==
|
integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==
|
||||||
|
|
||||||
|
tsutils@^3.17.1:
|
||||||
|
version "3.17.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
|
||||||
|
integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==
|
||||||
|
dependencies:
|
||||||
|
tslib "^1.8.1"
|
||||||
|
|
||||||
tty-browserify@0.0.0:
|
tty-browserify@0.0.0:
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
||||||
@@ -6395,11 +6753,6 @@ type-check@~0.3.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls "~1.1.2"
|
prelude-ls "~1.1.2"
|
||||||
|
|
||||||
type-fest@^0.11.0:
|
|
||||||
version "0.11.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
|
|
||||||
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
|
|
||||||
|
|
||||||
type-fest@^0.8.1:
|
type-fest@^0.8.1:
|
||||||
version "0.8.1"
|
version "0.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||||
@@ -6425,6 +6778,11 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
|
typescript@^3.9.6:
|
||||||
|
version "3.9.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
|
||||||
|
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
|
||||||
|
|
||||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||||
@@ -6789,6 +7147,13 @@ xregexp@2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
|
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
|
||||||
integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=
|
integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=
|
||||||
|
|
||||||
|
xregexp@^4.2.4:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50"
|
||||||
|
integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime-corejs3" "^7.8.3"
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
@@ -6819,7 +7184,7 @@ yaml@^1.9.2:
|
|||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
|
||||||
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
|
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
|
||||||
|
|
||||||
yargs-parser@^18.1.1:
|
yargs-parser@^18.1.2:
|
||||||
version "18.1.3"
|
version "18.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||||
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
|
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
|
||||||
@@ -6827,13 +7192,13 @@ yargs-parser@^18.1.1:
|
|||||||
camelcase "^5.0.0"
|
camelcase "^5.0.0"
|
||||||
decamelize "^1.2.0"
|
decamelize "^1.2.0"
|
||||||
|
|
||||||
yargs@^15.3.1:
|
yargs@^15.4.0:
|
||||||
version "15.3.1"
|
version "15.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b"
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.0.tgz#53949fb768309bac1843de9b17b80051e9805ec2"
|
||||||
integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==
|
integrity sha512-D3fRFnZwLWp8jVAAhPZBsmeIHY8tTsb8ItV9KaAaopmC6wde2u6Yw29JBIZHXw14kgkRnYmDgmQU4FVMDlIsWw==
|
||||||
dependencies:
|
dependencies:
|
||||||
cliui "^6.0.0"
|
cliui "^6.0.0"
|
||||||
decamelize "^1.2.0"
|
decamelize "^3.2.0"
|
||||||
find-up "^4.1.0"
|
find-up "^4.1.0"
|
||||||
get-caller-file "^2.0.1"
|
get-caller-file "^2.0.1"
|
||||||
require-directory "^2.1.1"
|
require-directory "^2.1.1"
|
||||||
@@ -6842,7 +7207,7 @@ yargs@^15.3.1:
|
|||||||
string-width "^4.2.0"
|
string-width "^4.2.0"
|
||||||
which-module "^2.0.0"
|
which-module "^2.0.0"
|
||||||
y18n "^4.0.0"
|
y18n "^4.0.0"
|
||||||
yargs-parser "^18.1.1"
|
yargs-parser "^18.1.2"
|
||||||
|
|
||||||
yargs@^3.19.0:
|
yargs@^3.19.0:
|
||||||
version "3.32.0"
|
version "3.32.0"
|
||||||
@@ -6861,3 +7226,8 @@ ylru@^1.2.0:
|
|||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"
|
resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"
|
||||||
integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==
|
integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==
|
||||||
|
|
||||||
|
yn@3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||||
|
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||||
Reference in new issue
Block a user