mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
workspace: update eslint rule (#1027)
This commit is contained in:
1 parent
05c3b8f2bd
commit
cee3c00ddb
150 files changed
+1069
-1043
No files matched your search
@@ -167,7 +167,7 @@ export async function startPerformanceTest(args: { enable5: boolean }, report) {
|
||||
await ProblemModel.addTestdata('system', docId, 'config.yaml', Buffer.from(yaml.dump({
|
||||
time: '3s',
|
||||
memory: args.enable5 ? '2g' : '512m',
|
||||
cases: new Array(20).fill({
|
||||
cases: Array.from({ length: 20 }).fill({
|
||||
input: '1.in',
|
||||
output: '1.out',
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { findFileSync, parseMemoryMB, parseTimeMS } from '@hydrooj/utils';
|
||||
import { CompilableSource, ProblemConfigFile } from './types';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// eslint-disable-next-line ts/no-unused-vars
|
||||
export async function readYamlCases(cfg: ProblemConfigFile = {}, checkFile = (s: CompilableSource, errMsg: string) => s) {
|
||||
const config: any = {
|
||||
checker_type: cfg.checker_type || 'default',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from './lang';
|
||||
export * from './status';
|
||||
export * from './types';
|
||||
export * from './subtask';
|
||||
export * from './types';
|
||||
@@ -2,18 +2,18 @@ import { parseMemoryMB, parseTimeMS } from '@hydrooj/utils/lib/common';
|
||||
|
||||
export function convertIniConfig(ini: string) {
|
||||
const f = ini.split('\n');
|
||||
const count = parseInt(f[0], 10);
|
||||
const count = Number.parseInt(f[0], 10);
|
||||
const res = { subtasks: [] };
|
||||
for (let i = 1; i <= count; i++) {
|
||||
if (!f[i]?.trim()) throw new Error('Testdata count incorrect.');
|
||||
const [input, output, time, score, memory] = f[i].split('|');
|
||||
const cur = {
|
||||
cases: [{ input, output }],
|
||||
score: parseInt(score, 10),
|
||||
score: Number.parseInt(score, 10),
|
||||
time: `${time}s`,
|
||||
memory: '256m',
|
||||
};
|
||||
if (!Number.isNaN(parseInt(memory, 10))) cur.memory = `${Math.floor(parseInt(memory, 10) / 1024)}m`;
|
||||
if (!Number.isNaN(Number.parseInt(memory, 10))) cur.memory = `${Math.floor(Number.parseInt(memory, 10) / 1024)}m`;
|
||||
res.subtasks.push(cur);
|
||||
}
|
||||
return res;
|
||||
@@ -29,7 +29,8 @@ interface MatchRule {
|
||||
|
||||
const SubtaskMatcher: MatchRule[] = [
|
||||
{
|
||||
regex: /^(([A-Za-z0-9._-]*?)(?:(\d*)[-_])?(\d+))\.(in|IN|txt|TXT|in\.txt|IN\.TXT)$/,
|
||||
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
||||
regex: /^(([\w.-]*?)(?:(\d*)[-_])?(\d+))\.(in|IN|txt|TXT|in\.txt|IN\.TXT)$/,
|
||||
output: (a) => ['out', 'ans']
|
||||
.flatMap((i) => [i, i.toUpperCase(), `${i}.txt`, `${i.toUpperCase()}.TXT`])
|
||||
.flatMap((i) => [`${a[1]}.${i}`, `${a[1]}.${i}`.replace(/input/g, 'output').replace(/INPUT/g, 'OUTPUT')])
|
||||
@@ -39,7 +40,7 @@ const SubtaskMatcher: MatchRule[] = [
|
||||
preferredScorerType: (a) => (a[3] ? 'min' : 'sum'),
|
||||
},
|
||||
{
|
||||
regex: /^([^\d]*)\.(in|IN)(\d+)$/,
|
||||
regex: /^(\D*)\.(in|IN)(\d+)$/,
|
||||
output: (a) => [
|
||||
`${a[1]}.${a[2] === 'in' ? 'ou' : 'OU'}${a[3]}`,
|
||||
`${a[1]}.${a[2] === 'in' ? 'out' : 'OUT'}${a[3]}`,
|
||||
@@ -49,14 +50,14 @@ const SubtaskMatcher: MatchRule[] = [
|
||||
preferredScorerType: () => 'sum',
|
||||
},
|
||||
{
|
||||
regex: /^([^\d]*)([0-9]+)([-_])([0-9]+)\.(in|IN)$/,
|
||||
regex: /^(\D*)([0-9]+)([-_])([0-9]+)\.(in|IN)$/,
|
||||
output: (a) => ['out', 'ans', 'OUT', 'ANS'].flatMap((i) => `${a[1]}${a[2]}${a[3]}${a[4]}.${i}`),
|
||||
id: (a) => +a[4],
|
||||
subtask: (a) => +a[2],
|
||||
preferredScorerType: () => 'min',
|
||||
},
|
||||
{
|
||||
regex: /^(([0-9]+)[-_](?:.*))\.(in|IN)$/,
|
||||
regex: /^(([0-9]+)[-_].*)\.(in|IN)$/,
|
||||
output: (a) => ['out', 'ans', 'OUT', 'ANS'].flatMap((i) => `${a[1]}.${i}`),
|
||||
id: (a) => +a[2],
|
||||
subtask: () => 1,
|
||||
|
||||
@@ -66,13 +66,13 @@ export interface ProblemConfigFile {
|
||||
|
||||
export interface FileInfo {
|
||||
/** storage path */
|
||||
_id: string,
|
||||
_id: string;
|
||||
/** filename */
|
||||
name: string,
|
||||
name: string;
|
||||
/** file size (in bytes) */
|
||||
size: number,
|
||||
etag: string,
|
||||
lastModified: Date,
|
||||
size: number;
|
||||
etag: string;
|
||||
lastModified: Date;
|
||||
}
|
||||
|
||||
export interface JudgeMeta {
|
||||
@@ -113,7 +113,7 @@ export interface RecordPayload extends RecordJudgeInfo {
|
||||
/** 0 if pretest&script */
|
||||
contest?: string;
|
||||
|
||||
files?: Record<string, string>
|
||||
files?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface JudgeRequest extends Omit<RecordPayload, 'testCases'> {
|
||||
|
||||
@@ -91,7 +91,7 @@ function MonacoContainer({
|
||||
if (!editor) return;
|
||||
const current = editor.getValue({ lineEnding: '\n', preserveBOM: false });
|
||||
const diff = diffLines(current, config);
|
||||
const ops: { range: any; text: string }[] = [];
|
||||
const ops: { range: any, text: string }[] = [];
|
||||
let cursor = 1;
|
||||
for (const line of diff) {
|
||||
if (line.added) {
|
||||
@@ -109,11 +109,7 @@ function MonacoContainer({
|
||||
}
|
||||
model.pushEditOperations([], ops, undefined);
|
||||
}, [editor, config]);
|
||||
return (
|
||||
<div ref={initializeEditor} style={{
|
||||
width: '100%', height: '500px',
|
||||
}} />
|
||||
);
|
||||
return <div ref={initializeEditor} style={{ width: '100%', height: '500px' }} />;
|
||||
}
|
||||
|
||||
export default function ConfigEditor({
|
||||
@@ -166,8 +162,12 @@ export default function ConfigEditor({
|
||||
<Allotment onChange={setSize}>
|
||||
<Allotment.Pane>
|
||||
<MonacoContainer
|
||||
editorCallback={registerAction} schema={schema} monaco={monaco}
|
||||
config={stringConfig} setValue={updateFromMonaco} setError={setInfo}
|
||||
editorCallback={registerAction}
|
||||
schema={schema}
|
||||
monaco={monaco}
|
||||
config={stringConfig}
|
||||
setValue={updateFromMonaco}
|
||||
setError={setInfo}
|
||||
size={size[0]}
|
||||
dark={document.documentElement.className.includes('theme--dark')}
|
||||
/>
|
||||
|
||||
@@ -79,7 +79,7 @@ function DraggableSelection({
|
||||
const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, ref: React.Ref<AutoCompleteHandle<T>>) {
|
||||
const {
|
||||
multi = false, width = '100%', height = 'auto',
|
||||
freeSolo = false, allowEmptyQuery = false, listStyle = {}, // eslint-disable-line @eslint-react/no-unstable-default-props
|
||||
freeSolo = false, allowEmptyQuery = false, listStyle = {},
|
||||
disabled = false, disabledHint = '', draggable = multi,
|
||||
} = props;
|
||||
const queryItems = props.queryItems ?? (() => []);
|
||||
@@ -87,7 +87,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
const itemText = props.itemText ?? ((item) => item.toString());
|
||||
const itemKey = props.itemKey ?? itemText;
|
||||
const onChange = props.onChange ?? (() => { });
|
||||
const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : ((i) => i);
|
||||
const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : (i) => i;
|
||||
|
||||
const [focused, setFocused] = useState(false); // is focused
|
||||
const [selectedKeys, setSelectedKeys] = useState(props.selectedKeys || []); // keys of selected items
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export { default as ComponentsProvider } from './provider';
|
||||
export { default as AutoComplete, type AutoCompleteHandle, type AutoCompleteProps } from './autocomplete/AutoComplete';
|
||||
export { default as CustomSelectAutoComplete } from './autocomplete/CustomSelectAutoComplete';
|
||||
export { default as ConfigEditor } from './ConfigEditor';
|
||||
export { default as Icon } from './Icon';
|
||||
export { default as Notification } from './Notification';
|
||||
export { default as AutoComplete, type AutoCompleteHandle, type AutoCompleteProps } from './autocomplete/AutoComplete';
|
||||
export { default as CustomSelectAutoComplete } from './autocomplete/CustomSelectAutoComplete';
|
||||
export { default as ComponentsProvider } from './provider';
|
||||
@@ -21,7 +21,7 @@
|
||||
"@types/diff": "^8.0.0",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"sass": "^1.89.2"
|
||||
"sass": "^1.90.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -27,7 +27,7 @@ class FpsProblemImportHandler extends Handler {
|
||||
samples: p.sample_input?.map((input, i) => [input, p.sample_output[i]]),
|
||||
hint: p.hint?.[0],
|
||||
source: p.source?.join(' '),
|
||||
}, 'html', (s) => this.translate(s)).replace(/<math xm<x>lns=/g, '<math xmlns=').replace(/\[\/?md]/g, '');
|
||||
}, 'html', (s) => this.translate(s)).replace(/<math xm<x>lns=/g, '<math xmlns=').replace(/\[\/?md\]/g, '');
|
||||
const config: ProblemConfigFile = {
|
||||
time: p.time_limit[0]._ + p.time_limit[0].$.unit,
|
||||
memory: p.memory_limit[0]._ + p.memory_limit[0].$.unit,
|
||||
|
||||
@@ -7,11 +7,11 @@ const buffer = fs.readFileSync(findFileSync('@hydrooj/geoip/GeoLite2-City.mmdb')
|
||||
const reader = new Reader(buffer);
|
||||
|
||||
export interface Result {
|
||||
location?: string,
|
||||
continent?: string,
|
||||
country?: string,
|
||||
city?: string,
|
||||
display: string
|
||||
location?: string;
|
||||
continent?: string;
|
||||
country?: string;
|
||||
city?: string;
|
||||
display: string;
|
||||
}
|
||||
|
||||
export default class GeoIPService extends Service {
|
||||
|
||||
@@ -1 +1 @@
|
||||
geoip_locale: zh-CN
|
||||
geoip_locale: zh-CN
|
||||
@@ -1 +1 @@
|
||||
geoip_locale: zh-CN
|
||||
geoip_locale: zh-CN
|
||||
@@ -1,38 +1,38 @@
|
||||
c:
|
||||
compile: /usr/bin/gcc -O2 -Wall -std=c99 -o foo foo.c -lm
|
||||
compile: /usr/bin/gcc -O2 -Wall -std=c99 -o foo foo.c -lm
|
||||
cc:
|
||||
compile: /usr/bin/g++ -O2 -Wall -std=c++11 -o foo foo.cc -lm
|
||||
compile: /usr/bin/g++ -O2 -Wall -std=c++11 -o foo foo.cc -lm
|
||||
pas:
|
||||
compile: /usr/bin/fpc -O2 -ofoo foo.pas
|
||||
compile: /usr/bin/fpc -O2 -ofoo foo.pas
|
||||
java:
|
||||
compile: /usr/bin/bash -c "javac -d /w -encoding utf8 ./Main.java && jar cvf Main.jar *.class >/dev/null"
|
||||
code_file: Main.java
|
||||
target: Main.jar
|
||||
execute: /usr/bin/java -Dfile.encoding="UTF-8" -cp Main.jar Main
|
||||
time_limit_rate: 2
|
||||
compile_time_limit: 30000
|
||||
compile_memory_limit: 1024
|
||||
compile: /usr/bin/bash -c "javac -d /w -encoding utf8 ./Main.java && jar cvf Main.jar *.class >/dev/null"
|
||||
code_file: Main.java
|
||||
target: Main.jar
|
||||
execute: /usr/bin/java -Dfile.encoding="UTF-8" -cp Main.jar Main
|
||||
time_limit_rate: 2
|
||||
compile_time_limit: 30000
|
||||
compile_memory_limit: 1024
|
||||
py3:
|
||||
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
||||
code_file: foo.py
|
||||
execute: /usr/bin/python3 foo
|
||||
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
||||
code_file: foo.py
|
||||
execute: /usr/bin/python3 foo
|
||||
php:
|
||||
execute: /usr/bin/php foo.php
|
||||
execute: /usr/bin/php foo.php
|
||||
rs:
|
||||
compile: /usr/bin/rustc -O -o /w/foo /w/foo.rs
|
||||
compile_time_limit: 30000
|
||||
compile: /usr/bin/rustc -O -o /w/foo /w/foo.rs
|
||||
compile_time_limit: 30000
|
||||
hs:
|
||||
compile: /usr/bin/ghc -O -outputdir /tmp -o /w/foo /w/foo.hs
|
||||
compile_time_limit: 30000
|
||||
compile: /usr/bin/ghc -O -outputdir /tmp -o /w/foo /w/foo.hs
|
||||
compile_time_limit: 30000
|
||||
js:
|
||||
execute: /usr/bin/jsc foo.js
|
||||
execute: /usr/bin/jsc foo.js
|
||||
go:
|
||||
compile: /usr/bin/go build -o /w/foo /w/foo.go
|
||||
compile_time_limit: 30000
|
||||
compile: /usr/bin/go build -o /w/foo /w/foo.go
|
||||
compile_time_limit: 30000
|
||||
rb:
|
||||
execute: /usr/bin/ruby foo.rb
|
||||
compile_time_limit: 30000
|
||||
execute: /usr/bin/ruby foo.rb
|
||||
compile_time_limit: 30000
|
||||
cs:
|
||||
compile: /usr/bin/mcs csc -optimize+ -out:/w/foo.exe /w/foo.cs
|
||||
execute: /usr/bin/mono foo.exe
|
||||
compile_time_limit: 30000
|
||||
compile: /usr/bin/mcs csc -optimize+ -out:/w/foo.exe /w/foo.cs
|
||||
execute: /usr/bin/mono foo.exe
|
||||
compile_time_limit: 30000
|
||||
@@ -36,4 +36,4 @@ Unknown language type.: 未知的语言类型。
|
||||
Unsupported language {0}.: 不支持的语言 {0}。
|
||||
User output longer than standard answer.: 选手输出比标准输出长。
|
||||
You did not specify a checker.: 你没有指定比较器文件。
|
||||
Your program returned {0}.: 您的程序返回了{0}。
|
||||
Your program returned {0}.: 您的程序返回了{0}。
|
||||
@@ -8,7 +8,7 @@
|
||||
"dependencies": {
|
||||
"@hydrooj/common": "workspace:^",
|
||||
"@hydrooj/utils": "workspace:^",
|
||||
"@zip.js/zip.js": "^2.7.71",
|
||||
"@zip.js/zip.js": "^2.7.72",
|
||||
"cac": "^6.7.14",
|
||||
"p-queue": "^8.1.0",
|
||||
"schemastery": "^3.16.1",
|
||||
|
||||
@@ -17,9 +17,9 @@ export interface CheckConfig {
|
||||
}
|
||||
|
||||
type Checker = (config: CheckConfig) => Promise<{
|
||||
status: number,
|
||||
score: number,
|
||||
message: string,
|
||||
status: number;
|
||||
score: number;
|
||||
message: string;
|
||||
}>;
|
||||
|
||||
function parseDiffMsg(msg: string) {
|
||||
@@ -206,7 +206,6 @@ const checkers: Record<string, Checker> = new Proxy({
|
||||
* stderr:输出错误报告
|
||||
*/
|
||||
async syzoj(config) {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { status, stdout, stderr } = await runQueued(config.execute, {
|
||||
copyIn: {
|
||||
input: config.input,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { STATUS_TEXTS } from '@hydrooj/common';
|
||||
|
||||
interface CompileErrorInfo {
|
||||
stdout?: string,
|
||||
stderr?: string,
|
||||
status?: number
|
||||
stdout?: string;
|
||||
stderr?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
export class CompileError extends Error {
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Task {
|
||||
compile: () => Promise<void>;
|
||||
judgeCase: (c: NormalizedCase) => (
|
||||
(ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => Promise<JudgeResultBody['case']>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const Score = {
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function judge({
|
||||
if (!Object.keys(config.answers).length) throw new FormatError('Invalid standard answer.');
|
||||
for (const key in config.answers) {
|
||||
const ansInfo = config.answers[key] as [string | string[], number] | Record<string, number>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
||||
// eslint-disable-next-line ts/no-loop-func
|
||||
const report = (status: STATUS, score: number, message: string) => {
|
||||
const [subtaskId, caseId] = key.split('-').map(Number);
|
||||
totalScore += score;
|
||||
|
||||
@@ -27,8 +27,8 @@ export class Stream extends EventEmitter {
|
||||
break;
|
||||
case 2:
|
||||
this.emit('output', {
|
||||
index: (data[1] >> 4) & 0xf,
|
||||
fd: (data[1]) & 0xf,
|
||||
index: (data[1] >> 4) & 0xF,
|
||||
fd: (data[1]) & 0xF,
|
||||
content: data.subarray(2),
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DetailType, STATUS } from '@hydrooj/common';
|
||||
|
||||
const operation = /^\s*(status|score)\((\d+)\)\s*(.*)$/m;
|
||||
const operation = /^\s*(status|score)\((\d+)\)\s*((\S|$).*)$/m;
|
||||
|
||||
export function parse(output: string, fullscore: number, detail: DetailType) {
|
||||
let status = STATUS.STATUS_WRONG_ANSWER;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CompilableSource } from '@hydrooj/common';
|
||||
import { fs } from '@hydrooj/utils';
|
||||
import { FormatError } from './error';
|
||||
|
||||
const EMPTY_STR = /^[ \r\n\t]*$/i;
|
||||
const EMPTY_STR = /^[ \r\n\t]*$/;
|
||||
|
||||
export const cmd = parse;
|
||||
|
||||
@@ -34,7 +34,7 @@ export function compilerText(...messages: string[]) {
|
||||
function restrictFile(p: string) {
|
||||
if (!p) return '/';
|
||||
if (p[0] === '/') p = '';
|
||||
return p.replace(/\.\./gmi, '');
|
||||
return p.replace(/\.\./g, '');
|
||||
}
|
||||
|
||||
export function ensureFile(folder: string) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
import cac from 'cac';
|
||||
import { getAddons } from '../src/options';
|
||||
|
||||
|
||||
@@ -114,4 +114,4 @@ user_lostpass: Lost Password
|
||||
user_register: Register
|
||||
user_sudo: Entering Sudo Mode
|
||||
wiki_about: About
|
||||
wiki_help: Help
|
||||
wiki_help: Help
|
||||
@@ -41,9 +41,6 @@ __langname: 한국어
|
||||
(All Problems): (전체 문제)
|
||||
(None): (N/A)
|
||||
(Not changed): 수정되지 않음
|
||||
#Sample Input: Sample Input
|
||||
#Sample Output: Sample Output
|
||||
#Star: Star
|
||||
Aborted: 중지됨
|
||||
About {0}: '{0}에 대하여'
|
||||
About Markdown: Markdown에 대하여
|
||||
|
||||
@@ -3,7 +3,7 @@ __id: zh
|
||||
__interface: yes
|
||||
__langname: 简体中文
|
||||
'"{data}" copied to clipboard!': “{data}”已复制到剪贴板!
|
||||
'{0} {1} delete your discussion reply {2} in "{3}"({4}).' : '{0} {1} 从讨论 {3}({4}) 删除了你的回复 {2}。'
|
||||
'{0} {1} delete your discussion reply {2} in "{3}"({4}).': '{0} {1} 从讨论 {3}({4}) 删除了你的回复 {2}。'
|
||||
'{0} comments': '{0} 条评论'
|
||||
'{0} discussions': '{0} 条讨论'
|
||||
'{0} is a moderator of this domain.': '{}是这个域的管理者之一。'
|
||||
@@ -38,7 +38,7 @@ __langname: 简体中文
|
||||
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
||||
'Yes': 是
|
||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
||||
"Or upload a file:": 或者上传一个文件:
|
||||
'Or upload a file:': 或者上传一个文件:
|
||||
"Split by ',' '.'": 由“, ”或“,”分隔。
|
||||
"What's file?": 什么是文件?
|
||||
(All Problems): (所有题目)
|
||||
@@ -64,8 +64,8 @@ Add new data: 添加新数据
|
||||
Add User: 添加用户
|
||||
Add: 添加
|
||||
Additional File: 附加文件
|
||||
Admin {0} delete your discussion "{1}". : 管理员 {0} 删除了你的讨论 "{1}"。
|
||||
Admin {0} delete your discussion tail reply {1} in "{2}"({3}). : 管理员 {0} 从讨论 {2}({3}) 中删除了你的楼中楼回复 {1}。
|
||||
Admin {0} delete your discussion "{1}".: 管理员 {0} 删除了你的讨论 "{1}"。
|
||||
Admin {0} delete your discussion tail reply {1} in "{2}"({3}).: 管理员 {0} 从讨论 {2}({3}) 中删除了你的楼中楼回复 {1}。
|
||||
All {0} Contests: 所有 {0} 比赛
|
||||
All Contests: 所有比赛
|
||||
All Homeworks: 所有作业
|
||||
@@ -947,4 +947,4 @@ You've already attended this contest.: 您已经参加本次比赛。
|
||||
You've already claimed this homework.: 您已认领过该作业。
|
||||
You've already voted.: 您已经投过票。
|
||||
Your permission: 您的权限
|
||||
Your role: 您的角色
|
||||
Your role: 您的角色
|
||||
@@ -563,4 +563,4 @@ You've already attended this contest.: 您已經參加本次比賽。
|
||||
You've already claimed this homework.: 您已認領該功課。
|
||||
You've already voted.: 您已經投過票。
|
||||
Your permission: 您的許可權
|
||||
Your role: 您的角色
|
||||
Your role: 您的角色
|
||||
@@ -23,14 +23,14 @@
|
||||
"@hydrooj/utils": "workspace:^",
|
||||
"@simplewebauthn/server": "13.1.2",
|
||||
"@undefined-moe/require-resolve-hook": "^1.1.0",
|
||||
"@zip.js/zip.js": "^2.7.71",
|
||||
"@zip.js/zip.js": "^2.7.72",
|
||||
"adm-zip": "0.5.16",
|
||||
"cac": "^6.7.14",
|
||||
"cordis": "4.0.0-beta.5",
|
||||
"csv-stringify": "^6.6.0",
|
||||
"emoji-regex": "^10.4.0",
|
||||
"emojis-list": "2.1.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"fs-extra": "^11.3.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"koa-proxies": "^0.12.4",
|
||||
"koa-static-cache": "^5.1.4",
|
||||
|
||||
@@ -22,13 +22,13 @@ langs:
|
||||
__hint__:
|
||||
- ~ All keys starting with _ will be ignored
|
||||
- ~ Entries named like [foo], [foo].[bar] [foo].[baz]
|
||||
- will be merged into one category.
|
||||
- . will be merged into one category.
|
||||
- ~ A entry named [foo].[bar] will inhert all options
|
||||
- defined in entry [foo] if not overwritten
|
||||
- . defined in entry [foo] if not overwritten
|
||||
- ~ code_file option defaults to foo.[name]
|
||||
- ~ highlight option defaults to [name]
|
||||
- ~ if highlight option was set, monaco option defaults
|
||||
- to [highlight], otherwise defaults to [name]
|
||||
- . to [highlight], otherwise defaults to [name]
|
||||
- ~ Compiling be be ignored if compile option wasn't set
|
||||
- ~ Key marked with a ? means the option is optional
|
||||
- (name):
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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,4 +1,3 @@
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import '../lib/index';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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,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
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 将在五秒后继续启动。');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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']>(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,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';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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` });
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
@@ -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,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';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
} from 'hydrooj';
|
||||
|
||||
function unescapedString(escapedString: string) {
|
||||
escapedString += new Array(5 - (escapedString.length % 4)).join('=');
|
||||
escapedString += Array.from({ length: 5 - (escapedString.length % 4) }).join('=');
|
||||
return escapedString.replace(/-/g, '+').replace(/_/g, '/');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable no-tabs */
|
||||
/* eslint-disable style/no-tabs */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import path from 'path';
|
||||
import mariadb from 'mariadb';
|
||||
@@ -70,7 +70,7 @@ export async function run({
|
||||
host = 'localhost', port = 3306, name = 'jol',
|
||||
username, password, domainId, contestType = 'oi',
|
||||
dataDir, uploadDir = '/home/judge/src/web/upload/', rerun = true, randomMail = false,
|
||||
}, report: Function) {
|
||||
}, report: (data: any) => void) {
|
||||
let remoteUsed = false;
|
||||
const src = await mariadb.createConnection({
|
||||
host,
|
||||
@@ -191,7 +191,7 @@ export async function run({
|
||||
samples: [[pdoc.sample_input.trim(), pdoc.sample_output.trim()]],
|
||||
hint: pdoc.hint,
|
||||
source: pdoc.source,
|
||||
}, 'html').replace(/<math xm<x>lns=/g, '<math xmlns=').replace(/\[\/?md]/g, '');
|
||||
}, 'html').replace(/<math xm<x>lns=/g, '<math xmlns=').replace(/\[\/?md\]/g, '');
|
||||
const uploadFiles = content.matchAll(/(?:src|href)="\/upload\/([^"/]+)(?:\/([^"/]+))?\/([^"/]+\.[^"/.]+)"/g);
|
||||
for (const file of uploadFiles) {
|
||||
try {
|
||||
@@ -295,7 +295,7 @@ hydrooj install https://hydro.ac/hydroac-client.zip
|
||||
await Promise.all(Object.keys(files).map((filename) => addContestFile(domainId, tid, filename, files[filename])));
|
||||
if (Object.keys(files).length) report({ message: `move ${Object.keys(files).length} file for contest ${tidMap[tdoc.contest_id]}` });
|
||||
|
||||
const allowedUser:{ user_id:string }[] = await query(`SELECT * FROM privilege WHERE rightstr = 'c${tdoc.contest_id}';`);
|
||||
const allowedUser: { user_id: string }[] = await query(`SELECT * FROM privilege WHERE rightstr = 'c${tdoc.contest_id}';`);
|
||||
const assignUserList = allowedUser.map((i) => uidMap[i.user_id]).filter((i) => i);
|
||||
if (isAssignMode) {
|
||||
await ContestModel.edit(domainId, tid, {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable no-tabs */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable style/no-tabs */
|
||||
|
||||
import path from 'path';
|
||||
import zlib from 'zlib';
|
||||
import mariadb from 'mariadb';
|
||||
@@ -34,7 +35,7 @@ export async function run({
|
||||
host = 'localhost', port = 3306, name,
|
||||
username, password, domainId, contestType = 'oi',
|
||||
dataDir, imageDir, rerun = true, randomMail = false,
|
||||
}, report: Function) {
|
||||
}, report: (data: any) => void) {
|
||||
const src = await mariadb.createConnection({
|
||||
host,
|
||||
port,
|
||||
@@ -150,6 +151,7 @@ export async function run({
|
||||
hint: pdoc.hint,
|
||||
source: pdoc.source,
|
||||
}, 'html');
|
||||
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
||||
const uploadFiles = content.matchAll(/(?:src|href)="\/images\/([^"]+\/([^"]+))"/g);
|
||||
for (const file of uploadFiles) {
|
||||
try {
|
||||
|
||||
@@ -75,7 +75,7 @@ export async function run({
|
||||
host = 'localhost', port = 3306, name = 'syzoj',
|
||||
username, password, domainId, dataDir,
|
||||
rerun = true, randomMail = 'never',
|
||||
}, report: Function) {
|
||||
}, report: (data: any) => void) {
|
||||
const src = await mariadb.createConnection({
|
||||
host,
|
||||
port,
|
||||
@@ -199,7 +199,7 @@ export async function run({
|
||||
`publicize_time` datetime NULL DEFAULT NULL, 公开时间
|
||||
`type` enum('traditional','submit-answer','interaction') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'traditional',
|
||||
*/
|
||||
const fileReg = /\[.*\]\((\/problem\/(\d+)\/testdata\/download\/(.*))\)/gm;
|
||||
const fileReg = /\[.*\]\((\/problem\/(\d+)\/testdata\/download\/(.*))\)/g;
|
||||
const pidMap: Record<string, number> = {};
|
||||
const configMap: Record<string, string> = {};
|
||||
const problemAdditionalFile = {};
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function run({
|
||||
host = '172.17.0.2', port = 3306, name = 'app_uoj233',
|
||||
username, password, domainId, dataDir,
|
||||
rerun = true, randomMail = false,
|
||||
}, report: Function) {
|
||||
}, report: (data: any) => void) {
|
||||
const src = await mariadb.createConnection({
|
||||
host,
|
||||
port,
|
||||
@@ -506,10 +506,10 @@ export async function run({
|
||||
if (confInfo.n_subtasks
|
||||
&& Object.keys(confInfo.subtask_end).length === +confInfo.n_subtasks
|
||||
&& Object.keys(confInfo.subtask_score).length === +confInfo.n_subtasks) {
|
||||
config.subtasks.push(...[...new Array(+confInfo.n_subtasks)].map((v, i) => i + 1).map((i) => ({
|
||||
config.subtasks.push(...[...Array.from({ length: +confInfo.n_subtasks })].map((v, i) => i + 1).map((i) => ({
|
||||
id: +i,
|
||||
score: confInfo.subtask_score[i],
|
||||
cases: [...new Array(confInfo.subtask_end[i] - (confInfo.subtask_end[i - 1] || 0))].map((v, j) => ({
|
||||
cases: [...Array.from({ length: confInfo.subtask_end[i] - (confInfo.subtask_end[i - 1] || 0) })].map((v, j) => ({
|
||||
input: `${confInfo.input_pre}${j + (confInfo.subtask_end[i - 1] || 0) + 1}.${confInfo.input_suf}`,
|
||||
output: `${confInfo.output_pre}${j + (confInfo.subtask_end[i - 1] || 0) + 1}.${confInfo.output_suf}`,
|
||||
})),
|
||||
@@ -521,7 +521,7 @@ export async function run({
|
||||
id: 1,
|
||||
score: 97,
|
||||
type: 'sum' as SubtaskType,
|
||||
cases: [...new Array(+confInfo.n_tests)].map((v, i) => i + 1).map((i) => ({
|
||||
cases: [...Array.from({ length: +confInfo.n_tests })].map((v, i) => i + 1).map((i) => ({
|
||||
input: `${confInfo.input_pre}${i}.${confInfo.input_suf}`,
|
||||
output: `${confInfo.output_pre}${i}.${confInfo.output_suf}`,
|
||||
})),
|
||||
@@ -530,7 +530,7 @@ export async function run({
|
||||
config.subtasks.push({
|
||||
id: Math.max(...config.subtasks.map((i) => i.id)) + 1,
|
||||
score: 3,
|
||||
cases: [...new Array(+confInfo.n_ex_tests)].map((v, i) => i + 1).map((i) => ({
|
||||
cases: [...Array.from({ length: +confInfo.n_ex_tests })].map((v, i) => i + 1).map((i) => ({
|
||||
input: `ex_${confInfo.input_pre}${i}.${confInfo.input_suf}`,
|
||||
output: `ex_${confInfo.output_pre}${i}.${confInfo.output_suf}`,
|
||||
})),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/* eslint-disable ts/naming-convention */
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import mongodb, { Db, FindCursor } from 'mongodb';
|
||||
import {
|
||||
@@ -13,6 +13,8 @@ const pid = (id) => {
|
||||
return id;
|
||||
};
|
||||
|
||||
type Report = (args: { progress?: number, message?: string }) => void;
|
||||
|
||||
const tasks = {
|
||||
user: async (doc) => ({
|
||||
_id: doc._id,
|
||||
@@ -93,7 +95,7 @@ const tasks = {
|
||||
processer: (rule) => {
|
||||
const n = {};
|
||||
for (const key in rule) {
|
||||
n[parseInt(key, 10) / 3600] = rule;
|
||||
n[Number.parseInt(key, 10) / 3600] = rule;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
@@ -282,7 +284,7 @@ const cursor: NodeJS.Dict<CursorGetter> = {
|
||||
file: (s) => s.collection('fs.files').find(),
|
||||
};
|
||||
|
||||
async function discussionNode(src: Db, report: Function) {
|
||||
async function discussionNode(src: Db, report: Report) {
|
||||
const count = await src.collection('document').countDocuments({ doc_type: 20 });
|
||||
await report({ progress: 1, message: `discussion.node: ${count}` });
|
||||
const total = Math.floor(count / 5);
|
||||
@@ -327,7 +329,7 @@ async function fix(doc) {
|
||||
);
|
||||
}
|
||||
|
||||
async function fixProblem(report: Function) {
|
||||
async function fixProblem(report: Report) {
|
||||
const count = await dst.collection('document').countDocuments({ docType: 10 });
|
||||
await report({ progress: 1, message: `Fix pid: ${count}` });
|
||||
const total = Math.floor(count / 50);
|
||||
@@ -349,7 +351,7 @@ function objid(ts: Date) {
|
||||
}
|
||||
|
||||
// FIXME this seems not working
|
||||
async function message(src: Db, report: Function) {
|
||||
async function message(src: Db, report: Report) {
|
||||
const count = await src.collection('message').countDocuments();
|
||||
await report({ progress: 1, message: `Messages: ${count}` });
|
||||
const total = Math.floor(count / 50);
|
||||
@@ -373,7 +375,7 @@ async function message(src: Db, report: Function) {
|
||||
}
|
||||
}
|
||||
|
||||
async function removeInvalidPid(report: Function) {
|
||||
async function removeInvalidPid(report: Report) {
|
||||
const count = await dst.collection('document').countDocuments({ docType: 10 });
|
||||
const bulk = dst.collection('document').initializeUnorderedBulkOp();
|
||||
await report({ progress: 1, message: `Remove pid: ${count}` });
|
||||
@@ -383,7 +385,7 @@ async function removeInvalidPid(report: Function) {
|
||||
.find({ docType: 10 }).skip(i * 50).limit(50)
|
||||
.toArray();
|
||||
for (const doc of docs) {
|
||||
const id = parseInt(doc.pid, 10);
|
||||
const id = Number.parseInt(doc.pid, 10);
|
||||
if (Number.isSafeInteger(id)) {
|
||||
bulk.find({ _id: doc._id }).updateOne({ $unset: { pid: '' } });
|
||||
}
|
||||
@@ -392,7 +394,7 @@ async function removeInvalidPid(report: Function) {
|
||||
}
|
||||
}
|
||||
|
||||
async function task(name: any, src: Db, report: Function) {
|
||||
async function task(name: any, src: Db, report: Report) {
|
||||
await report({ progress: 1, message: `${name}` });
|
||||
let lastProgress = -1;
|
||||
for (let i = 0; ; i++) {
|
||||
@@ -466,7 +468,7 @@ async function task(name: any, src: Db, report: Function) {
|
||||
|
||||
export async function run({
|
||||
host = 'localhost', port = 27017, name = 'vijos4', username, password,
|
||||
}, report: Function) {
|
||||
}, report: Report) {
|
||||
let mongourl = 'mongodb://';
|
||||
if (username) mongourl += `${username}:${password}@`;
|
||||
mongourl += `${host}:${port}/${name}`;
|
||||
|
||||
@@ -139,25 +139,25 @@ export function apply(ctx: Context) {
|
||||
solved: +i === STATUS.STATUS_ACCEPTED,
|
||||
})),
|
||||
getFeed('languages', {
|
||||
id: 'c', name: 'C', 'entry_point_required': false, extensions: ['c'],
|
||||
id: 'c', name: 'C', entry_point_required: false, extensions: ['c'],
|
||||
}),
|
||||
getFeed('languages', {
|
||||
id: 'cpp', name: 'C++', 'entry_point_required': false, extensions: ['cpp', 'cc', 'cxx', 'c++'],
|
||||
id: 'cpp', name: 'C++', entry_point_required: false, extensions: ['cpp', 'cc', 'cxx', 'c++'],
|
||||
}),
|
||||
getFeed('languages', {
|
||||
id: 'java', name: 'Java', 'entry_point_required': true, extensions: ['java'],
|
||||
id: 'java', name: 'Java', entry_point_required: true, extensions: ['java'],
|
||||
}),
|
||||
getFeed('languages', {
|
||||
id: 'python3', name: 'Python 3', 'entry_point_required': false, 'entry_point_name': 'Main file', extensions: ['py', 'py3'],
|
||||
id: 'python3', name: 'Python 3', entry_point_required: false, entry_point_name: 'Main file', extensions: ['py', 'py3'],
|
||||
}),
|
||||
getFeed('languages', {
|
||||
id: 'kotlin', name: 'Kotlin', 'entry_point_required': true, extensions: ['kt'],
|
||||
id: 'kotlin', name: 'Kotlin', entry_point_required: true, extensions: ['kt'],
|
||||
}),
|
||||
getFeed('languages', {
|
||||
id: 'rust', name: 'Rust', 'entry_point_required': true, extensions: ['rs'],
|
||||
id: 'rust', name: 'Rust', entry_point_required: true, extensions: ['rs'],
|
||||
}),
|
||||
getFeed('languages', {
|
||||
id: 'go', name: 'Go', 'entry_point_required': true, extensions: ['go'],
|
||||
id: 'go', name: 'Go', entry_point_required: true, extensions: ['go'],
|
||||
}),
|
||||
getFeed('groups', { id: 'participants', name: '正式队伍' }),
|
||||
getFeed('groups', { id: 'observers', name: '打星队伍' }),
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
} from 'hydrooj';
|
||||
|
||||
const file = fs.readFileSync(path.join(__dirname, 'public/assets/board.html'), 'utf8');
|
||||
const indexJs = file.match(/index-([A-Za-z0-9_-]+)\.js"/)?.[1];
|
||||
const indexCss = file.match(/index-([A-Za-z0-9_-]+)\.css"/)?.[1];
|
||||
const indexJs = file.match(/index-([\w-]+)\.js"/)?.[1];
|
||||
const indexCss = file.match(/index-([\w-]+)\.css"/)?.[1];
|
||||
const status = {
|
||||
[STATUS.STATUS_WRONG_ANSWER]: 'WRONG_ANSWER',
|
||||
[STATUS.STATUS_ACCEPTED]: 'CORRECT',
|
||||
@@ -72,9 +72,9 @@ export async function apply(ctx: Context) {
|
||||
},
|
||||
organization: 'School',
|
||||
status_time_display: {
|
||||
'correct': true,
|
||||
'incorrect': true,
|
||||
'pending': true,
|
||||
correct: true,
|
||||
incorrect: true,
|
||||
pending: true,
|
||||
},
|
||||
medal: {
|
||||
official: {
|
||||
|
||||
+10
-10
@@ -1,23 +1,23 @@
|
||||
/* eslint-disable import/first */
|
||||
export * from './utils';
|
||||
export { default as Notification } from './components/notification';
|
||||
export * from './components/dialog';
|
||||
export { Context, Service, ctx } from './context';
|
||||
export { AnsiUp } from 'ansi_up';
|
||||
export { default as loadMonaco } from './components/monaco/loader';
|
||||
export { default as Notification } from './components/notification';
|
||||
export { default as Socket } from './components/socket/index';
|
||||
export { default as uploadFiles } from './components/upload';
|
||||
export * as redux from 'react-redux';
|
||||
export * from './components/zipDownloader';
|
||||
export * from '@hydrooj/utils/lib/common';
|
||||
export { default as download } from './components/zipDownloader';
|
||||
export { Context, ctx, Service } from './context';
|
||||
export { initPageLoader } from './hydro';
|
||||
export * from './lazyload';
|
||||
export * from './misc/Page';
|
||||
export * from './utils';
|
||||
export * from '@hydrooj/utils/lib/common';
|
||||
export { AnsiUp } from 'ansi_up';
|
||||
export { default as $ } from 'jquery';
|
||||
export { default as _ } from 'lodash';
|
||||
export { default as React } from 'react';
|
||||
export { default as ReactDOM } from 'react-dom/client';
|
||||
export * from './misc/Page';
|
||||
export { initPageLoader } from './hydro';
|
||||
export * from './lazyload';
|
||||
export * as redux from 'react-redux';
|
||||
import { load } from './lazyload';
|
||||
|
||||
export default load;
|
||||
@@ -30,7 +30,7 @@ import ProblemSelectAutoComplete from './components/autocomplete/ProblemSelectAu
|
||||
import UserSelectAutoComplete from './components/autocomplete/UserSelectAutoComplete';
|
||||
|
||||
export {
|
||||
AutoComplete, UserSelectAutoComplete, ProblemSelectAutoComplete, DomainSelectAutoComplete, CustomSelectAutoComplete,
|
||||
AutoComplete, CustomSelectAutoComplete, DomainSelectAutoComplete, ProblemSelectAutoComplete, UserSelectAutoComplete,
|
||||
};
|
||||
export function addPage(page: import('./misc/Page').Page | (() => Promise<void> | void)) {
|
||||
window.Hydro.extraPages.push(page);
|
||||
|
||||
@@ -10,7 +10,7 @@ import esbuild from 'esbuild';
|
||||
|
||||
declare module 'hydrooj' {
|
||||
interface UI {
|
||||
esbuildPlugins?: esbuild.Plugin[]
|
||||
esbuildPlugins?: esbuild.Plugin[];
|
||||
}
|
||||
interface SystemKeys {
|
||||
'ui-default.nav_logo_dark': string;
|
||||
@@ -91,7 +91,7 @@ export async function buildUI() {
|
||||
const entryPoints: string[] = [];
|
||||
const lazyModules: string[] = [];
|
||||
const newFiles = ['entry.js'];
|
||||
for (const addon of Object.values(global.addons)) {
|
||||
for (const addon of Object.values(global.addons) as string[]) {
|
||||
let publicPath = resolve(addon, 'frontend');
|
||||
if (!fs.existsSync(publicPath)) publicPath = resolve(addon, 'public');
|
||||
if (!fs.existsSync(publicPath)) continue;
|
||||
|
||||
@@ -8,7 +8,7 @@ function parseNextNumber(str, pos, max) {
|
||||
value: '',
|
||||
};
|
||||
let code = str.charCodeAt(pos);
|
||||
while (pos < max && (code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */) || code === 0x25 /* % */) {
|
||||
while (pos < max && ((code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */) || code === 0x25 /* % */)) {
|
||||
code = str.charCodeAt(++pos);
|
||||
}
|
||||
result.ok = true;
|
||||
@@ -24,9 +24,9 @@ function parseImageSize(str, pos, max) {
|
||||
width: '',
|
||||
height: '',
|
||||
};
|
||||
if (pos >= max) { return result; }
|
||||
if (pos >= max) return result;
|
||||
let code = str.charCodeAt(pos);
|
||||
if (code !== 0x3d /* = */) { return result; }
|
||||
if (code !== 0x3D /* = */) return result;
|
||||
pos++;
|
||||
code = str.charCodeAt(pos);
|
||||
if (code !== 0x78 /* x */ && (code < 0x30 || code > 0x39) /* [0-9] */) {
|
||||
@@ -67,14 +67,14 @@ export default function plugin(md) {
|
||||
const oldPos = state.pos;
|
||||
const max = state.posMax;
|
||||
|
||||
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }
|
||||
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; }
|
||||
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) return false;
|
||||
if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) return false;
|
||||
|
||||
const labelStart = state.pos + 2;
|
||||
const labelEnd = md.helpers.parseLinkLabel(state, state.pos + 1, false);
|
||||
|
||||
// parser failed to find ']', so it's not a valid link
|
||||
if (labelEnd < 0) { return false; }
|
||||
if (labelEnd < 0) return false;
|
||||
|
||||
pos = labelEnd + 1;
|
||||
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
|
||||
@@ -89,7 +89,7 @@ export default function plugin(md) {
|
||||
code = state.src.charCodeAt(pos);
|
||||
if (code !== 0x20 && code !== 0x0A) { break; }
|
||||
}
|
||||
if (pos >= max) { return false; }
|
||||
if (pos >= max) return false;
|
||||
|
||||
// [link]( <href> "title" )
|
||||
// ^^^^^^ parsing link destination
|
||||
@@ -162,7 +162,7 @@ export default function plugin(md) {
|
||||
//
|
||||
// Link reference
|
||||
//
|
||||
if (typeof state.env.references === 'undefined') { return false; }
|
||||
if (typeof state.env.references === 'undefined') return false;
|
||||
|
||||
// [foo] [bar]
|
||||
// ^^ optional whitespace (can include newlines)
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function plugin(md) {
|
||||
const render = (latex: string, displayMode = false) => {
|
||||
options.displayMode = displayMode;
|
||||
try {
|
||||
latex = latex.replace(/\\def{\\([a-zA-Z0-9]+)}/g, '\\def\\$1');
|
||||
latex = latex.replace(/\\def\{\\([a-zA-Z0-9]+)\}/g, '\\def\\$1');
|
||||
return katex.renderToString(latex, options);
|
||||
} catch (error) {
|
||||
if (options.throwOnError) console.error(error);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
/* eslint-disable max-len */
|
||||
/* eslint-disable regexp/prefer-question-quantifier */
|
||||
/* eslint-disable regexp/no-useless-non-capturing-group */
|
||||
/* eslint-disable regexp/optimal-quantifier-concatenation */
|
||||
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
@@ -10,6 +13,7 @@ function youtubeParser(url: string) {
|
||||
const match = url.match(ytRegex);
|
||||
return match && match[7].length === 11 ? match[7] : url;
|
||||
}
|
||||
// eslint-disable-next-line regexp/no-empty-alternative
|
||||
const vimeoRegex = /https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|)(\d+)(?:$|\/|\?)/;
|
||||
function vimeoParser(url: string) {
|
||||
const match = url.match(vimeoRegex);
|
||||
@@ -25,7 +29,7 @@ function preziParser(url: string) {
|
||||
const match = url.match(preziRegex);
|
||||
return match ? match[1] : url;
|
||||
}
|
||||
const EMBED_REGEX = /@\[([a-zA-Z].+?)]\((.*?)[)]/im;
|
||||
const EMBED_REGEX = /@\[([a-zA-Z].+?)\]\((.*?)\)/;
|
||||
function extractVideoParameters(url: string) {
|
||||
const parameterMap = new Map();
|
||||
const params = url.replace(/&/gi, '&').split(/[#?&]/);
|
||||
@@ -83,7 +87,7 @@ declare module 'hydrooj' {
|
||||
interface ModuleInterfaces {
|
||||
richmedia: {
|
||||
get: (service: string, src: string, md: MarkdownIt) => string | null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const depedentTags = {
|
||||
dt: ['dl'],
|
||||
dd: ['dl'],
|
||||
};
|
||||
const whitelistClasses = ['row', 'columns', 'typo', 'note', 'warn'].concat(new Array(12).fill(0).map((_, i) => `medium-${i + 1}`));
|
||||
const whitelistClasses = ['row', 'columns', 'typo', 'note', 'warn'].concat(Array.from({ length: 12 }).fill(0).map((_, i) => `medium-${i + 1}`));
|
||||
|
||||
const tagCheck = new FilterXSS({
|
||||
css: false,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { moment, ObjectId } from 'hydrooj';
|
||||
import { AnsiUp } from 'ansi_up';
|
||||
|
||||
export { size, formatSeconds } from 'hydrooj';
|
||||
export { formatSeconds, size } from 'hydrooj';
|
||||
|
||||
const AU = new AnsiUp();
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ async function getFiles(folder: string, base = ''): Promise<string[]> {
|
||||
files.push(...await getFiles(path.join(folder, i), path.join(base, i)));
|
||||
} else files.push(path.join(base, i));
|
||||
}
|
||||
return files.map((item) => item.replace(/\\/gmi, '/'));
|
||||
return files.map((item) => item.replace(/\\/g, '/'));
|
||||
}
|
||||
|
||||
function locateFile(basePath: string, filenames: string[]) {
|
||||
@@ -47,7 +47,7 @@ nunjucks.runtime.memberLookup = function memberLookup(obj, val) {
|
||||
if (obj === undefined || obj === null) return undefined;
|
||||
if (typeof obj[val] === 'function') {
|
||||
const fn = function (...args) {
|
||||
return obj[val].call(obj, ...args);
|
||||
return obj[val].call(obj, ...args); // eslint-disable-line no-useless-call
|
||||
};
|
||||
fn._original = obj[val];
|
||||
return fn;
|
||||
@@ -255,7 +255,7 @@ export class TemplateService extends Service {
|
||||
const pending = Object.values(global.addons);
|
||||
const logger = this.ctx.logger('template');
|
||||
for (const i of pending) {
|
||||
const p = locateFile(i, ['template', 'templates']);
|
||||
const p = locateFile(i as string, ['template', 'templates']);
|
||||
if (p && (await fs.stat(p)).isDirectory()) {
|
||||
try {
|
||||
const files = await getFiles(p);
|
||||
|
||||
@@ -108,7 +108,7 @@ export default async function (env: { watch?: boolean, production?: boolean, mea
|
||||
devtool: env.production ? 'source-map' : false,
|
||||
entry: {
|
||||
[`hydro-${version}`]: './entry.js',
|
||||
'sentry': './sentry.ts',
|
||||
sentry: './sentry.ts',
|
||||
'default.theme': './theme/default.js',
|
||||
'service-worker': './service-worker.ts',
|
||||
},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/no-import-module-exports */
|
||||
import path from 'path';
|
||||
import { size } from '@hydrooj/utils/lib/utils';
|
||||
import cac from 'cac';
|
||||
@@ -106,7 +105,7 @@ async function main() {
|
||||
classNamePrefix: 'icon',
|
||||
fontName: 'hydro-icons',
|
||||
css: true,
|
||||
startUnicode: 0xea01,
|
||||
startUnicode: 0xEA01,
|
||||
svg2ttf: {
|
||||
timestamp: 1577836800, // 2020-1-1
|
||||
},
|
||||
|
||||
@@ -118,7 +118,6 @@ export default class DOMAttachedObject {
|
||||
}
|
||||
|
||||
constructor($dom: JQuery<HTMLElement>, monitorDetach = false) {
|
||||
// eslint-disable-next-line no-constructor-return
|
||||
if ($dom == null) return null;
|
||||
this.$dom = $dom;
|
||||
this.id = ++DOMAttachedObject.uniqueIdCounter;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CustomSelectAutoComplete as CustomSelectAutoCompleteFC } from '@hydrooj
|
||||
import AutoComplete, { AutoCompleteOptions } from '.';
|
||||
|
||||
interface CustomSelectOptions {
|
||||
data: any[]
|
||||
data: any[];
|
||||
}
|
||||
|
||||
export default class CustomSelectAutoComplete<Multi extends boolean> extends AutoComplete<CustomSelectOptions> {
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface AutoCompleteOptions<Multi extends boolean = boolean> {
|
||||
text?: () => string;
|
||||
}
|
||||
|
||||
export default class AutoComplete<Options extends Record<string, any> = {}, Multi extends boolean = boolean> extends DOMAttachedObject {
|
||||
export default class AutoComplete<Options extends Record<string, any> = object, Multi extends boolean = boolean> extends DOMAttachedObject {
|
||||
static DOMAttachKey = 'ucwAutoCompleteInstance';
|
||||
ref = null;
|
||||
container = document.createElement('div');
|
||||
|
||||
@@ -215,7 +215,7 @@ export default class Calendar {
|
||||
const daysByWeek = _.chunk(days, 7);
|
||||
|
||||
const numberOfWeeks = days.length / 7;
|
||||
const bannersByWeek = _.fill(new Array(numberOfWeeks), 1).map(() => []);
|
||||
const bannersByWeek = _.fill(Array.from({ length: numberOfWeeks }), 1).map(() => []);
|
||||
const beginDate = days[0].date.clone();
|
||||
const endDate = _.last(days).date.clone();
|
||||
|
||||
@@ -268,7 +268,7 @@ export default class Calendar {
|
||||
]))
|
||||
.map((banners) => {
|
||||
const dayBitmap = _
|
||||
.fill(new Array(7), 1)
|
||||
.fill(Array.from({ length: 7 }), 1)
|
||||
.map(() => []);
|
||||
banners.forEach((banner) => {
|
||||
const beginDay = banner.beginAt.day();
|
||||
@@ -292,7 +292,7 @@ export default class Calendar {
|
||||
// merge adjacent cells and arrange banners by vertical index
|
||||
const vMaxLength = _.max(_.range(0, 7).map((day) => dayBitmap[day].length));
|
||||
const weekBanners = _
|
||||
.fill(new Array(vMaxLength), 1)
|
||||
.fill(Array.from({ length: vMaxLength }), 1)
|
||||
.map(() => []);
|
||||
for (let vIndex = 0; vIndex < vMaxLength; ++vIndex) {
|
||||
let last = { span: 1, banner: dayBitmap[0][vIndex] };
|
||||
|
||||
@@ -27,12 +27,14 @@ export default new AutoloadPage('customFont', (pageName) => {
|
||||
if (!dialog) {
|
||||
const fonts = await getAvailableFonts();
|
||||
dialog = new ActionDialog({
|
||||
$body: $(tpl(<CustomSelectAutoComplete
|
||||
ref={(el) => { handle = el; }}
|
||||
allowEmptyQuery={false}
|
||||
data={fonts}
|
||||
renderItem={(item) => <div style={{ fontFamily: item.id }}>{item.name}</div>}
|
||||
/>, true)),
|
||||
$body: $(tpl(
|
||||
<CustomSelectAutoComplete
|
||||
ref={(el) => { handle = el; }}
|
||||
allowEmptyQuery={false}
|
||||
data={fonts}
|
||||
renderItem={(item) => <div style={{ fontFamily: item.id }}>{item.name}</div>}
|
||||
/>, true,
|
||||
)),
|
||||
onDispatch: (action) => {
|
||||
selection = handle.getSelectedItems()?.[0]?.id;
|
||||
return action !== 'ok' || selection;
|
||||
|
||||
@@ -144,24 +144,29 @@ export async function prompt<T extends string>(title: string, fields: Record<T,
|
||||
<div className="textbox-container">
|
||||
{field.type === 'text'
|
||||
? <input
|
||||
type="text" className="textbox" data-autofocus={field.autofocus}
|
||||
type="text"
|
||||
className="textbox"
|
||||
data-autofocus={field.autofocus}
|
||||
defaultValue={field.default}
|
||||
onChange={(e) => setValues({ ...values, [name]: e.target.value })}
|
||||
/>
|
||||
: field.type === 'user'
|
||||
? <UserSelectAutoComplete
|
||||
data-autofocus={field.autofocus} selectedKeys={values[name] ? [values[name].toString()] : []}
|
||||
data-autofocus={field.autofocus}
|
||||
selectedKeys={values[name] ? [values[name].toString()] : []}
|
||||
onChange={(e) => setValues({ ...values, [name]: +e })}
|
||||
byId
|
||||
/>
|
||||
: <DomainSelectAutoComplete
|
||||
data-autofocus={field.autofocus} selectedKeys={values[name] ? [values[name]] : []}
|
||||
data-autofocus={field.autofocus}
|
||||
selectedKeys={values[name] ? [values[name]] : []}
|
||||
onChange={(e) => setValues({ ...values, [name]: e })}
|
||||
/>}
|
||||
</div>
|
||||
</label>}
|
||||
{field.type === 'checkbox' && <label className="checkbox">
|
||||
<input type="checkbox"
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultChecked={field.default === 'true'}
|
||||
onChange={(e) => setValues({ ...values, [name]: !!e.target.checked })}
|
||||
/>
|
||||
|
||||
@@ -62,8 +62,8 @@ function Reaction({ payload, ele }) {
|
||||
<input name="emojiSuggest" onFocus={() => updateFocus(true)} onBlur={() => updateFocus(false)}></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}>
|
||||
</div>}
|
||||
>
|
||||
<span className="icon icon-emoji"></span>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -169,9 +169,9 @@ export default class Editor extends DOMAttachedObject {
|
||||
const [val, setVal] = React.useState(value);
|
||||
that.setMarkdownEditorValue = setVal;
|
||||
return <MdEditor
|
||||
className='textbox'
|
||||
className="textbox"
|
||||
autoFocus={hasFocus}
|
||||
codeTheme='github'
|
||||
codeTheme="github"
|
||||
codeStyleReverse={false}
|
||||
ref={renderCallback}
|
||||
value={val}
|
||||
|
||||
@@ -73,7 +73,7 @@ function addInvisibles(grammar) {
|
||||
for (const name in grammar) {
|
||||
if (Object.prototype.hasOwnProperty.call(grammar, name) && !invisibles[name]) {
|
||||
if (name === 'rest') addInvisibles(grammar.rest);
|
||||
else handlerInvisiblesToken(grammar, name); // eslint-disable-line @typescript-eslint/no-use-before-define
|
||||
else handlerInvisiblesToken(grammar, name); // eslint-disable-line ts/no-use-before-define
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ class Marker {
|
||||
}
|
||||
|
||||
constructor() {
|
||||
// eslint-disable-next-line no-constructor-return
|
||||
if (Marker.exists()) return markerInstance;
|
||||
if (markerInstance) {
|
||||
markerInstance.destroy();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-restricted-globals */
|
||||
/// <reference no-default-lib="true" />
|
||||
/// <reference lib="webworker" />
|
||||
import ReconnectingWebsocket from 'reconnecting-websocket';
|
||||
@@ -64,10 +63,10 @@ function initConn(path: string, port: MessagePort, cookie: any) {
|
||||
console.log('Connected');
|
||||
broadcastMsg({ type: 'open' });
|
||||
conn.send(JSON.stringify({
|
||||
'operation': 'subscribe',
|
||||
'request_id': Math.random().toString(16).substring(2),
|
||||
'credential': lcookie,
|
||||
'channels': ['message'],
|
||||
operation: 'subscribe',
|
||||
request_id: Math.random().toString(16).substring(2),
|
||||
credential: lcookie,
|
||||
channels: ['message'],
|
||||
}));
|
||||
};
|
||||
conn.onerror = () => broadcastMsg({ type: 'error' });
|
||||
|
||||
@@ -94,11 +94,11 @@ export default connect(mapStateToProps)(class MessagePadDialogueContentContainer
|
||||
<>
|
||||
<div className="messagepad__header">
|
||||
{ this.props.item
|
||||
&& (
|
||||
<a className="messagepad__content__header__title" href={`/user/${this.props.item.udoc._id}`}>
|
||||
{`${this.props.item.udoc.uname}(UID: ${this.props.item.udoc._id})`}
|
||||
</a>
|
||||
)}
|
||||
&& (
|
||||
<a className="messagepad__content__header__title" href={`/user/${this.props.item.udoc._id}`}>
|
||||
{`${this.props.item.udoc.uname}(UID: ${this.props.item.udoc._id})`}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<ol className="messagepad__content" ref="list">
|
||||
{this.renderInner()}
|
||||
|
||||
@@ -51,7 +51,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MessagePadInpu
|
||||
componentDidUpdate(prevProps) {
|
||||
this.focusInput = (
|
||||
this.props.activeId !== prevProps.activeId
|
||||
|| prevProps.isPosting !== this.props.isPosting && this.props.isPosting === false
|
||||
|| (prevProps.isPosting !== this.props.isPosting && this.props.isPosting === false)
|
||||
);
|
||||
if (this.focusInput) {
|
||||
const { scrollX, scrollY } = window;
|
||||
|
||||
@@ -12,7 +12,7 @@ function format(message, args) {
|
||||
return result;
|
||||
}
|
||||
|
||||
let CURRENT_LOCALE_DATA = {}; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
let CURRENT_LOCALE_DATA = {}; // eslint-disable-line ts/naming-convention
|
||||
|
||||
export function localize(path, message, ...args) {
|
||||
return format(CURRENT_LOCALE_DATA[path.key || path] || CURRENT_LOCALE_DATA[message] || message, args);
|
||||
|
||||
@@ -159,7 +159,7 @@ export async function previewFile(ev?, type = '') {
|
||||
Notification.info(i18n('Loading file...'));
|
||||
try {
|
||||
const { url } = await request.get(link, {}, { headers: { Pragma: 'no-cache' } });
|
||||
if (/^(doc|xls|ppt)x?$/.test(ext)) return previewOffice(link, url);
|
||||
if (/^(?:doc|xls|ppt)x?$/.test(ext)) return previewOffice(link, url);
|
||||
content = await request.get(url, undefined, { dataType: 'text' });
|
||||
} catch (e) {
|
||||
Notification.error(i18n('Failed to load file: {0}', e.message));
|
||||
|
||||
@@ -24,7 +24,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
interface Props {
|
||||
config: any;
|
||||
handleUpdateCode: Function;
|
||||
handleUpdateCode: (val: string) => void;
|
||||
}
|
||||
|
||||
const configKey = [
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ProblemConfigTree } from './ProblemConfigTree';
|
||||
import { RootState } from './reducer';
|
||||
|
||||
interface Props {
|
||||
onSave: () => void
|
||||
onSave: () => void;
|
||||
}
|
||||
|
||||
export default function ProblemConfig(props: Props) {
|
||||
@@ -31,8 +31,12 @@ export default function ProblemConfig(props: Props) {
|
||||
<Tabs onChange={(t) => (t !== 'errors' && setSelected(t.toString()))} selectedTabId={valid ? selected : 'errors'}>
|
||||
<Tab id="basic" disabled={!valid} title={i18n('Basic')} panel={<ProblemConfigForm />} />
|
||||
<Tab id="subtasks" disabled={!valid} title={i18n('Subtasks')} panel={<ProblemConfigTree />} />
|
||||
<Tab id="errors" disabled={valid} title={errors.length ? `Errors(${errors.length})` : 'No Errors'} panel={
|
||||
<div>{errors.map((i) => (<pre key={i}>{i}</pre>))}</div>} />
|
||||
<Tab
|
||||
id="errors"
|
||||
disabled={valid}
|
||||
title={errors.length ? `Errors(${errors.length})` : 'No Errors'}
|
||||
panel={<div>{errors.map((i) => (<pre key={i}>{i}</pre>))}</div>}
|
||||
/>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user