修复 @param 在类扩展时作用域污染的问题,完善interface

This commit is contained in:
undefined committed 2020-07-11 13:35:28 +08:00
1 parent cd6938dd6c
commit dd7914e1fb
31 files changed
+269 -179

No files matched your search

-28
View File
@@ -1,28 +0,0 @@
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' {
let isFirstWorker: boolean;
}
export { };
-1
View File
@@ -98,7 +98,6 @@ export const DiscussionNodeNotFoundError = Err('DiscussionNodeNotFoundError', Do
export const InvalidOperationError = Err('InvalidOperationError', MethodNotAllowedError);
global.Hydro.error = {
Err,
HydroError,
BadRequestError,
BlacklistedError,
+149
View File
@@ -0,0 +1,149 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { ObjectID, GridFSBucket } from 'mongodb';
import fs from 'fs';
export interface Setting {
family: string,
key: string,
range: Array<[string, string]> | { [key: string]: string },
value: any,
type: string,
name: string,
desc: string,
flag: number,
}
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 interface TestCase {
time: number,
memory: number,
status: number,
message: string,
}
export interface Rdoc {
_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,
stdout?: string,
stderr?: string,
tid?: ObjectID,
ttype?: number,
}
export type ProblemImporter = (url: string, handler: any) =>
Promise<[Pdoc, fs.ReadStream?]> | [Pdoc, fs.ReadStream?];
export interface Script {
run: (args: any, report: Function) => any,
description: string,
validate: any,
}
declare global {
namespace NodeJS {
interface Global {
Hydro: {
model: {
blacklist: typeof import('./model/blacklist'),
builtin: typeof import('./model/builtin'),
contest: typeof import('./model/contest'),
discussion: typeof import('./model/discussion'),
document: typeof import('./model/document'),
domain: typeof import('./model/domain'),
file: typeof import('./model/file'),
message: typeof import('./model/message'),
opcount: typeof import('./model/opcount'),
problem: typeof import('./model/problem'),
record: typeof import('./model/record'),
setting: typeof import('./model/setting'),
solution: typeof import('./model/solution'),
system: typeof import('./model/system'),
task: typeof import('./model/task'),
token: typeof import('./model/token'),
training: typeof import('./model/training'),
user: typeof import('./model/user'),
[key: string]: any,
},
handler: { [key: string]: Function },
script: { [key: string]: Script },
service: {
bus: typeof import('./service/bus'),
db: typeof import('./service/db'),
gridfs: GridFSBucket,
monitor: typeof import('./service/monitor'),
server: typeof import('./service/server'),
},
lib: {
download: typeof import('./lib/download').default,
'hash.hydro': typeof import('./lib/hash.hydro').default,
hpm: typeof import('./lib/hpm'),
i18n: typeof import('./lib/i18n').default,
'import.syzoj': typeof import('./lib/import.syzoj').syzoj,
jwt: typeof import('./lib/jwt'),
logger: typeof import('./lib/logger').default,
mail: typeof import('./lib/mail'),
markdown: typeof import('./lib/markdown'),
md5: typeof import('./lib/md5').default,
misc: typeof import('./lib/misc'),
nav: typeof import('./lib/nav').default,
paginate: typeof import('./lib/paginate').default,
rank: typeof import('./lib/rank').default,
rating: typeof import('./lib/rating').default,
readConfig: typeof import('./lib/readConfig').default,
sha1: typeof import('./lib/sha1').default,
sysinfo: typeof import('./lib/sysinfo'),
template: typeof import('./lib/template'),
'testdata.convert.ini': typeof import('./lib/testdata.convert.ini').default,
useragent: typeof import('./lib/useragent'),
validator: typeof import('./lib/validator'),
[key: string]: any
},
stat: any,
wiki: { [category: string]: { [page: string]: any } },
template: { [key: string]: string },
ui: any,
error: typeof import('./error'),
locales: { [id: string]: { [key: string]: string } },
},
nodeModules: any,
onDestory: Function[],
}
}
}
declare module 'cluster' {
let isFirstWorker: boolean;
}
+1 -1
View File
@@ -11,7 +11,7 @@ async function _download(url: string, path: string, retry: number) {
return path;
}
export default function download(url: string, path: string | undefined | null, retry = 3) {
export default function download(url: string, path?: string, retry = 3) {
if (path) return _download(url, path, retry);
return superagent.get(url).retry(retry);
}
+71
View File
@@ -0,0 +1,71 @@
import assert from 'assert';
import superagent from 'superagent';
import download from './download';
import { ValidationError, RemoteOnlineJudgeError } from '../error';
const RE_SYZOJ = /https?:\/\/([a-zA-Z0-9.]+)\/problem\/([0-9]+)\/?/i;
export async function syzoj(url: string, handler: any) {
assert(url.match(RE_SYZOJ), new ValidationError('url'));
if (!url.endsWith('/')) url += '/';
const [, host, pid] = RE_SYZOJ.exec(url);
const res = await superagent.get(`${url}export`);
assert(res.status === 200, new RemoteOnlineJudgeError('Cannot connect to target server'));
assert(res.body.success, new RemoteOnlineJudgeError((res.body.error || {}).message));
const p = res.body.obj;
const content = [];
if (p.description) {
content.push(
handler.translate('problem.import.problem_description'),
p.description,
);
}
if (p.input_format) {
content.push(
handler.translate('problem.import.input_format'),
p.input_format,
);
}
if (p.output_format) {
content.push(
handler.translate('problem.import.output_format'),
p.output_format,
);
}
if (p.hint) {
content.push(
handler.translate('problem.import.hint'),
p.hint,
);
}
if (p.limit_and_hint) {
content.push(
handler.translate('problem.import.limit_and_hint'),
p.limit_and_hint,
);
}
if (p.have_additional_file) {
content.push(
handler.translate('problem.import.additional_file'),
`${url}download/additional_file`,
);
}
const pdoc = {
title: p.title,
content: content.join(' \n'),
owner: handler.user._id,
from: url,
pid: `${host}_${pid}`,
tags: p.tags,
config: {
time: p.time_limit,
memory: p.memory_limit * 1024,
filename: p.file_io_input_name,
type: p.type,
},
};
const r = download(`${url}testdata/download`);
return [pdoc, r];
}
global.Hydro.lib['import.syzoj'] = syzoj;
+1 -1
View File
@@ -56,7 +56,7 @@ export function size(s, base = 1) {
return '{0} {1}'.format(Math.round(s * unit), unitNames[unitNames.length - 1]);
}
export function _digit2(number: number) {
function _digit2(number: number) {
if (number < 10) return `0${number}`;
return number.toString();
}
+1
View File
@@ -17,4 +17,5 @@ async function readConfig(filePath: string) {
}
global.Hydro.lib.readConfig = readConfig;
export default readConfig;
-1
View File
@@ -9,4 +9,3 @@ export function icon(str: string) {
}
global.Hydro.lib.useragent = { parse, icon };
export default { parse, icon };
+5 -1
View File
@@ -2,20 +2,24 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-eval */
import './declare';
import './interface';
import cluster from 'cluster';
import { argv } from 'yargs';
global.Hydro = {
stat: { reqCount: 0 },
handler: {},
// @ts-ignore
service: {},
// @ts-ignore
model: {},
script: {},
// @ts-ignore
lib: {},
wiki: {},
template: {},
ui: {},
// @ts-ignore
error: {},
locales: {},
};
+1
View File
@@ -609,6 +609,7 @@ export const USER_GENDER_ICONS = {
};
global.Hydro.model.builtin = {
Permission,
PERM,
PERMS,
PERMS_BY_FAMILY,
+1 -1
View File
@@ -531,7 +531,7 @@ export function getMulti(domainId: string, query = {}, type = document.TYPE_CONT
return document.getMulti(domainId, type, query);
}
export function _getStatusJournal(tsdoc) {
function _getStatusJournal(tsdoc) {
return tsdoc.journal.sort((a, b) => (a.rid.generationTime - b.rid.generationTime));
}
+1
View File
@@ -222,6 +222,7 @@ global.Hydro.model.discussion = {
setStar,
getStatus,
addNode,
getNode,
getNodes,
getVnode,
getListVnodes,
-2
View File
@@ -417,5 +417,3 @@ global.Hydro.model.document = {
TYPE_FILE,
TYPE_TRAINING,
};
export default global.Hydro.model.document;
+2 -17
View File
@@ -3,26 +3,10 @@ import { STATUS } from './builtin';
import * as file from './file';
import * as document from './document';
import * as domain from './domain';
import { Pdoc } from '../interface';
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',
@@ -180,6 +164,7 @@ export async function setTestdata(domainId: string, _id: number, filePath: strin
}
global.Hydro.model.problem = {
pdocHidden,
add,
inc,
get,
+9 -37
View File
@@ -1,45 +1,15 @@
import _ from 'lodash';
import yaml from 'js-yaml';
import { ObjectID, FilterQuery } from 'mongodb';
import { ObjectID } from 'mongodb';
import { STATUS } from './builtin';
import * as task from './task';
import * as problem from './problem';
import { Rdoc, TestCase } from '../interface';
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,
@@ -119,7 +89,7 @@ export async function add(domainId: string, data: RdocBase, addTask = true) {
return res.insertedId;
}
export async function get(domainId: string, _id: ObjectID) {
export async function get(domainId: string, _id: ObjectID): Promise<Rdoc> {
const rdoc = await coll.findOne({ domainId, _id });
if (!rdoc) throw new RecordNotFoundError(_id);
return rdoc;
@@ -132,7 +102,7 @@ export function getMulti(domainId: string, query: any) {
export async function update(
domainId: string, _id: ObjectID,
$set: any = {}, $push: any = {}, $unset: any = {},
) {
): Promise<Rdoc> {
const $update: any = {};
if ($set && Object.keys($set).length) $update.$set = $set;
if ($push && Object.keys($push).length) $update.$push = $push;
@@ -157,11 +127,13 @@ export function reset(domainId: string, rid: ObjectID) {
});
}
export function count<T>(domainId: string, query: FilterQuery<T>) {
export function count(domainId: string, query: any) {
return coll.find({ domainId, ...query }).count();
}
export async function getList(domainId: string, rids: ObjectID[], showHidden = false) {
export async function getList(
domainId: string, rids: ObjectID[], showHidden = false,
): Promise<{ [key: string]: Rdoc }> {
const r = {};
for (const rid of rids) {
// eslint-disable-next-line no-await-in-loop
@@ -184,7 +156,7 @@ export function getUserInProblemMulti(
return coll.find({ domainId, uid, pid });
}
export function getByUid(domainId: string, uid: number) {
export function getByUid(domainId: string, uid: number): Promise<Rdoc[]> {
return coll.find({ domainId, uid }).toArray();
}
+11 -10
View File
@@ -1,8 +1,9 @@
import moment from 'moment-timezone';
import * as builtin from './builtin';
import { Setting as _Setting } from '../interface';
const countries = moment.tz.countries();
const tzs = new Set();
const tzs: Set<string> = new Set();
for (const country of countries) {
const tz = moment.tz.zonesForCountry(country);
for (const t of tz) tzs.add(t);
@@ -25,40 +26,40 @@ export const DOMAIN_SETTINGS_BY_KEY = {};
export const SYSTEM_SETTINGS_BY_KEY = {};
export const Setting = (
family, key, range = null,
value = null, type = 'text', name = '',
family: string, key: string, range: Array<[string, string]> | { [key: string]: string } = null,
value: any = null, type = 'text', name = '',
desc = '', flag = 0,
) => ({
): _Setting => ({
family, key, range, value, type, name, desc, flag,
});
export const PreferenceSetting = (...settings) => {
export const PreferenceSetting = (...settings: _Setting[]) => {
for (const setting of settings) {
PREFERENCE_SETTINGS.push(setting);
SETTINGS.push(setting);
SETTINGS_BY_KEY[setting.key] = setting;
}
};
export const AccountSetting = (...settings) => {
export const AccountSetting = (...settings: _Setting[]) => {
for (const setting of settings) {
ACCOUNT_SETTINGS.push(setting);
SETTINGS.push(setting);
SETTINGS_BY_KEY[setting.key] = setting;
}
};
export const DomainUserSetting = (...settings) => {
export const DomainUserSetting = (...settings: _Setting[]) => {
for (const setting of settings) {
DOMAIN_USER_SETTINGS.push(setting);
DOMAIN_USER_SETTINGS_BY_KEY[setting.key] = setting;
}
};
export const DomainSetting = (...settings) => {
export const DomainSetting = (...settings: _Setting[]) => {
for (const setting of settings) {
DOMAIN_SETTINGS.push(setting);
DOMAIN_SETTINGS_BY_KEY[setting.key] = setting;
}
};
export const SystemSetting = (...settings) => {
export const SystemSetting = (...settings: _Setting[]) => {
for (const setting of settings) {
SYSTEM_SETTINGS.push(setting);
SYSTEM_SETTINGS_BY_KEY[setting.key] = setting;
@@ -69,7 +70,7 @@ PreferenceSetting(
// TODO generate by global.Hydro.locales
Setting('setting_display', 'viewLang', builtin.VIEW_LANGS.map((i) => [i.code, i.name]),
'zh_CN', 'select', 'UI Language'),
Setting('setting_display', 'timezone', timezones,
Setting('setting_display', 'timezone', timezones as [string, string][],
'Asia/Shanghai', 'select', 'Timezone'),
Setting('setting_usage', 'codeLang', builtin.LANG_TEXTS,
null, 'select', 'Default Code Language'),
-6
View File
@@ -4,12 +4,6 @@ import * as db from '../service/db';
const coll = db.collection('task');
export interface Task {
_id: ObjectID,
count: number,
executeAfter: number,
}
export async function add(task: any) {
const t = { ...task };
if (typeof t.executeAfter === 'object') t.executeAfter = t.executeAfter.getTime();
+1
View File
@@ -90,6 +90,7 @@ global.Hydro.model.token = {
TYPE_CSRF_TOKEN,
TYPE_OAUTH,
TYPE_REGISTRATION,
TYPE_LOSTPASS,
ensureIndexes,
add,
+1
View File
@@ -304,6 +304,7 @@ export function ensureIndexes() {
}
global.Hydro.model.user = {
User,
changePassword,
create,
getByEmail,
-2
View File
@@ -43,5 +43,3 @@ export function publish(event, payload, isMaster = true) {
global.Hydro.service.bus = {
subscribe, unsubscribe, publish,
};
export default global.Hydro.service.bus;
+1 -3
View File
@@ -19,6 +19,4 @@ export function collection(c: string) {
return db.collection(c);
}
global.Hydro.service.db = { collection };
export default { collection };
global.Hydro.service.db = { collection, db };
+3 -3
View File
@@ -1,8 +1,8 @@
import { GridFSBucket } from 'mongodb';
import { db } from './db';
const exp = new GridFSBucket(db);
export const fs = new GridFSBucket(db);
global.Hydro.service.gridfs = exp;
global.Hydro.service.gridfs = fs;
export default exp;
export default fs;
+3 -5
View File
@@ -3,7 +3,7 @@ import * as sysinfo from '../lib/sysinfo';
const coll = db.collection('status');
async function update() {
export async function update() {
const [mid, $set] = await sysinfo.update();
await coll.updateOne(
{ mid, type: 'server' },
@@ -13,7 +13,7 @@ async function update() {
global.Hydro.stat.reqCount = 0;
}
async function postInit() {
export async function postInit() {
const info = await sysinfo.get();
await coll.updateOne(
{ mid: info.mid, type: 'server' },
@@ -23,6 +23,4 @@ async function postInit() {
setInterval(update, 60 * 1000);
}
global.Hydro.service.monitor = { postInit };
export default { postInit };
global.Hydro.service.monitor = { update, postInit };
+5 -4
View File
@@ -138,12 +138,13 @@ export function param(...args: any): MethodDecorator {
}
return function desc(target: any, funcName: string, obj: any) {
if (!target.__param) target.__param = {};
if (!target.__param[funcName]) {
target.__param[funcName] = [{ name: 'domainId', type: 'string' }];
if (!target.__param[target.constructor.name]) target.__param[target.constructor.name] = {};
if (!target.__param[target.constructor.name][funcName]) {
target.__param[target.constructor.name][funcName] = [{ name: 'domainId', type: 'string' }];
const originalMethod = obj.value;
obj.value = function func(rawArgs: any) {
const c = [];
const arglist: ParamOption[] = this.__param[funcName];
const arglist: ParamOption[] = this.__param[target.constructor.name][funcName];
for (const item of arglist) {
if (!item.isOptional || rawArgs[item.name]) {
if (!rawArgs[item.name]) throw new ValidationError(item.name);
@@ -160,7 +161,7 @@ export function param(...args: any): MethodDecorator {
return originalMethod.call(this, ...c);
};
}
target.__param[funcName].splice(1, 0, ...d);
target.__param[target.constructor.name][funcName].splice(1, 0, ...d);
return obj;
};
}