workspace: update eslint rule (#1027)

This commit is contained in:
undefined authored and GitHub committed 2025-08-06 18:51:25 +01:00
1 parent 05c3b8f2bd
commit cee3c00ddb
150 files changed
+1069 -1043

No files matched your search

+2 -1
View File
@@ -1,3 +1,4 @@
/* eslint-disable ts/no-unsafe-declaration-merging */
import LoggerService from '@cordisjs/plugin-logger';
import { TimerService } from '@cordisjs/plugin-timer';
import * as cordis from 'cordis';
@@ -25,7 +26,7 @@ function provideModule<T extends keyof ModuleInterfaces>(type: T, id: string, mo
export type Fiber = cordis.Fiber<Context>;
export { Disposable, Plugin, FiberState } from 'cordis';
export { Disposable, FiberState, Plugin } from 'cordis';
export interface Context extends cordis.Context {
[Context.events]: EventMap & cordis.Events<Context>;
+2 -2
View File
@@ -13,8 +13,8 @@ import {
const argv = cac().parse();
const tmpdir = path.resolve(os.tmpdir(), 'hydro');
const COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
const ARR = /=>.*$/mg;
const COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
const ARR = /=>.*$/gm;
function parseParameters(fn: Function) {
const code = fn.toString()
.replace(COMMENTS, '')
-1
View File
@@ -1,4 +1,3 @@
/* eslint-disable import/no-dynamic-require */
/* eslint-disable no-await-in-loop */
import '../lib/index';
+7 -7
View File
@@ -42,7 +42,7 @@ export class ContestListHandler extends Handler {
if (group && !groups.includes(group)) throw new NotAssignedError(group);
const rules = Object.keys(contest.RULES).filter((i) => !contest.RULES[i].hidden);
const escaped = escapeRegExp(q.toLowerCase());
const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi');
const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gim');
const filter = {
...(this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) && !group)
? {}
@@ -626,10 +626,10 @@ export class ContestBalloonHandler extends ContestManagementBaseHandler {
}
}
type BuiltinInput = {
interface BuiltinInput {
tdoc: Tdoc;
groups: any[];
};
}
type AnyFunction = (...args: any) => any;
type ParseArgs<T extends { [key: string]: keyof BuiltinInput | AnyFunction | Type<any> }> = {
[key in keyof T]: T[key] extends keyof BuiltinInput ? BuiltinInput[T[key]] : T[key] extends AnyFunction ? ReturnType<T[key]> : any
@@ -696,9 +696,9 @@ class ScoreboardService extends Service {
addView<T extends { [key: string]: keyof BuiltinInput | AnyFunction | Type<any> }>(
id: string, name: string, args: T,
{ display, supportedRules, cacheTime }: {
display: (this: ContestScoreboardHandler, args: ParseArgs<T>) => Promise<void>,
supportedRules: string[],
cacheTime?: number,
display: (this: ContestScoreboardHandler, args: ParseArgs<T>) => Promise<void>;
supportedRules: string[];
cacheTime?: number;
},
) {
if (this.views[id]) throw new Error(`View ${id} already exists`);
@@ -766,7 +766,7 @@ export async function apply(ctx: Context) {
config.lockAt = this.tdoc.lockAt;
}
const [, rows, udict, pdict] = await contest.getScoreboard.call(this, tdoc.domainId, tdoc._id, config);
// eslint-disable-next-line @typescript-eslint/naming-convention
// eslint-disable-next-line ts/naming-convention
const page_name = tdoc.rule === 'homework'
? 'homework_scoreboard'
: 'contest_scoreboard';
+1 -1
View File
@@ -95,7 +95,7 @@ class DiscussionNodeHandler extends DiscussionHandler {
async get(domainId: string, type: string, _name: string, page = 1) {
let name: ObjectId | string | number;
if (ObjectId.isValid(_name)) name = new ObjectId(_name);
else if (isSafeInteger(parseInt(_name, 10))) name = parseInt(_name, 10);
else if (isSafeInteger(Number.parseInt(_name, 10))) name = Number.parseInt(_name, 10);
else name = _name;
const hidden = this.user.own(this.vnode) || this.user.hasPerm(PERM.PERM_EDIT_DISCUSSION) ? {} : { hidden: false };
const [ddocs, dpcount] = await this.paginate(
+1 -1
View File
@@ -534,7 +534,7 @@ class HomeDomainCreateHandler extends Handler {
@param('name', Types.Title)
@param('bulletin', Types.Content)
@param('avatar', Types.Content, true)
// eslint-disable-next-line @typescript-eslint/no-shadow
// eslint-disable-next-line ts/no-shadow
async post(_: string, id: string, name: string, bulletin: string, avatar: string) {
const doc = await domain.get(id);
if (doc) throw new DomainAlreadyExistsError(id);
+1 -1
View File
@@ -52,7 +52,7 @@ class HomeworkMainHandler extends Handler {
],
},
...group ? { assign: { $in: [group] } } : {},
...q ? { title: { $regex: new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi') } } : {},
...q ? { title: { $regex: new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gim') } } : {},
}).sort({
penaltySince: -1, endAt: -1, beginAt: -1, _id: -1,
});
+1 -1
View File
@@ -79,7 +79,7 @@ export class JudgeResultCallbackContext {
private relatedId = new ObjectId();
private meta: { rejudge?: JudgeMeta['rejudge'] };
constructor(public ctx: Context, public readonly task: Omit<Task, '_id'> & { type: string }) { // eslint-disable-line @typescript-eslint/no-shadow
constructor(public ctx: Context, public readonly task: Omit<Task, '_id'> & { type: string }) { // eslint-disable-line ts/no-shadow
this.meta = task.meta as JudgeMeta || {};
this.finishPromise = new Promise((resolve) => {
this.resolve = resolve;
+1 -1
View File
@@ -239,7 +239,7 @@ class SystemUserImportHandler extends SystemHandler {
@param('draft', Types.Boolean)
async post(domainId: string, _users: string, draft: boolean) {
const users = _users.split('\n');
const udocs: { email: string, username: string, password: string, displayName?: string, [key: string]: any; }[] = [];
const udocs: { email: string, username: string, password: string, displayName?: string, [key: string]: any }[] = [];
const messages = [];
const mapping = Object.create(null);
const groups: Record<string, string[]> = Object.create(null);
+6 -6
View File
@@ -56,7 +56,7 @@ function buildQuery(udoc: User) {
const defaultSearch = async (domainId: string, q: string, options?: ProblemSearchOptions) => {
const escaped = escapeRegExp(q.toLowerCase());
const projection: (keyof ProblemDoc)[] = ['domainId', 'docId', 'pid'];
const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi');
const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gim');
const filter = { $or: [{ pid: { $regex } }, { title: { $regex } }, { tag: q }] };
const pdocs = await problem.getMulti(domainId, filter, projection)
.skip(options.skip || 0).limit(options.limit || system.get('pagination.problem')).toArray();
@@ -109,7 +109,7 @@ export class ProblemMainHandler extends Handler {
this.response.template = 'problem_main.html';
if (!limit || limit > this.ctx.setting.get('pagination.problem') || page > 1) limit = this.ctx.setting.get('pagination.problem');
this.queryContext.query = buildQuery(this.user);
// eslint-disable-next-line @typescript-eslint/no-shadow
// eslint-disable-next-line ts/no-shadow
const query = this.queryContext.query;
const psdict = {};
const search = Object.values(global.Hydro.module.problemSearch)[0] || defaultSearch;
@@ -607,7 +607,7 @@ export class ProblemEditHandler extends ProblemManageHandler {
@route('pid', Types.ProblemId)
@post('title', Types.Title)
@post('content', Types.Content)
@post('pid', Types.ProblemId, true, (i) => /^([a-zA-Z0-9]{1,10}-)?[a-zA-Z]+[a-zA-Z0-9]*$/i.test(i))
@post('pid', Types.ProblemId, true, (i) => /^(?:[a-z0-9]{1,10}-)?[a-z][a-z0-9]*$/i.test(i))
@post('hidden', Types.Boolean)
@post('tag', Types.Content, true, null, parseCategory)
@post('difficulty', Types.PositiveInt, (i) => +i <= 10, true)
@@ -837,7 +837,7 @@ export class ProblemSolutionHandler extends ProblemDetailHandler {
if (!accepted || !this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION_ACCEPT)) {
this.checkPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION);
}
// eslint-disable-next-line prefer-const
let [psdocs, pcount, pscount] = await this.paginate(
solution.getMulti(domainId, this.pdoc.docId),
page,
@@ -995,7 +995,7 @@ export class ProblemCreateHandler extends Handler {
@post('title', Types.Title)
@post('content', Types.Content)
@post('pid', Types.ProblemId, true, (i) => /^([a-zA-Z0-9]{1,10}-)?[a-zA-Z]+[a-zA-Z0-9]*$/i.test(i))
@post('pid', Types.ProblemId, true, (i) => /^(?:[a-z0-9]{1,10}-)?[a-z][a-z0-9]*$/i.test(i))
@post('hidden', Types.Boolean)
@post('difficulty', Types.PositiveInt, (i) => +i <= 10, true)
@post('tag', Types.Content, true, null, parseCategory)
@@ -1006,7 +1006,7 @@ export class ProblemCreateHandler extends Handler {
if (typeof pid !== 'string') pid = `P${pid}`;
if (pid && await problem.get(domainId, pid)) throw new ProblemAlreadyExistError(pid);
const docId = await problem.add(domainId, pid, title, content, this.user._id, tag ?? [], { hidden, difficulty });
const files = new Set(Array.from(content.matchAll(/file:\/\/([a-zA-Z0-9_-]+\.[a-zA-Z0-9]+)/g)).map((i) => i[1]));
const files = new Set(Array.from(content.matchAll(/file:\/\/([\w-]+\.[a-zA-Z0-9]+)/g)).map((i) => i[1]));
const tasks = [];
for (const file of files) {
if (this.user._files.find((i) => i.name === file)) {
+1 -2
View File
@@ -70,7 +70,7 @@ class RecordListHandler extends ContestDetailBaseHandler {
}
if (pid) {
if (typeof pid === 'string' && tdoc && /^[A-Z]$/.test(pid)) {
pid = tdoc.pids[parseInt(pid, 36) - 10];
pid = tdoc.pids[Number.parseInt(pid, 36) - 10];
}
const pdoc = await problem.get(domainId, pid);
if (pdoc) q.pid = pdoc.docId;
@@ -341,7 +341,6 @@ class RecordMainConnectionHandler extends ConnectionHandler {
if (typeof this.pid === 'number' && rdoc.pid !== this.pid) return;
if (typeof this.uid === 'number' && rdoc.uid !== this.uid) return;
// eslint-disable-next-line prefer-const
let [udoc, pdoc] = await Promise.all([
user.getById(this.args.domainId, rdoc.uid),
problem.get(rdoc.domainId, rdoc.pid),
+1 -1
View File
@@ -1,4 +1,4 @@
const versionNum = +process.version.replace(/v/gim, '').split('.')[0];
const versionNum = +process.version.replace(/v/gi, '').split('.')[0];
if (versionNum < 22) throw new Error('NodeJS >=22 required');
console.log('Process', process.pid, 'running as', process.env.NODE_APP_INSTANCE === '0' ? 'master' : 'worker');
+114 -113
View File
@@ -1,3 +1,4 @@
/* eslint-disable perfectionist/sort-exports */
import type { AttestationFormat, CredentialDeviceType } from '@simplewebauthn/server';
import type { ParsedAuthenticatorData } from '@simplewebauthn/server/helpers';
import type fs from 'fs';
@@ -16,43 +17,43 @@ export * from '@hydrooj/common/types';
type document = typeof import('./model/document');
export interface System {
_id: string,
value: any,
_id: string;
value: any;
}
export interface SystemKeys {
'smtp.user': string,
'smtp.from': string,
'smtp.pass': string,
'smtp.host': string,
'smtp.port': number,
'smtp.secure': boolean,
'installid': string,
'server.name': string,
'server.url': string,
'server.xff': string,
'server.xhost': string,
'server.host': string,
'server.port': number,
'server.language': string,
'limit.problem_files_max': number,
'problem.categories': string,
'session.keys': string[],
'session.saved_expire_seconds': number,
'session.unsaved_expire_seconds': number,
'user.quota': number,
'smtp.user': string;
'smtp.from': string;
'smtp.pass': string;
'smtp.host': string;
'smtp.port': number;
'smtp.secure': boolean;
installid: string;
'server.name': string;
'server.url': string;
'server.xff': string;
'server.xhost': string;
'server.host': string;
'server.port': number;
'server.language': string;
'limit.problem_files_max': number;
'problem.categories': string;
'session.keys': string[];
'session.saved_expire_seconds': number;
'session.unsaved_expire_seconds': number;
'user.quota': number;
}
export interface Setting {
family: string,
key: string,
range: [string, string][] | Record<string, string>,
value: any,
type: string,
subType?: string,
name: string,
desc: string,
flag: number,
family: string;
key: string;
range: [string, string][] | Record<string, string>;
value: any;
type: string;
subType?: string;
name: string;
desc: string;
flag: number;
}
export interface Authenticator {
@@ -119,7 +120,7 @@ export interface UserPreferenceDoc {
content: string;
}
export type ownerInfo = { owner: number, maintainer?: number[] };
export interface OwnerInfo { owner: number, maintainer?: number[] }
export type User = import('./model/user').User;
export type Udict = Record<number, User>;
@@ -190,11 +191,11 @@ export type { ProblemDoc } from './model/problem';
export type ProblemDict = NumericDictionary<ProblemDoc>;
export interface StatusDocBase {
_id: ObjectId,
docId: any,
docType: number,
domainId: string,
uid: number,
_id: ObjectId;
docId: any;
docType: number;
domainId: string;
uid: number;
}
export interface ProblemStatusDoc extends StatusDocBase {
@@ -241,10 +242,10 @@ export type ScoreboardRow = ScoreboardNode[] & { raw?: any };
export type PenaltyRules = Dictionary<number>;
export interface TrainingNode {
_id: number,
title: string,
requireNids: number[],
pids: number[],
_id: number;
title: string;
requireNids: number[];
pids: number[];
}
export interface Tdoc extends Document {
@@ -287,28 +288,28 @@ export interface Tdoc extends Document {
}
export interface TrainingDoc extends Omit<Tdoc, 'docType'> {
docType: document['TYPE_TRAINING'],
docType: document['TYPE_TRAINING'];
description: string;
pin?: number;
dag: TrainingNode[];
}
export interface DomainDoc extends Record<string, any> {
_id: string,
owner: number,
roles: Dictionary<string>,
avatar: string,
bulletin: string,
_join?: any,
host?: string[],
_id: string;
owner: number;
roles: Dictionary<string>;
avatar: string;
bulletin: string;
_join?: any;
host?: string[];
}
// Message
export interface MessageDoc {
from: number,
to: number,
content: string,
flag: number,
from: number;
to: number;
content: string;
flag: number;
}
// Blacklist
@@ -382,22 +383,22 @@ export interface ContestClarificationDoc extends Document {
}
export interface TokenDoc {
_id: string,
tokenType: number,
createAt: Date,
updateAt: Date,
expireAt: Date,
[key: string]: any,
_id: string;
tokenType: number;
createAt: Date;
updateAt: Date;
expireAt: Date;
[key: string]: any;
}
export interface OplogDoc extends Record<string, any> {
_id: ObjectId,
type: string,
_id: ObjectId;
type: string;
}
export interface ContestStat extends Record<string, any> {
detail: Record<number, Record<string, any>>,
unrank?: boolean,
detail: Record<number, Record<string, any>>;
unrank?: boolean;
}
export interface ScoreboardConfig {
@@ -438,9 +439,9 @@ export type ContestRules = Dictionary<ContestRule>;
export type ProblemImporter = (url: string, handler: any) => Promise<[ProblemDoc, fs.ReadStream?]> | [ProblemDoc, fs.ReadStream?];
export interface Script {
run: (args: any, report: Function) => any,
description: string,
validate: any,
run: (args: any, report: Function) => any;
description: string;
validate: any;
}
export interface Task {
@@ -528,66 +529,66 @@ export interface LockDoc {
declare module './service/db' {
interface Collections {
'blacklist': BlacklistDoc;
'domain': DomainDoc;
blacklist: BlacklistDoc;
domain: DomainDoc;
'domain.user': any;
'record': RecordDoc;
record: RecordDoc;
'record.stat': RecordStatDoc;
'record.history': RecordHistoryDoc;
'document': any;
document: any;
'document.status': StatusDocBase & {
[K in keyof DocStatusType]: { docType: K } & DocStatusType[K];
}[keyof DocStatusType];
'discussion.history': DiscussionHistoryDoc;
'user': Udoc;
user: Udoc;
'user.preference': UserPreferenceDoc;
'vuser': VUdoc;
vuser: VUdoc;
'user.group': GDoc;
'check': System;
'message': MessageDoc;
'token': TokenDoc;
'status': any;
'oauth': OauthMap;
'system': System;
'task': Task;
'storage': FileNode;
'oplog': OplogDoc;
'event': EventDoc;
'opcount': OpCountDoc;
'schedule': Schedule;
check: System;
message: MessageDoc;
token: TokenDoc;
status: any;
oauth: OauthMap;
system: System;
task: Task;
storage: FileNode;
oplog: OplogDoc;
event: EventDoc;
opcount: OpCountDoc;
schedule: Schedule;
'contest.balloon': ContestBalloonDoc;
'lock': LockDoc;
lock: LockDoc;
}
}
export interface Model {
blacklist: typeof import('./model/blacklist').default,
builtin: typeof import('./model/builtin'),
contest: typeof import('./model/contest'),
discussion: typeof import('./model/discussion'),
document: Omit<typeof import('./model/document'), 'apply'>,
domain: typeof import('./model/domain').default,
message: typeof import('./model/message').default,
opcount: typeof import('./model/opcount'),
problem: typeof import('./model/problem').default,
record: typeof import('./model/record').default,
setting: typeof import('./model/setting'),
solution: typeof import('./model/solution').default,
system: typeof import('./model/system'),
task: typeof import('./model/task').default,
blacklist: typeof import('./model/blacklist').default;
builtin: typeof import('./model/builtin');
contest: typeof import('./model/contest');
discussion: typeof import('./model/discussion');
document: Omit<typeof import('./model/document'), 'apply'>;
domain: typeof import('./model/domain').default;
message: typeof import('./model/message').default;
opcount: typeof import('./model/opcount');
problem: typeof import('./model/problem').default;
record: typeof import('./model/record').default;
setting: typeof import('./model/setting');
solution: typeof import('./model/solution').default;
system: typeof import('./model/system');
task: typeof import('./model/task').default;
schedule: typeof import('./model/schedule').default;
oplog: typeof import('./model/oplog'),
token: typeof import('./model/token').default,
training: typeof import('./model/training'),
user: typeof import('./model/user').default,
oauth: typeof import('./model/oauth').default,
storage: typeof import('./model/storage').default,
rp: typeof import('./script/rating').RpTypes,
oplog: typeof import('./model/oplog');
token: typeof import('./model/token').default;
training: typeof import('./model/training');
user: typeof import('./model/user').default;
oauth: typeof import('./model/oauth').default;
storage: typeof import('./model/storage').default;
rp: typeof import('./script/rating').RpTypes;
}
export interface GeoIP {
provider: string,
lookup: (ip: string, locale?: string) => any,
provider: string;
lookup: (ip: string, locale?: string) => any;
}
export interface ProblemSearchResponse {
@@ -604,9 +605,9 @@ export type ProblemSearch = (domainId: string, q: string, options?: ProblemSearc
export type UIInjectableFields = 'ProblemAdd' | 'Notification' | 'Nav' | 'UserDropdown' | 'DomainManage' | 'ControlPanel';
export interface UI {
nodes: Record<UIInjectableFields, any[]>,
getNodes: typeof import('./lib/ui').getNodes,
inject: typeof import('./lib/ui').inject,
nodes: Record<UIInjectableFields, any[]>;
getNodes: typeof import('./lib/ui').getNodes;
inject: typeof import('./lib/ui').inject;
}
export interface ModuleInterfaces {
@@ -629,8 +630,8 @@ export interface HydroGlobal {
declare global {
namespace NodeJS {
interface Global {
Hydro: HydroGlobal,
addons: string[],
Hydro: HydroGlobal;
addons: string[];
}
}
/** @deprecated */
+1 -1
View File
@@ -6,7 +6,7 @@ type AvatarProvider = (src: string, size: number) => string;
export const providers: Record<string, AvatarProvider> = {
gravatar: (email, size) => `${global.Hydro.model.system.get('avatar.gravatar_url')}`
+ `${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${size || 32}`,
qq: (id) => `//q1.qlogo.cn/g?b=qq&nk=${(/(\d+)/g.exec(id) || ['', ''])[1]}&s=160`,
qq: (id) => `//q1.qlogo.cn/g?b=qq&nk=${(/(\d+)/.exec(id) || ['', ''])[1]}&s=160`,
github: (id, size) => `//github.com/${id}.png?size=${Math.min(size, 460)}`,
url: (url) => url,
};
+7 -7
View File
@@ -1,11 +1,11 @@
export interface ProblemSource {
description?: string,
input?: string,
output?: string,
samples?: [string, string][],
samplesRaw?: string,
hint?: string,
source?: string,
description?: string;
input?: string;
output?: string;
samples?: [string, string][];
samplesRaw?: string;
hint?: string;
source?: string;
}
export function buildContent(source: ProblemSource, type: 'markdown' | 'html' = 'markdown', translate?: (s: string) => string) {
+5 -5
View File
@@ -91,14 +91,14 @@ class RatingCalculator {
}
interface RatingInputUser {
old: number,
uid: number,
rank: number,
old: number;
uid: number;
rank: number;
}
interface RatingOutputUser {
new: number,
uid: number,
new: number;
uid: number;
}
function calculate(users: RatingInputUser[]): RatingOutputUser[] {
+4 -4
View File
@@ -5,13 +5,13 @@ import { ObjectId as ObjectID } from 'mongodb';
import Schema from 'schemastery';
import superagent from 'superagent';
import { Context } from './context';
export { ObjectId, Filter } from 'mongodb';
export { WebSocket, WebSocketServer } from '@hydrooj/framework';
export * from './utils';
export * from './interface';
export * from './typeutils';
export * from './utils';
export { WebSocket, WebSocketServer } from '@hydrooj/framework';
export { Filter, ObjectId } from 'mongodb';
export {
Schema, superagent, _, ObjectID, Zip,
_, ObjectID, Schema, superagent, Zip,
};
/** @deprecated Use ZipReader/ZipWriter instead */
export const AdmZip = _AdmZip;
+2 -3
View File
@@ -1,4 +1,3 @@
/* eslint-disable import/no-dynamic-require */
/* eslint-disable consistent-return */
/* eslint-disable simple-import-sort/imports */
import './init';
@@ -181,7 +180,7 @@ export async function load() {
if (process.env.DEV) {
const q = await simpleGit().listRemote(['--get-url']);
if (!q.includes('hydro-dev/Hydro')) {
console.warn('\x1b[93m');
console.warn('\x1B[93m');
console.warn('DISCLAIMER:');
console.warn(' You are under development mode.');
console.warn(' The Hydro project is licensed under AGPL3,');
@@ -195,7 +194,7 @@ export async function load() {
console.warn(' 这意味着除非你获得了原作者的其他授权,');
console.warn(' 你需要同样以 AGPL3 协议开源所有的修改,');
console.warn(' 并保留所有的版权声明。');
console.warn('\x1b[39m');
console.warn('\x1B[39m');
console.log('');
console.log('Hydro will start in 5s.');
console.log('Hydro 将在五秒后继续启动。');
+1 -1
View File
@@ -641,7 +641,7 @@ const homework = buildContestRule({
);
if (exceedSeconds < 0) return rate * jdoc.score;
let coefficient = 1;
const keys = Object.keys(tdoc.penaltyRules).map(parseFloat).sort((a, b) => a - b);
const keys = Object.keys(tdoc.penaltyRules).map(Number.parseFloat).sort((a, b) => a - b);
for (const i of keys) {
if (i * 3600 <= exceedSeconds) coefficient = tdoc.penaltyRules[i];
else break;
+2 -3
View File
@@ -1,4 +1,3 @@
/* eslint-disable object-curly-newline */
import assert from 'assert';
import {
Filter, FindCursor, ObjectId, OnlyFieldsOfType, PushOperator, UpdateFilter,
@@ -43,9 +42,9 @@ export interface DocType {
}
export interface DocStatusType {
[TYPE_PROBLEM]: ProblemStatusDoc,
[TYPE_PROBLEM]: ProblemStatusDoc;
// FIXME: this need to be typed
[key: number]: any
[key: number]: any;
}
export async function add<T extends keyof DocType, K extends DocType[T]['docId']>(
+3 -3
View File
@@ -15,8 +15,8 @@ const collUser = db.collection('domain.user');
const cache = new LRUCache<string, DomainDoc>({ max: 1000, ttl: 300 * 1000 });
interface DomainUserArg {
_id: number,
priv: number,
_id: number;
priv: number;
}
class DomainModel {
@@ -282,7 +282,7 @@ class DomainModel {
@ArgMethod
static async getPrefixSearch(prefix: string, limit: number = 50) {
const $regex = new RegExp(escapeRegExp(prefix), 'mi');
const $regex = new RegExp(escapeRegExp(prefix), 'im');
const ddocs = await coll.find({
$or: [{ _id: { $regex } }, { name: { $regex } }],
}).limit(limit).toArray();
+5 -6
View File
@@ -1,4 +1,3 @@
/* eslint-disable object-curly-newline */
import { pick, sum } from 'lodash';
import moment from 'moment-timezone';
import {
@@ -133,11 +132,11 @@ export default class RecordModel {
domainId: string, pid: number, uid: number,
lang: string, code: string, addTask: boolean,
args: {
contest?: ObjectId,
input?: string,
files?: Record<string, string>,
hackTarget?: ObjectId,
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate',
contest?: ObjectId;
input?: string;
files?: Record<string, string>;
hackTarget?: ObjectId;
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate';
} = { type: 'judge' },
) {
const data: RecordDoc = {
+1 -1
View File
@@ -69,7 +69,7 @@ export const Setting = (
declare global {
namespace Schemastery {
interface Meta<T> { // eslint-disable-line @typescript-eslint/no-unused-vars
interface Meta<T> { // eslint-disable-line ts/no-unused-vars
family?: string;
secret?: boolean;
}
+2 -2
View File
@@ -97,8 +97,8 @@ export async function get(domainId: string, tid: ObjectId) {
if (!tdoc) throw new TrainingNotFoundError(domainId, 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);
if (Number.isSafeInteger(Number.parseInt(tdoc.dag[i].pids[j], 10))) {
tdoc.dag[i].pids[j] = Number.parseInt(tdoc.dag[i].pids[j], 10);
}
}
}
+5 -5
View File
@@ -5,7 +5,7 @@ import { serializer } from '@hydrooj/framework';
import { LoginError, UserAlreadyExistError, UserNotFoundError } from '../error';
import {
Authenticator, BaseUserDict, FileInfo, GDoc,
ownerInfo, Udict, Udoc, VUdoc,
OwnerInfo, Udict, Udoc, VUdoc,
} from '../interface';
import avatar from '../lib/avatar';
import pwhash from '../lib/hash.hydro';
@@ -116,9 +116,9 @@ export class User {
return this;
}
own<T extends ownerInfo>(doc: T, checkPerm: bigint): boolean;
own<T extends ownerInfo>(doc: T, exact: boolean): boolean;
own<T extends ownerInfo>(doc: T): boolean;
own<T extends OwnerInfo>(doc: T, checkPerm: bigint): boolean;
own<T extends OwnerInfo>(doc: T, exact: boolean): boolean;
own<T extends OwnerInfo>(doc: T): boolean;
own<T extends { owner: number, maintainer?: number[] }>(doc: T): boolean;
own(doc: any, arg1: any = false): boolean {
if (typeof arg1 === 'bigint' && !this.hasPerm(arg1)) return false;
@@ -143,7 +143,7 @@ export class User {
throw new LoginError(this.uname);
}
if (this.hashType !== 'hydro') {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
// eslint-disable-next-line ts/no-use-before-define
UserModel.setPassword(this._id, password);
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ export async function iterateAllPsdoc(filter: Filter<ProblemStatusDoc>, cb: (psd
}
interface PartialProblemDoc extends ProblemDoc {
[key: string]: any,
[key: string]: any;
}
export async function iterateAllProblemInDomain(
+7 -6
View File
@@ -1,12 +1,13 @@
/* eslint-disable perfectionist/sort-exports */
import pwsh from './lib/hash.hydro';
import db from './service/db';
export { nanoid } from 'nanoid';
export { default as moment, isMoment } from 'moment-timezone';
export { isMoment, default as moment } from 'moment-timezone';
export {
WebService, Router, HandlerCommon, httpServer,
Query, Mutation, Subscription, Apis, APIS,
Apis, APIS, HandlerCommon, httpServer,
Mutation, Query, Router, Subscription, WebService,
} from '@hydrooj/framework';
export * from './pipelineUtils';
@@ -37,10 +38,10 @@ export { default as TaskModel } from './model/task';
export * from './model/builtin';
/** @deprecated */
export * as JudgeHandler from './handler/judge';
export { postJudge, JudgeResultCallbackContext } from './handler/judge';
export { JudgeResultCallbackContext, postJudge } from './handler/judge';
export { Collections } from './service/db';
export { Handler, ConnectionHandler, requireSudo } from './service/server';
export { Service, Context } from './context';
export { ConnectionHandler, Handler, requireSudo } from './service/server';
export { Context, Service } from './context';
export { buildContent } from './lib/content';
export { default as mime } from './lib/mime';
export { default as difficultyAlgorithm } from './lib/difficulty';
+1 -1
View File
@@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable ts/no-use-before-define */
import { ObjectId } from 'mongodb';
import Schema from 'schemastery';
import blacklist from '../model/blacklist';
+1 -1
View File
@@ -14,7 +14,7 @@ async function getRemoteVersion(id: string) {
}
}
export async function run(_: {}, report: Function) {
export async function run(_: void, report: (data: any) => void) {
const current = global.Hydro.version;
const message = [''];
for (const name in current) {
+5 -4
View File
@@ -17,9 +17,10 @@ import db from '../service/db';
export const description = 'Calculate rp of a domain, or all domains';
type ND = NumericDictionary<number>;
type Report = (data: any) => void;
interface RpDef {
run(domainIds: string[], udict: ND, report: Function): Promise<void>;
run(domainIds: string[], udict: ND, report: Report): Promise<void>;
hidden: boolean;
base: number;
}
@@ -101,7 +102,7 @@ export const RpTypes: Record<string, RpDef> = {
};
global.Hydro.model.rp = RpTypes;
export async function calcLevel(domainId: string, report: Function) {
export async function calcLevel(domainId: string, report: Report) {
await domain.setMultiUserInDomain(domainId, {}, { level: 0, rank: null });
let last = { rp: null };
let rank = 0;
@@ -135,7 +136,7 @@ export async function calcLevel(domainId: string, report: Function) {
await bulk.execute();
}
async function runInDomain(domainId: string, report: Function) {
async function runInDomain(domainId: string, report: Report) {
const results: Record<keyof typeof RpTypes, ND> = {};
const udict = Counter();
await db.collection('domain.user').updateMany({ domainId }, { $set: { rpInfo: {} } });
@@ -160,7 +161,7 @@ async function runInDomain(domainId: string, report: Function) {
await calcLevel(domainId, report);
}
export async function run({ domainId }, report: Function) {
export async function run({ domainId }, report: Report) {
if (!domainId) {
const domains = await domain.getMulti().toArray();
await report({ message: `Found ${domains.length} domains` });
+61 -61
View File
@@ -15,95 +15,95 @@ export type Disposable = () => void;
export type VoidReturn = Promise<any> | any;
export interface EventMap {
'app/listen': () => void
'app/started': () => void
'app/ready': () => VoidReturn
'app/exit': () => VoidReturn
'app/before-reload': (entries: Set<string>) => VoidReturn
'app/reload': (entries: Set<string>) => VoidReturn
'app/listen': () => void;
'app/started': () => void;
'app/ready': () => VoidReturn;
'app/exit': () => VoidReturn;
'app/before-reload': (entries: Set<string>) => VoidReturn;
'app/reload': (entries: Set<string>) => VoidReturn;
'subscription/init': (h: ConnectionHandler<Context>, privileged: boolean) => VoidReturn
'subscription/subscribe': (channel: string, user: User, metadata: Record<string, string>) => VoidReturn
'subscription/init': (h: ConnectionHandler<Context>, privileged: boolean) => VoidReturn;
'subscription/subscribe': (channel: string, user: User, metadata: Record<string, string>) => VoidReturn;
'subscription/enable': (
channel: string, h: ConnectionHandler<Context>, privileged: boolean, onDispose: (disposable: () => void) => void
) => VoidReturn
) => VoidReturn;
'app/watch/change': (path: string) => VoidReturn
'app/watch/unlink': (path: string) => VoidReturn
'app/watch/change': (path: string) => VoidReturn;
'app/watch/unlink': (path: string) => VoidReturn;
'database/connect': (db: Db) => void
'database/config': () => VoidReturn
'database/connect': (db: Db) => void;
'database/config': () => VoidReturn;
'system/setting': (args: Record<string, any>) => VoidReturn
'bus/broadcast': (event: keyof EventMap, payload: any, trace?: string) => VoidReturn
'monitor/update': (type: 'server' | 'judge', $set: any) => VoidReturn
'monitor/collect': (info: any) => VoidReturn
'system/setting': (args: Record<string, any>) => VoidReturn;
'bus/broadcast': (event: keyof EventMap, payload: any, trace?: string) => VoidReturn;
'monitor/update': (type: 'server' | 'judge', $set: any) => VoidReturn;
'monitor/collect': (info: any) => VoidReturn;
'api/update': () => void;
'task/daily': () => void;
'task/daily/finish': (pref: Record<string, number>) => void;
'user/message': (uid: number[], mdoc: Omit<MessageDoc, 'to'>) => void
'user/get': (udoc: User) => void
'user/delcache': (content: string | true) => void
'user/message': (uid: number[], mdoc: Omit<MessageDoc, 'to'>) => void;
'user/get': (udoc: User) => void;
'user/delcache': (content: string | true) => void;
'user/import/parse': (payload: any) => VoidReturn
'user/import/create': (uid: number, udoc: any) => VoidReturn
'user/import/parse': (payload: any) => VoidReturn;
'user/import/create': (uid: number, udoc: any) => VoidReturn;
'domain/create': (ddoc: DomainDoc) => VoidReturn
'domain/before-get': (query: Filter<DomainDoc>) => VoidReturn
'domain/get': (ddoc: DomainDoc) => VoidReturn
'domain/before-update': (domainId: string, $set: Partial<DomainDoc>) => VoidReturn
'domain/update': (domainId: string, $set: Partial<DomainDoc>, ddoc: DomainDoc) => VoidReturn
'domain/delete': (domainId: string) => VoidReturn
'domain/delete-cache': (domainId: string) => VoidReturn
'domain/create': (ddoc: DomainDoc) => VoidReturn;
'domain/before-get': (query: Filter<DomainDoc>) => VoidReturn;
'domain/get': (ddoc: DomainDoc) => VoidReturn;
'domain/before-update': (domainId: string, $set: Partial<DomainDoc>) => VoidReturn;
'domain/update': (domainId: string, $set: Partial<DomainDoc>, ddoc: DomainDoc) => VoidReturn;
'domain/delete': (domainId: string) => VoidReturn;
'domain/delete-cache': (domainId: string) => VoidReturn;
'document/add': (doc: any) => VoidReturn
'document/add': (doc: any) => VoidReturn;
'document/set': <T extends keyof DocType>(
domainId: string, docType: T, docId: DocType[T],
$set: any, $unset: OnlyFieldsOfType<DocType[T], any, true | '' | 1>
) => VoidReturn
) => VoidReturn;
'discussion/before-add': (payload: Partial<DiscussionDoc>) => VoidReturn
'discussion/add': (payload: Partial<DiscussionDoc>) => VoidReturn
'discussion/before-add': (payload: Partial<DiscussionDoc>) => VoidReturn;
'discussion/add': (payload: Partial<DiscussionDoc>) => VoidReturn;
'problem/before-add': (domainId: string, content: string, owner: number, docId: number, doc: Partial<ProblemDoc>) => VoidReturn
'problem/add': (doc: Partial<ProblemDoc>, docId: number) => VoidReturn
'problem/before-edit': (doc: Partial<ProblemDoc>, $unset: OnlyFieldsOfType<ProblemDoc, any, true | '' | 1>) => VoidReturn
'problem/edit': (doc: ProblemDoc) => VoidReturn
'problem/before-del': (domainId: string, docId: number) => VoidReturn
'problem/del': (domainId: string, docId: number) => VoidReturn
'problem/list': (query: Filter<ProblemDoc>, handler: any, sort?: string[]) => VoidReturn
'problem/get': (doc: ProblemDoc, handler: any) => VoidReturn
'problem/delete': (domainId: string, docId: number) => VoidReturn
'problem/addTestdata': (domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => VoidReturn
'problem/renameTestdata': (domainId: string, docId: number, name: string, newName: string) => VoidReturn
'problem/delTestdata': (domainId: string, docId: number, name: string[]) => VoidReturn
'problem/addAdditionalFile': (domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => VoidReturn
'problem/renameAdditionalFile': (domainId: string, docId: number, name: string, newName: string) => VoidReturn
'problem/delAdditionalFile': (domainId: string, docId: number, name: string[]) => VoidReturn
'problem/before-add': (domainId: string, content: string, owner: number, docId: number, doc: Partial<ProblemDoc>) => VoidReturn;
'problem/add': (doc: Partial<ProblemDoc>, docId: number) => VoidReturn;
'problem/before-edit': (doc: Partial<ProblemDoc>, $unset: OnlyFieldsOfType<ProblemDoc, any, true | '' | 1>) => VoidReturn;
'problem/edit': (doc: ProblemDoc) => VoidReturn;
'problem/before-del': (domainId: string, docId: number) => VoidReturn;
'problem/del': (domainId: string, docId: number) => VoidReturn;
'problem/list': (query: Filter<ProblemDoc>, handler: any, sort?: string[]) => VoidReturn;
'problem/get': (doc: ProblemDoc, handler: any) => VoidReturn;
'problem/delete': (domainId: string, docId: number) => VoidReturn;
'problem/addTestdata': (domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => VoidReturn;
'problem/renameTestdata': (domainId: string, docId: number, name: string, newName: string) => VoidReturn;
'problem/delTestdata': (domainId: string, docId: number, name: string[]) => VoidReturn;
'problem/addAdditionalFile': (domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => VoidReturn;
'problem/renameAdditionalFile': (domainId: string, docId: number, name: string, newName: string) => VoidReturn;
'problem/delAdditionalFile': (domainId: string, docId: number, name: string[]) => VoidReturn;
'contest/before-add': (payload: Partial<Tdoc>) => VoidReturn
'contest/add': (payload: Partial<Tdoc>, id: ObjectId) => VoidReturn
'contest/edit': (payload: Tdoc) => VoidReturn
'contest/list': (query: Filter<Tdoc>, handler: any) => VoidReturn
'contest/scoreboard': (tdoc: Tdoc, rows: ScoreboardRow[], udict: BaseUserDict, pdict: ProblemDict) => VoidReturn
'contest/balloon': (domainId: string, tid: ObjectId, bdoc: ContestBalloonDoc) => VoidReturn
'contest/del': (domainId: string, tid: ObjectId) => VoidReturn
'contest/before-add': (payload: Partial<Tdoc>) => VoidReturn;
'contest/add': (payload: Partial<Tdoc>, id: ObjectId) => VoidReturn;
'contest/edit': (payload: Tdoc) => VoidReturn;
'contest/list': (query: Filter<Tdoc>, handler: any) => VoidReturn;
'contest/scoreboard': (tdoc: Tdoc, rows: ScoreboardRow[], udict: BaseUserDict, pdict: ProblemDict) => VoidReturn;
'contest/balloon': (domainId: string, tid: ObjectId, bdoc: ContestBalloonDoc) => VoidReturn;
'contest/del': (domainId: string, tid: ObjectId) => VoidReturn;
'oplog/log': (type: string, handler: Handler<Context> | ConnectionHandler<Context>, args: any, data: any) => VoidReturn;
'training/list': (query: Filter<TrainingDoc>, handler: any) => VoidReturn
'training/get': (tdoc: TrainingDoc, handler: any) => VoidReturn
'training/list': (query: Filter<TrainingDoc>, handler: any) => VoidReturn;
'training/get': (tdoc: TrainingDoc, handler: any) => VoidReturn;
'record/change': (rdoc: RecordDoc, $set?: any, $push?: any, body?: any) => void
'record/judge': (rdoc: RecordDoc, updated: boolean, pdoc?: ProblemDoc, updater?: any) => VoidReturn
'record/change': (rdoc: RecordDoc, $set?: any, $push?: any, body?: any) => void;
'record/judge': (rdoc: RecordDoc, updated: boolean, pdoc?: ProblemDoc, updater?: any) => VoidReturn;
}
export function apply(ctx: Context) {
try {
if (!process.send || !pm2 || process.env.exec_mode !== 'cluster_mode') throw new Error();
if (!process.send || !pm2 || process.env.exec_mode !== 'cluster_mode') throw new Error('not in cluster mode');
pm2.launchBus((err, bus) => {
if (err) throw new Error();
if (err) throw new Error('cannot launch pm2 bus');
bus.on('hydro:broadcast', (packet) => {
(app.parallel as any)(packet.data.event, ...BSON.EJSON.parse(packet.data.payload));
});
+10 -10
View File
@@ -14,16 +14,16 @@ const logger = new Logger('mongo');
export interface Collections { }
interface MongoConfig {
protocol?: string,
username?: string,
password?: string,
host?: string,
port?: string,
name?: string,
url?: string,
uri?: string,
prefix?: string,
collectionMap?: Record<string, string>,
protocol?: string;
username?: string;
password?: string;
host?: string;
port?: string;
name?: string;
url?: string;
uri?: string;
prefix?: string;
collectionMap?: Record<string, string>;
}
declare module '../context' {
+1 -2
View File
@@ -1,7 +1,6 @@
// This file was adapted from @koishijs, MIT licensed.
/* eslint-disable consistent-return */
/* eslint-disable import/no-dynamic-require */
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable ts/no-shadow */
import { readFileSync } from 'fs';
import { relative } from 'path';
import { codeFrameColumns } from '@babel/code-frame';
+3 -3
View File
@@ -28,7 +28,7 @@ const ignoredLimit = `,${argv.options.ignoredLimit},`;
const logger = new Logger('server');
declare module '@hydrooj/framework' {
export interface HandlerCommon<C> { // eslint-disable-line @typescript-eslint/no-unused-vars
export interface HandlerCommon<C> { // eslint-disable-line ts/no-unused-vars
domain: DomainDoc;
paginate<T>(cursor: FindCursor<T>, page: number, key: string): Promise<[docs: T[], numPages: number, count: number]>;
@@ -39,9 +39,9 @@ declare module '@hydrooj/framework' {
}
}
export { Mutation, Query } from '@hydrooj/framework/api';
export * from '@hydrooj/framework/decorators';
export * from '@hydrooj/framework/validator';
export { Query, Mutation } from '@hydrooj/framework/api';
/*
* For security concern, some API requires sudo privilege to access.
@@ -153,7 +153,7 @@ export async function apply(ctx: Context) {
? (!host.includes(this.request.host))
: this.request.host !== host)
)) withDomainId ||= domainId;
res = server.router.url.call(server.router, name, args, { query }).toString();
res = server.router.url(name, args, { query }).toString();
if (anchor) res = `${res}#${anchor}`;
if (withDomainId) res = `/d/${withDomainId}${res}`;
} catch (e) {
+4 -4
View File
@@ -1,7 +1,7 @@
/* eslint-disable consistent-return */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable ts/no-unused-vars */
/* eslint-disable no-await-in-loop */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable ts/naming-convention */
import yaml from 'js-yaml';
import { ObjectId } from 'mongodb';
import { randomstring, sleep } from '@hydrooj/utils';
@@ -51,7 +51,7 @@ export const coreScripts: MigrationScript[] = [
return true;
},
// Init
...new Array(47).fill(unsupportedUpgrade), // oxlint-disable-line no-new-array
...Array.from({ length: 47 }).fill(unsupportedUpgrade) as any,
async function _48_49() {
await RecordModel.coll.updateMany({ input: { $exists: true } }, { $set: { contest: new ObjectId('000000000000000000000000') } });
return true;
@@ -221,7 +221,7 @@ export const coreScripts: MigrationScript[] = [
config.answers[cnt] = l;
}
function processSingleLanguage(content: string) { // eslint-disable-line no-inner-declarations
function processSingleLanguage(content: string) {
let text = '';
try {
let scnt = 0;