mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +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
+36
-17
@@ -1,20 +1,31 @@
|
|||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable ts/naming-convention */
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
import react from '@hydrooj/eslint-config';
|
import react from '@hydrooj/eslint-config';
|
||||||
|
|
||||||
export default defineConfig([globalIgnores([
|
const config = react({
|
||||||
'**/{public,files}/**/*.js',
|
ignores: [
|
||||||
'**/dist',
|
'**/{public,files}/**/*.js',
|
||||||
'**/*.d.ts',
|
'**/dist',
|
||||||
'**/node_modules',
|
'**/*.d.ts',
|
||||||
'**/.*.js',
|
'**/node_modules',
|
||||||
'packages/ui-default/public',
|
'**/.*.js',
|
||||||
]), {
|
'packages/ui-default/public',
|
||||||
extends: [react],
|
'packages/hydrojudge/vendor',
|
||||||
|
],
|
||||||
|
stylistic: {
|
||||||
|
indent: 4,
|
||||||
|
},
|
||||||
|
jsonc: false,
|
||||||
|
rules: {
|
||||||
|
'yaml/indent': ['warn', 2],
|
||||||
|
|
||||||
|
// There are too many `global` and `Function` in codebase already
|
||||||
|
'no-restricted-globals': 'off',
|
||||||
|
'ts/no-unsafe-function-type': 'off',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 5,
|
ecmaVersion: 5,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
@@ -105,16 +116,17 @@ export default defineConfig([globalIgnores([
|
|||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'github/array-foreach': 0,
|
'github/array-foreach': 0,
|
||||||
'@typescript-eslint/no-invalid-this': 0,
|
'ts/no-invalid-this': 0,
|
||||||
|
|
||||||
// FIXME A bug with eslint-parser
|
// FIXME A bug with eslint-parser
|
||||||
// 'template-curly-spacing': 'off',
|
// 'template-curly-spacing': 'off',
|
||||||
|
|
||||||
'@stylistic/indent': [
|
'style/indent': [
|
||||||
'warn',
|
'warn',
|
||||||
2,
|
2,
|
||||||
{ SwitchCase: 1 },
|
{ SwitchCase: 1 },
|
||||||
],
|
],
|
||||||
|
'style/indent-binary-ops': ['warn', 2],
|
||||||
'function-paren-newline': 'off',
|
'function-paren-newline': 'off',
|
||||||
'no-mixed-operators': 'off',
|
'no-mixed-operators': 'off',
|
||||||
'no-await-in-loop': 'off',
|
'no-await-in-loop': 'off',
|
||||||
@@ -139,14 +151,21 @@ export default defineConfig([globalIgnores([
|
|||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
files: ['packages/ui-next/src/**/*.{ts,tsx}'],
|
files: ['packages/ui-next/src/**/*.{ts,tsx}'],
|
||||||
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: {
|
globals: {
|
||||||
...globals.browser,
|
...globals.browser,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'@stylistic/indent': ['warn', 2],
|
'style/indent': ['warn', 2],
|
||||||
|
'style/indent-binary-ops': ['warn', 2],
|
||||||
},
|
},
|
||||||
}]);
|
}, {
|
||||||
|
files: ['**/*.yaml', '**/*.yml'],
|
||||||
|
rules: {
|
||||||
|
'yaml/plain-scalar': 'off',
|
||||||
|
'style/no-trailing-spaces': 'off',
|
||||||
|
'max-len': 'off',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default config;
|
||||||
+366
-385
@@ -1,409 +1,144 @@
|
|||||||
import { FlatCompat } from '@eslint/eslintrc';
|
/* eslint perfectionist/sort-objects: error */
|
||||||
|
import antfu from '@antfu/eslint-config';
|
||||||
import eslintReact from '@eslint-react/eslint-plugin';
|
import eslintReact from '@eslint-react/eslint-plugin';
|
||||||
import stylistic from '@stylistic/eslint-plugin';
|
|
||||||
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
|
||||||
import tsParser from '@typescript-eslint/parser';
|
import tsParser from '@typescript-eslint/parser';
|
||||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
||||||
import deMorgan from 'eslint-plugin-de-morgan';
|
import deMorgan from 'eslint-plugin-de-morgan';
|
||||||
import github from 'eslint-plugin-github';
|
import github from 'eslint-plugin-github';
|
||||||
import jsxA11Y from 'eslint-plugin-jsx-a11y';
|
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||||
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
||||||
|
|
||||||
const compat = new FlatCompat({});
|
/** @type {typeof antfu} */
|
||||||
|
const base = (option, ...args) => antfu(
|
||||||
export default defineConfig([
|
|
||||||
globalIgnores([
|
|
||||||
'**/*.d.ts',
|
|
||||||
'**/node_modules',
|
|
||||||
'**/.git',
|
|
||||||
]),
|
|
||||||
{
|
{
|
||||||
extends: [
|
markdown: false,
|
||||||
compat.extends('airbnb-base'),
|
typescript: true,
|
||||||
compat.extends('airbnb/hooks'),
|
vue: true,
|
||||||
github.getFlatConfigs().react,
|
...option,
|
||||||
deMorgan.configs.recommended,
|
gitignore: false,
|
||||||
eslintReact.configs['recommended-typescript'],
|
ignores: [
|
||||||
|
'**/*.d.ts',
|
||||||
|
'**/.git',
|
||||||
|
'**/.pnp.*',
|
||||||
|
...(option.ignores || []),
|
||||||
],
|
],
|
||||||
|
|
||||||
settings: {
|
|
||||||
'import/parsers': {
|
|
||||||
'@typescript-eslint/parser': ['.ts', '.tsx', '.d.ts'],
|
|
||||||
},
|
|
||||||
|
|
||||||
'import/resolver': {
|
|
||||||
node: {
|
|
||||||
extensions: ['.mjs', '.js', '.json', '.ts', '.d.ts'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
'import/extensions': ['.js', '.mjs', '.jsx', '.ts', '.tsx', '.d.ts'],
|
|
||||||
'import/external-module-folders': ['node_modules', 'node_modules/@types'],
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: {
|
|
||||||
'@typescript-eslint': typescriptEslint,
|
|
||||||
'simple-import-sort': simpleImportSort,
|
|
||||||
// import: fixupPluginRules(_import),
|
|
||||||
'react-refresh': reactRefresh,
|
|
||||||
'jsx-a11y': jsxA11Y,
|
|
||||||
'@stylistic': stylistic,
|
|
||||||
},
|
|
||||||
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: {
|
globals: {
|
||||||
Atomics: 'readonly',
|
Atomics: 'readonly',
|
||||||
SharedArrayBuffer: 'readonly',
|
|
||||||
BigInt: 'readonly',
|
BigInt: 'readonly',
|
||||||
|
SharedArrayBuffer: 'readonly',
|
||||||
|
...(option.languageOptions?.globals || {}),
|
||||||
},
|
},
|
||||||
|
...(option.languageOptions || {}),
|
||||||
parser: tsParser,
|
},
|
||||||
|
lessOpinionated: true,
|
||||||
|
plugins: {
|
||||||
|
'@eslint-react': eslintReact,
|
||||||
|
'react-refresh': reactRefresh,
|
||||||
|
'simple-import-sort': simpleImportSort,
|
||||||
|
...(option.plugins || {}),
|
||||||
},
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'brace-style': 'off',
|
'@eslint-react/dom/no-missing-button-type': 0,
|
||||||
|
// to allow `javascript:;`
|
||||||
|
'@eslint-react/dom/no-script-url': 0,
|
||||||
|
'@eslint-react/hooks-extra/no-direct-set-state-in-use-effect': 0,
|
||||||
|
'@eslint-react/hooks-extra/no-unnecessary-use-prefix': 0,
|
||||||
|
'@eslint-react/no-array-index-key': 0,
|
||||||
|
|
||||||
'@stylistic/brace-style': ['error', '1tbs', {
|
// ui-default is currently on react 18
|
||||||
allowSingleLine: true,
|
'@eslint-react/no-prop-types': 0,
|
||||||
}],
|
'@eslint-react/no-string-refs': 0,
|
||||||
|
|
||||||
camelcase: 'off',
|
'antfu/consistent-chaining': 'off',
|
||||||
'comma-dangle': 'off',
|
'antfu/consistent-list-newline': 'off',
|
||||||
|
|
||||||
'@stylistic/comma-dangle': ['error', {
|
|
||||||
arrays: 'always-multiline',
|
|
||||||
objects: 'always-multiline',
|
|
||||||
imports: 'always-multiline',
|
|
||||||
exports: 'always-multiline',
|
|
||||||
functions: 'always-multiline',
|
|
||||||
enums: 'always-multiline',
|
|
||||||
generics: 'always-multiline',
|
|
||||||
tuples: 'always-multiline',
|
|
||||||
}],
|
|
||||||
|
|
||||||
'comma-spacing': 'off',
|
|
||||||
|
|
||||||
'@stylistic/comma-spacing': ['error', {
|
|
||||||
before: false,
|
|
||||||
after: true,
|
|
||||||
}],
|
|
||||||
|
|
||||||
|
'class-methods-use-this': 0,
|
||||||
|
'consistent-return': 1,
|
||||||
|
curly: ['error', 'multi-line'],
|
||||||
'default-param-last': 'off',
|
'default-param-last': 'off',
|
||||||
|
|
||||||
'func-call-spacing': 'off',
|
|
||||||
'function-call-spacing': 'off',
|
|
||||||
'@stylistic/function-call-spacing': ['error', 'never'],
|
|
||||||
indent: 'off',
|
|
||||||
|
|
||||||
'@stylistic/indent': ['warn', 4, {
|
|
||||||
SwitchCase: 1,
|
|
||||||
VariableDeclarator: 1,
|
|
||||||
outerIIFEBody: 1,
|
|
||||||
|
|
||||||
FunctionDeclaration: {
|
|
||||||
parameters: 1,
|
|
||||||
body: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
FunctionExpression: {
|
|
||||||
parameters: 1,
|
|
||||||
body: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
CallExpression: {
|
|
||||||
arguments: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
ArrayExpression: 1,
|
|
||||||
ObjectExpression: 1,
|
|
||||||
ImportDeclaration: 1,
|
|
||||||
flatTernaryExpressions: false,
|
|
||||||
|
|
||||||
ignoredNodes: [
|
|
||||||
'JSXElement',
|
|
||||||
'JSXElement > *',
|
|
||||||
'JSXAttribute',
|
|
||||||
'JSXIdentifier',
|
|
||||||
'JSXNamespacedName',
|
|
||||||
'JSXMemberExpression',
|
|
||||||
'JSXSpreadAttribute',
|
|
||||||
'JSXExpressionContainer',
|
|
||||||
'JSXOpeningElement',
|
|
||||||
'JSXClosingElement',
|
|
||||||
'JSXFragment',
|
|
||||||
'JSXOpeningFragment',
|
|
||||||
'JSXClosingFragment',
|
|
||||||
'JSXText',
|
|
||||||
'JSXEmptyExpression',
|
|
||||||
'JSXSpreadChild',
|
|
||||||
],
|
|
||||||
|
|
||||||
ignoreComments: false,
|
|
||||||
}],
|
|
||||||
|
|
||||||
'keyword-spacing': 'off',
|
|
||||||
|
|
||||||
'@stylistic/keyword-spacing': ['error', {
|
|
||||||
before: true,
|
|
||||||
after: true,
|
|
||||||
|
|
||||||
overrides: {
|
|
||||||
return: {
|
|
||||||
after: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
throw: {
|
|
||||||
after: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
case: {
|
|
||||||
after: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
|
|
||||||
'lines-between-class-members': 'off',
|
|
||||||
|
|
||||||
'@stylistic/lines-between-class-members': ['error', 'always', {
|
|
||||||
exceptAfterSingleLine: true,
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-array-constructor': 'off',
|
|
||||||
'@typescript-eslint/no-array-constructor': 'error',
|
|
||||||
'no-dupe-class-members': 'off',
|
|
||||||
'@typescript-eslint/no-dupe-class-members': 'error',
|
|
||||||
'no-empty-function': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-empty-function': ['error', {
|
|
||||||
allow: ['arrowFunctions', 'functions', 'methods'],
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-extra-parens': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-extra-parens': ['off', 'all', {
|
|
||||||
conditionalAssign: true,
|
|
||||||
nestedBinaryExpressions: false,
|
|
||||||
returnAssign: false,
|
|
||||||
ignoreJSX: 'all',
|
|
||||||
enforceForArrowConditionals: false,
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-extra-semi': 'off',
|
|
||||||
'@stylistic/no-extra-semi': 'error',
|
|
||||||
'no-new-func': 'off',
|
|
||||||
'no-loss-of-precision': 'off',
|
|
||||||
'@typescript-eslint/no-loss-of-precision': 'error',
|
|
||||||
'no-loop-func': 'off',
|
|
||||||
'@typescript-eslint/no-loop-func': 'error',
|
|
||||||
'no-magic-numbers': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-magic-numbers': ['off', {
|
|
||||||
ignore: [],
|
|
||||||
ignoreArrayIndexes: true,
|
|
||||||
enforceConst: true,
|
|
||||||
detectObjects: false,
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-redeclare': 'off',
|
|
||||||
'@typescript-eslint/no-redeclare': 'error',
|
|
||||||
'no-shadow': 'off',
|
|
||||||
'@typescript-eslint/no-shadow': 'error',
|
|
||||||
'space-before-blocks': 'off',
|
|
||||||
'@stylistic/space-before-blocks': 'error',
|
|
||||||
'no-unused-expressions': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-unused-expressions': ['error', {
|
|
||||||
allowShortCircuit: false,
|
|
||||||
allowTernary: false,
|
|
||||||
allowTaggedTemplates: false,
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-unused-vars': ['error', {
|
|
||||||
vars: 'all',
|
|
||||||
args: 'after-used',
|
|
||||||
ignoreRestSiblings: true,
|
|
||||||
caughtErrorsIgnorePattern: '^_|e',
|
|
||||||
argsIgnorePattern: '^_',
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-use-before-define': 'off',
|
|
||||||
|
|
||||||
'@typescript-eslint/no-use-before-define': ['error', {
|
|
||||||
functions: true,
|
|
||||||
classes: true,
|
|
||||||
variables: true,
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-useless-constructor': 'off',
|
|
||||||
'@typescript-eslint/no-useless-constructor': 'error',
|
|
||||||
quotes: 'off',
|
|
||||||
|
|
||||||
'@stylistic/quotes': ['warn', 'single', {
|
|
||||||
avoidEscape: true,
|
|
||||||
}],
|
|
||||||
|
|
||||||
semi: 'off',
|
|
||||||
'@stylistic/semi': ['error', 'always'],
|
|
||||||
'space-before-function-paren': 'off',
|
|
||||||
|
|
||||||
'@stylistic/space-before-function-paren': ['error', {
|
|
||||||
anonymous: 'always',
|
|
||||||
named: 'never',
|
|
||||||
asyncArrow: 'always',
|
|
||||||
}],
|
|
||||||
|
|
||||||
'require-await': 'off',
|
|
||||||
'@typescript-eslint/require-await': 'off',
|
|
||||||
'space-infix-ops': 'off',
|
|
||||||
'@stylistic/space-infix-ops': 'error',
|
|
||||||
'object-curly-spacing': 'off',
|
|
||||||
'@stylistic/object-curly-spacing': ['error', 'always'],
|
|
||||||
|
|
||||||
'function-call-argument-newline': 0,
|
|
||||||
|
|
||||||
'no-implicit-coercion': ['warn', {
|
|
||||||
allow: ['!!', '+'],
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-extra-boolean-cast': ['warn', {
|
|
||||||
enforceForLogicalOperands: true,
|
|
||||||
}],
|
|
||||||
|
|
||||||
'no-empty-pattern': 0,
|
|
||||||
'no-multi-str': 0,
|
|
||||||
|
|
||||||
'dot-notation': 0,
|
'dot-notation': 0,
|
||||||
|
|
||||||
'@typescript-eslint/naming-convention': [
|
'eslint-comments/no-unlimited-disable': 'off',
|
||||||
'warn',
|
'func-names': 0,
|
||||||
{
|
'function-call-argument-newline': 0,
|
||||||
selector: 'default',
|
'function-paren-newline': 0,
|
||||||
format: ['camelCase'],
|
'github/a11y-svg-has-accessible-name': 0,
|
||||||
leadingUnderscore: 'allowSingleOrDouble',
|
'github/array-foreach': 1,
|
||||||
}, {
|
|
||||||
selector: 'default',
|
|
||||||
|
|
||||||
filter: {
|
|
||||||
regex: '^([A-Z]|_+id|__call__)$',
|
|
||||||
match: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
format: null,
|
|
||||||
}, {
|
|
||||||
selector: 'variable',
|
|
||||||
modifiers: ['destructured'],
|
|
||||||
format: null,
|
|
||||||
}, {
|
|
||||||
selector: 'import',
|
|
||||||
format: null,
|
|
||||||
}, {
|
|
||||||
selector: 'variable',
|
|
||||||
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
|
|
||||||
modifiers: ['const'],
|
|
||||||
leadingUnderscore: 'allowSingleOrDouble',
|
|
||||||
}, {
|
|
||||||
selector: 'property',
|
|
||||||
format: ['camelCase', 'PascalCase', 'UPPER_CASE', 'snake_case'],
|
|
||||||
leadingUnderscore: 'allowSingleOrDouble',
|
|
||||||
}, {
|
|
||||||
selector: ['function', 'parameter', 'parameterProperty', 'objectLiteralMethod'],
|
|
||||||
|
|
||||||
filter: {
|
|
||||||
regex: '__call__',
|
|
||||||
match: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
format: ['camelCase', 'PascalCase'],
|
|
||||||
leadingUnderscore: 'allowSingleOrDouble',
|
|
||||||
}, {
|
|
||||||
selector: ['objectLiteralProperty'],
|
|
||||||
|
|
||||||
filter: {
|
|
||||||
regex: '^[\\d]+$',
|
|
||||||
match: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
format: null,
|
|
||||||
}, {
|
|
||||||
selector: ['typeProperty', 'typeMethod', 'objectLiteralProperty'],
|
|
||||||
modifiers: ['requiresQuotes'],
|
|
||||||
format: null,
|
|
||||||
}, {
|
|
||||||
selector: ['typeMethod'],
|
|
||||||
format: ['camelCase', 'PascalCase'],
|
|
||||||
}, {
|
|
||||||
selector: ['enum', 'class', 'interface'],
|
|
||||||
format: ['PascalCase'],
|
|
||||||
}, {
|
|
||||||
selector: ['typeAlias', 'enumMember', 'typeParameter'],
|
|
||||||
format: ['PascalCase', 'camelCase', 'UPPER_CASE'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
'@typescript-eslint/no-invalid-this': 0,
|
|
||||||
|
|
||||||
'class-methods-use-this': 0,
|
|
||||||
'global-require': 0,
|
'global-require': 0,
|
||||||
'guard-for-in': 0,
|
'guard-for-in': 0,
|
||||||
'implicit-arrow-linebreak': 0,
|
'implicit-arrow-linebreak': 0,
|
||||||
|
|
||||||
|
'import/consistent-type-specifier-style': 'off',
|
||||||
'import/extensions': 0,
|
'import/extensions': 0,
|
||||||
'import/order': 0,
|
|
||||||
'consistent-return': 1,
|
|
||||||
'func-names': 0,
|
|
||||||
'import/newline-after-import': 0,
|
'import/newline-after-import': 0,
|
||||||
'import/no-cycle': 0,
|
'import/no-cycle': 0,
|
||||||
'import/no-extraneous-dependencies': 0,
|
'import/no-extraneous-dependencies': 0,
|
||||||
'import/no-named-as-default': 0,
|
'import/no-named-as-default': 0,
|
||||||
|
'import/order': 0,
|
||||||
'import/prefer-default-export': 0,
|
'import/prefer-default-export': 0,
|
||||||
|
|
||||||
|
'jsx-a11y/anchor-is-valid': 0,
|
||||||
|
'jsx-a11y/click-events-have-key-events': 0,
|
||||||
|
'jsx-a11y/label-has-associated-control': 0,
|
||||||
|
'jsx-a11y/no-static-element-interactions': 0,
|
||||||
|
|
||||||
'logical-assignment-operators': ['warn', 'always', {
|
'logical-assignment-operators': ['warn', 'always', {
|
||||||
enforceForIfStatements: true,
|
enforceForIfStatements: true,
|
||||||
}],
|
}],
|
||||||
|
|
||||||
'max-classes-per-file': 0,
|
'max-classes-per-file': 0,
|
||||||
'max-len': ['warn', 150],
|
'max-len': ['warn', 150],
|
||||||
'newline-per-chained-call': 0,
|
'newline-per-chained-call': 0,
|
||||||
|
'no-await-in-loop': 1,
|
||||||
'no-bitwise': 0,
|
'no-bitwise': 0,
|
||||||
'no-console': 0,
|
'no-console': 0,
|
||||||
'no-continue': 0,
|
'no-continue': 0,
|
||||||
'no-extend-native': 0,
|
|
||||||
|
|
||||||
'no-empty': ['warn', {
|
'no-empty': ['warn', {
|
||||||
allowEmptyCatch: true,
|
allowEmptyCatch: true,
|
||||||
}],
|
}],
|
||||||
|
'no-empty-pattern': 0,
|
||||||
'no-await-in-loop': 1,
|
'no-extend-native': 0,
|
||||||
|
'no-extra-boolean-cast': ['warn', {
|
||||||
|
enforceForLogicalOperands: true,
|
||||||
|
}],
|
||||||
|
'no-implicit-coercion': ['warn', {
|
||||||
|
allow: ['!!', '+'],
|
||||||
|
}],
|
||||||
'no-multi-assign': 0,
|
'no-multi-assign': 0,
|
||||||
'no-return-await': 0,
|
'no-multi-str': 0,
|
||||||
'no-nested-ternary': 0,
|
'no-nested-ternary': 0,
|
||||||
'no-param-reassign': 0,
|
'no-param-reassign': 0,
|
||||||
'no-plusplus': 0,
|
'no-plusplus': 0,
|
||||||
'no-underscore-dangle': 0,
|
|
||||||
'no-unmodified-loop-condition': 1,
|
|
||||||
'prefer-destructuring': 0,
|
|
||||||
'function-paren-newline': 0,
|
|
||||||
'simple-import-sort/exports': 0,
|
|
||||||
|
|
||||||
'no-restricted-syntax': [
|
'no-restricted-syntax': [
|
||||||
'error', {
|
'error', {
|
||||||
selector: 'LabeledStatement',
|
|
||||||
message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
|
message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
|
||||||
|
selector: 'LabeledStatement',
|
||||||
}, {
|
}, {
|
||||||
selector: 'WithStatement',
|
|
||||||
message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
|
message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
|
||||||
|
selector: 'WithStatement',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'no-return-await': 0,
|
||||||
|
'no-underscore-dangle': 0,
|
||||||
|
'no-unmodified-loop-condition': 1,
|
||||||
|
|
||||||
'generator-star-spacing': 0,
|
'node/prefer-global/buffer': 'off',
|
||||||
|
'node/prefer-global/process': 'off',
|
||||||
|
'perfectionist/sort-imports': 'off',
|
||||||
|
'perfectionist/sort-named-imports': 'off',
|
||||||
|
|
||||||
'github/array-foreach': 1,
|
'prefer-destructuring': 0,
|
||||||
'github/a11y-svg-has-accessible-name': 0,
|
|
||||||
'quote-props': 0,
|
'quote-props': 0,
|
||||||
'@stylistic/quote-props': [0, 'as-needed'],
|
'react-hooks/exhaustive-deps': 0,
|
||||||
|
'react-refresh/only-export-components': ['warn', {
|
||||||
|
allowConstantExport: true,
|
||||||
|
}],
|
||||||
|
|
||||||
|
'regexp/prefer-d': 'off',
|
||||||
|
'regexp/prefer-w': 'off',
|
||||||
|
'regexp/use-ignore-case': 'off',
|
||||||
|
|
||||||
|
'simple-import-sort/exports': 0,
|
||||||
'simple-import-sort/imports': ['warn', {
|
'simple-import-sort/imports': ['warn', {
|
||||||
groups: [
|
groups: [
|
||||||
['^\\u0000'],
|
['^\\u0000'],
|
||||||
@@ -416,50 +151,290 @@ export default defineConfig([
|
|||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
|
|
||||||
// ui-default is currently on react 18
|
'style/arrow-parens': ['error', 'always'],
|
||||||
'@eslint-react/no-prop-types': 0,
|
'style/brace-style': ['error', '1tbs', {
|
||||||
'@eslint-react/no-string-refs': 0,
|
allowSingleLine: true,
|
||||||
|
|
||||||
// to allow `javascript:;`
|
|
||||||
'@eslint-react/dom/no-script-url': 0,
|
|
||||||
|
|
||||||
'@eslint-react/no-array-index-key': 0,
|
|
||||||
'@eslint-react/dom/no-missing-button-type': 0,
|
|
||||||
'@eslint-react/dom/no-dangerously-set-innerhtml': 0,
|
|
||||||
'@eslint-react/hooks-extra/no-direct-set-state-in-use-effect': 0,
|
|
||||||
'@eslint-react/hooks-extra/no-unnecessary-use-prefix': 0,
|
|
||||||
'react-hooks/exhaustive-deps': 0,
|
|
||||||
// 'react/prefer-stateless-function': 0,
|
|
||||||
// 'react/function-component-definition': 0,
|
|
||||||
// 'react/static-property-placement': 0,
|
|
||||||
// 'react/self-closing-comp': 0,
|
|
||||||
// 'react/prop-types': 0,
|
|
||||||
// 'react/jsx-filename-extension': 0,
|
|
||||||
// 'react/jsx-one-expression-per-line': 0,
|
|
||||||
// 'react/jsx-props-no-spreading': 0,
|
|
||||||
// 'react/no-string-refs': 0,
|
|
||||||
// 'react/require-default-props': 0,
|
|
||||||
// 'react/react-in-jsx-scope': 0,
|
|
||||||
// 'react/destructuring-assignment': 0,
|
|
||||||
// 'react/button-has-type': 0,
|
|
||||||
// 'react/forbid-prop-types': 0,
|
|
||||||
'jsx-a11y/no-static-element-interactions': 0,
|
|
||||||
'jsx-a11y/anchor-is-valid': 0,
|
|
||||||
'jsx-a11y/click-events-have-key-events': 0,
|
|
||||||
'jsx-a11y/label-has-associated-control': 0,
|
|
||||||
|
|
||||||
'react-refresh/only-export-components': ['warn', {
|
|
||||||
allowConstantExport: true,
|
|
||||||
}],
|
}],
|
||||||
|
'style/comma-dangle': ['error', {
|
||||||
|
arrays: 'always-multiline',
|
||||||
|
enums: 'always-multiline',
|
||||||
|
exports: 'always-multiline',
|
||||||
|
functions: 'always-multiline',
|
||||||
|
generics: 'always-multiline',
|
||||||
|
imports: 'always-multiline',
|
||||||
|
objects: 'always-multiline',
|
||||||
|
tuples: 'always-multiline',
|
||||||
|
}],
|
||||||
|
'style/comma-spacing': ['error', {
|
||||||
|
after: true,
|
||||||
|
before: false,
|
||||||
|
}],
|
||||||
|
'style/function-call-spacing': ['error', 'never'],
|
||||||
|
'style/generator-star-spacing': ['error', {
|
||||||
|
after: true,
|
||||||
|
anonymous: { after: true, before: false },
|
||||||
|
before: false,
|
||||||
|
method: { after: false, before: true },
|
||||||
|
}],
|
||||||
|
'style/indent': ['warn', option.stylistic?.indent ?? 4, {
|
||||||
|
ArrayExpression: 1,
|
||||||
|
CallExpression: {
|
||||||
|
arguments: 1,
|
||||||
|
},
|
||||||
|
flatTernaryExpressions: false,
|
||||||
|
FunctionDeclaration: {
|
||||||
|
body: 1,
|
||||||
|
parameters: 1,
|
||||||
|
},
|
||||||
|
FunctionExpression: {
|
||||||
|
body: 1,
|
||||||
|
parameters: 1,
|
||||||
|
},
|
||||||
|
ignoreComments: false,
|
||||||
|
ignoredNodes: [
|
||||||
|
// 'JSXElement',
|
||||||
|
// 'JSXElement > *',
|
||||||
|
// 'JSXAttribute',
|
||||||
|
// 'JSXIdentifier',
|
||||||
|
// 'JSXNamespacedName',
|
||||||
|
// 'JSXMemberExpression',
|
||||||
|
// 'JSXSpreadAttribute',
|
||||||
|
// 'JSXExpressionContainer',
|
||||||
|
// 'JSXOpeningElement',
|
||||||
|
// 'JSXClosingElement',
|
||||||
|
// 'JSXFragment',
|
||||||
|
// 'JSXOpeningFragment',
|
||||||
|
// 'JSXClosingFragment',
|
||||||
|
'JSXText',
|
||||||
|
// 'JSXEmptyExpression',
|
||||||
|
// 'JSXSpreadChild',
|
||||||
|
],
|
||||||
|
ImportDeclaration: 1,
|
||||||
|
ObjectExpression: 1,
|
||||||
|
outerIIFEBody: 1,
|
||||||
|
SwitchCase: 1,
|
||||||
|
VariableDeclarator: 1,
|
||||||
|
}],
|
||||||
|
'style/jsx-closing-bracket-location': 'off',
|
||||||
|
'style/jsx-closing-tag-location': 'off',
|
||||||
|
'style/jsx-function-call-newline': 'off',
|
||||||
|
'style/jsx-indent-props': 'off',
|
||||||
|
'style/jsx-one-expression-per-line': 'off',
|
||||||
|
'style/jsx-wrap-multilines': 'off',
|
||||||
|
'style/keyword-spacing': ['error', {
|
||||||
|
after: true,
|
||||||
|
before: true,
|
||||||
|
overrides: {
|
||||||
|
case: {
|
||||||
|
after: true,
|
||||||
|
},
|
||||||
|
return: {
|
||||||
|
after: true,
|
||||||
|
},
|
||||||
|
throw: {
|
||||||
|
after: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
'style/lines-between-class-members': ['error', 'always', {
|
||||||
|
exceptAfterSingleLine: true,
|
||||||
|
}],
|
||||||
|
'style/max-statements-per-line': ['error', { max: 2 }],
|
||||||
|
'style/member-delimiter-style': ['error', {
|
||||||
|
multiline: {
|
||||||
|
delimiter: 'semi',
|
||||||
|
requireLast: true,
|
||||||
|
},
|
||||||
|
multilineDetection: 'brackets',
|
||||||
|
singleline: {
|
||||||
|
delimiter: 'comma',
|
||||||
|
requireLast: false,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
'style/multiline-ternary': 'off',
|
||||||
|
'style/no-extra-semi': 'error',
|
||||||
|
'style/object-curly-spacing': ['error', 'always'],
|
||||||
|
'style/operator-linebreak': ['warn', 'before', {
|
||||||
|
overrides: {
|
||||||
|
'=': 'after',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
'style/quote-props': ['warn', 'as-needed'],
|
||||||
|
'style/quotes': ['warn', 'single', {
|
||||||
|
avoidEscape: true,
|
||||||
|
}],
|
||||||
|
'style/space-before-blocks': 'error',
|
||||||
|
'style/space-before-function-paren': ['error', {
|
||||||
|
anonymous: 'always',
|
||||||
|
asyncArrow: 'always',
|
||||||
|
named: 'never',
|
||||||
|
}],
|
||||||
|
'style/space-infix-ops': 'error',
|
||||||
|
|
||||||
|
'test/no-import-node-test': 'off',
|
||||||
|
|
||||||
|
'ts/ban-ts-comment': 'off',
|
||||||
|
'ts/consistent-type-imports': 'off',
|
||||||
|
'ts/method-signature-style': 'off',
|
||||||
|
'ts/no-array-constructor': 'error',
|
||||||
|
'ts/no-dupe-class-members': 'error',
|
||||||
|
'ts/no-empty-function': ['error', {
|
||||||
|
allow: ['arrowFunctions', 'functions', 'methods'],
|
||||||
|
}],
|
||||||
|
'ts/no-extra-parens': ['off', 'all', {
|
||||||
|
conditionalAssign: true,
|
||||||
|
enforceForArrowConditionals: false,
|
||||||
|
ignoreJSX: 'all',
|
||||||
|
nestedBinaryExpressions: false,
|
||||||
|
returnAssign: false,
|
||||||
|
}],
|
||||||
|
'ts/no-invalid-this': 'off',
|
||||||
|
'ts/no-loop-func': 'error',
|
||||||
|
'ts/no-loss-of-precision': 'error',
|
||||||
|
'ts/no-magic-numbers': 'off',
|
||||||
|
'ts/no-namespace': 'off',
|
||||||
|
'ts/no-redeclare': 'off',
|
||||||
|
'ts/no-require-imports': 'off',
|
||||||
|
'ts/no-shadow': 'error',
|
||||||
|
'ts/no-this-alias': 'off',
|
||||||
|
'ts/no-unused-expressions': ['error', {
|
||||||
|
allowShortCircuit: false,
|
||||||
|
allowTaggedTemplates: false,
|
||||||
|
allowTernary: false,
|
||||||
|
}],
|
||||||
|
'ts/no-unused-vars': ['error', {
|
||||||
|
args: 'after-used',
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
caughtErrorsIgnorePattern: '^_|e',
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
vars: 'all',
|
||||||
|
}],
|
||||||
|
'ts/no-use-before-define': ['error', {
|
||||||
|
classes: true,
|
||||||
|
functions: true,
|
||||||
|
variables: true,
|
||||||
|
}],
|
||||||
|
'ts/no-useless-constructor': 'error',
|
||||||
|
'ts/prefer-as-const': 'off',
|
||||||
|
'ts/require-await': 'off',
|
||||||
|
|
||||||
|
'unicorn/error-message': 'off',
|
||||||
|
'unicorn/no-instanceof-builtins': 'off',
|
||||||
|
'unicorn/prefer-node-protocol': 'off',
|
||||||
|
|
||||||
|
'unused-imports/no-unused-vars': 'off',
|
||||||
|
|
||||||
|
...(option.rules || {}),
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import/extensions': ['.js', '.mjs', '.jsx', '.ts', '.tsx', '.d.ts'],
|
||||||
|
'import/external-module-folders': ['node_modules', 'node_modules/@types'],
|
||||||
|
'import/resolver': {
|
||||||
|
node: {
|
||||||
|
extensions: ['.mjs', '.js', '.json', '.ts', '.d.ts'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(option.settings || {}),
|
||||||
|
},
|
||||||
|
stylistic: {
|
||||||
|
indent: 4,
|
||||||
|
quotes: undefined,
|
||||||
|
semi: true,
|
||||||
|
...(typeof option.stylistic === 'object' ? option.stylistic : {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
github.getFlatConfigs().react,
|
||||||
|
deMorgan.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.vue'],
|
||||||
|
rules: {
|
||||||
|
'vue/block-order': ['warn', { order: ['template', 'script[setup]', 'script:not([setup])', 'style[scoped]', 'style:not([scoped])'] }],
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['**/*.ts', '**/*.tsx'],
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.cjs', '**/*.cts', '**/*.mjs', '**/*.mts'],
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'ts/naming-convention': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
format: ['camelCase'],
|
||||||
|
leadingUnderscore: 'allowSingleOrDouble',
|
||||||
|
selector: 'default',
|
||||||
|
}, {
|
||||||
|
filter: {
|
||||||
|
match: true,
|
||||||
|
regex: '^([A-Z]|_+id|__call__)$',
|
||||||
|
},
|
||||||
|
format: null,
|
||||||
|
selector: 'default',
|
||||||
|
}, {
|
||||||
|
format: null,
|
||||||
|
modifiers: ['destructured'],
|
||||||
|
selector: 'variable',
|
||||||
|
}, {
|
||||||
|
format: null,
|
||||||
|
selector: 'import',
|
||||||
|
}, {
|
||||||
|
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
|
||||||
|
leadingUnderscore: 'allowSingleOrDouble',
|
||||||
|
modifiers: ['const'],
|
||||||
|
selector: 'variable',
|
||||||
|
}, {
|
||||||
|
format: ['camelCase', 'PascalCase', 'UPPER_CASE', 'snake_case'],
|
||||||
|
leadingUnderscore: 'allowSingleOrDouble',
|
||||||
|
selector: 'property',
|
||||||
|
}, {
|
||||||
|
filter: {
|
||||||
|
match: false,
|
||||||
|
regex: '__call__',
|
||||||
|
},
|
||||||
|
format: ['camelCase', 'PascalCase'],
|
||||||
|
leadingUnderscore: 'allowSingleOrDouble',
|
||||||
|
selector: ['function', 'parameter', 'parameterProperty', 'objectLiteralMethod'],
|
||||||
|
}, {
|
||||||
|
filter: {
|
||||||
|
match: true,
|
||||||
|
regex: '^[\\d]+$',
|
||||||
|
},
|
||||||
|
format: null,
|
||||||
|
selector: ['objectLiteralProperty'],
|
||||||
|
}, {
|
||||||
|
format: null,
|
||||||
|
modifiers: ['requiresQuotes'],
|
||||||
|
selector: ['typeProperty', 'typeMethod', 'objectLiteralProperty'],
|
||||||
|
}, {
|
||||||
|
format: ['camelCase', 'PascalCase'],
|
||||||
|
selector: ['typeMethod'],
|
||||||
|
}, {
|
||||||
|
format: ['PascalCase'],
|
||||||
|
selector: ['enum', 'class', 'interface'],
|
||||||
|
}, {
|
||||||
|
format: ['PascalCase', 'camelCase', 'UPPER_CASE'],
|
||||||
|
selector: ['typeAlias', 'enumMember', 'typeParameter'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
},
|
||||||
|
|
||||||
// Hand over to tsc
|
// Hand over to tsc
|
||||||
rules: {
|
rules: {
|
||||||
'constructor-super': 'off',
|
'constructor-super': 'off',
|
||||||
'getter-return': 'off',
|
'getter-return': 'off',
|
||||||
|
'import/named': 'off',
|
||||||
|
'import/no-named-as-default-member': 'off',
|
||||||
|
'import/no-unresolved': 'off',
|
||||||
'no-const-assign': 'off',
|
'no-const-assign': 'off',
|
||||||
'no-dupe-args': 'off',
|
'no-dupe-args': 'off',
|
||||||
'no-dupe-class-members': 'off',
|
'no-dupe-class-members': 'off',
|
||||||
@@ -475,9 +450,15 @@ export default defineConfig([
|
|||||||
'no-unreachable': 'off',
|
'no-unreachable': 'off',
|
||||||
'no-unsafe-negation': 'off',
|
'no-unsafe-negation': 'off',
|
||||||
'valid-typeof': 'off',
|
'valid-typeof': 'off',
|
||||||
'import/named': 'off',
|
|
||||||
'import/no-named-as-default-member': 'off',
|
|
||||||
'import/no-unresolved': 'off',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
{
|
||||||
|
files: ['**/*.yaml', '**/*.yml'],
|
||||||
|
rules: {
|
||||||
|
'style/no-multi-spaces': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...args,
|
||||||
|
);
|
||||||
|
|
||||||
|
export default base;
|
||||||
@@ -1,22 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/eslint-config",
|
"name": "@hydrooj/eslint-config",
|
||||||
"version": "2.0.3",
|
"version": "3.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "base.mjs",
|
"main": "base.mjs",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro",
|
"repository": "https://github.com/hydro-dev/Hydro",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@antfu/eslint-config": "^5.2.0",
|
||||||
"@eslint-react/eslint-plugin": "^1.52.3",
|
"@eslint-react/eslint-plugin": "^1.52.3",
|
||||||
"@eslint/compat": "^1.3.1",
|
"@typescript-eslint/eslint-plugin": "^8.39.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@typescript-eslint/parser": "^8.39.0",
|
||||||
"@stylistic/eslint-plugin": "^5.2.2",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
||||||
"@typescript-eslint/parser": "^8.38.0",
|
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
|
||||||
"eslint-plugin-de-morgan": "^1.3.1",
|
"eslint-plugin-de-morgan": "^1.3.1",
|
||||||
"eslint-plugin-github": "^6.0.0",
|
"eslint-plugin-github": "^6.0.0",
|
||||||
"eslint-plugin-import": "2.31.0",
|
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ const REDIRECT = Symbol.for('hydro.api.response.redirect');
|
|||||||
|
|
||||||
type MaybePromise<T> = T | Promise<T>;
|
type MaybePromise<T> = T | Promise<T>;
|
||||||
export type ApiType = 'Query' | 'Mutation' | 'Subscription';
|
export type ApiType = 'Query' | 'Mutation' | 'Subscription';
|
||||||
export type ApiCall<Type extends ApiType, Arg, Res, Progress = never> = {
|
export interface ApiCall<Type extends ApiType, Arg, Res, Progress = never> {
|
||||||
readonly type: Type;
|
readonly type: Type;
|
||||||
readonly input: Schema<Arg>;
|
readonly input: Schema<Arg>;
|
||||||
readonly func: (Type extends 'Subscription'
|
readonly func: (Type extends 'Subscription'
|
||||||
? (context: any, args: Arg, emit: (payload: Res) => void) => (() => MaybePromise<void>)
|
? (context: any, args: Arg, emit: (payload: Res) => void) => (() => MaybePromise<void>)
|
||||||
: (context: any, args: Arg) => MaybePromise<Res | AsyncGenerator<Progress, Res, never>>);
|
: (context: any, args: Arg) => MaybePromise<Res | AsyncGenerator<Progress, Res, never>>);
|
||||||
readonly hooks: ApiCall<'Query', Arg, void>[];
|
readonly hooks: ApiCall<'Query', Arg, void>[];
|
||||||
};
|
}
|
||||||
|
|
||||||
export const _get = <Type extends ApiType>(type: Type) => <Arg, Res, Progress>(
|
export const _get = <Type extends ApiType>(type: Type) => <Arg, Res, Progress>(
|
||||||
schema: Schema<Arg>,
|
schema: Schema<Arg>,
|
||||||
func: ApiCall<Type, Arg, Res, Progress>['func'],
|
func: ApiCall<Type, Arg, Res, Progress>['func'],
|
||||||
hooks: ApiCall<'Query', Arg, void, never>[] = [],
|
hooks: ApiCall<'Query', Arg, void, never>[] = [],
|
||||||
): ApiCall<Type, Arg, Res, Progress> => ({ input: schema, func, hooks, type } as const); // eslint-disable-line
|
): ApiCall<Type, Arg, Res, Progress> => ({ input: schema, func, hooks, type } as const);
|
||||||
|
|
||||||
export const Query = _get('Query');
|
export const Query = _get('Query');
|
||||||
export const Mutation = _get('Mutation');
|
export const Mutation = _get('Mutation');
|
||||||
@@ -57,7 +57,7 @@ export interface Apis {
|
|||||||
builtin: {
|
builtin: {
|
||||||
'query.batch': ApiCall<'Query', { op: string, args: any }[], { [key: string]: any }>;
|
'query.batch': ApiCall<'Query', { op: string, args: any }[], { [key: string]: any }>;
|
||||||
'mutation.batch': ApiCall<'Mutation', { op: string, args: any }[], { [key: string]: any }>;
|
'mutation.batch': ApiCall<'Mutation', { op: string, args: any }[], { [key: string]: any }>;
|
||||||
}
|
};
|
||||||
test: typeof TestApis;
|
test: typeof TestApis;
|
||||||
}
|
}
|
||||||
export type FlattenedApis = Apis[keyof Apis];
|
export type FlattenedApis = Apis[keyof Apis];
|
||||||
@@ -68,8 +68,7 @@ type MKeyOf<T> = T extends any ? keyof T : never;
|
|||||||
type MId<T> = { [K in MKeyOf<T>]: T[K] } & {};
|
type MId<T> = { [K in MKeyOf<T>]: T[K] } & {};
|
||||||
type ProjectionSchema<T> = T extends Array<infer U>
|
type ProjectionSchema<T> = T extends Array<infer U>
|
||||||
? ProjectionSchema<U>
|
? ProjectionSchema<U>
|
||||||
: | { [K in keyof T]?: ProjectionSchemaId | ProjectionSchema<T[K]> }
|
: { [K in keyof T]?: ProjectionSchemaId | ProjectionSchema<T[K]> } | Record<keyof any, ProjectionSchemaId | object>;
|
||||||
| Record<keyof any, ProjectionSchemaId | object>;
|
|
||||||
type AsKeys<T> = T extends Array<infer U extends string> ? Record<U, 1> : T;
|
type AsKeys<T> = T extends Array<infer U extends string> ? Record<U, 1> : T;
|
||||||
type Projection<T, S> = S extends ProjectionSchemaId
|
type Projection<T, S> = S extends ProjectionSchemaId
|
||||||
? T : T extends Array<infer U> ? Array<MId<Projection<U, S>>> : {
|
? T : T extends Array<infer U> ? Array<MId<Projection<U, S>>> : {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ type MethodDecorator = (target: any, funcName: string, obj: any) => any;
|
|||||||
type ClassDecorator = <T extends new (...args: any[]) => any>(Class: T) => T extends new (...args: infer R) => infer S
|
type ClassDecorator = <T extends new (...args: any[]) => any>(Class: T) => T extends new (...args: infer R) => infer S
|
||||||
? new (...args: R) => S : never;
|
? new (...args: R) => S : never;
|
||||||
export interface ParamOption<T> {
|
export interface ParamOption<T> {
|
||||||
name: string,
|
name: string;
|
||||||
source: 'all' | 'get' | 'post' | 'route',
|
source: 'all' | 'get' | 'post' | 'route';
|
||||||
isOptional?: boolean | 'convert',
|
isOptional?: boolean | 'convert';
|
||||||
convert?: Converter<T>,
|
convert?: Converter<T>;
|
||||||
validate?: Validator,
|
validate?: Validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
const kSchema = Symbol.for('schemastery');
|
const kSchema = Symbol.for('schemastery');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
interface IHydroError {
|
interface IHydroError {
|
||||||
new(...args: any[]): HydroError
|
new(...args: any[]): HydroError;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HydroError extends Error {
|
export class HydroError extends Error {
|
||||||
@@ -32,7 +32,7 @@ const Err = (name: string, Class: IHydroError, ...info: Array<(() => string) | s
|
|||||||
msg = item;
|
msg = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
// eslint-disable-next-line ts/no-shadow
|
||||||
return class HydroError extends Class {
|
return class HydroError extends Class {
|
||||||
name = name;
|
name = name;
|
||||||
constructor(...args: any[]) {
|
constructor(...args: any[]) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export * from './server';
|
|
||||||
export { default as serializer } from './serializer';
|
|
||||||
export * from './decorators';
|
|
||||||
export * from './validator';
|
|
||||||
export * from './error';
|
|
||||||
export * from './router';
|
|
||||||
export * from './interface';
|
|
||||||
export * from './api';
|
export * from './api';
|
||||||
|
export * from './decorators';
|
||||||
|
export * from './error';
|
||||||
|
export * from './interface';
|
||||||
|
export * from './router';
|
||||||
|
export { default as serializer } from './serializer';
|
||||||
|
export * from './server';
|
||||||
|
export * from './validator';
|
||||||
@@ -3,7 +3,7 @@ import type { ApiHandler } from './api';
|
|||||||
import type { ConnectionHandler, Handler, NotFoundHandler } from './server';
|
import type { ConnectionHandler, Handler, NotFoundHandler } from './server';
|
||||||
|
|
||||||
export interface KnownHandlers {
|
export interface KnownHandlers {
|
||||||
'NotFound': NotFoundHandler;
|
NotFound: NotFoundHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapHandlerEvents<N extends string, H extends Handler<any>> =
|
type MapHandlerEvents<N extends string, H extends Handler<any>> =
|
||||||
@@ -24,19 +24,19 @@ export type Methods = 'get' | 'post' | 'put' | 'delete' | 'patch';
|
|||||||
export type HookWithMethod = Exclude<HookType, 'before-operation'>;
|
export type HookWithMethod = Exclude<HookType, 'before-operation'>;
|
||||||
|
|
||||||
export interface ServerEvents<C extends Context> extends KnownHandlerEvents, HandlerEvents<C> {
|
export interface ServerEvents<C extends Context> extends KnownHandlerEvents, HandlerEvents<C> {
|
||||||
'handler/create': (thisArg: Handler<C> | ConnectionHandler<C>, type: 'ws' | 'http') => VoidReturn
|
'handler/create': (thisArg: Handler<C> | ConnectionHandler<C>, type: 'ws' | 'http') => VoidReturn;
|
||||||
'handler/create/http': (thisArg: Handler<C>) => VoidReturn
|
'handler/create/http': (thisArg: Handler<C>) => VoidReturn;
|
||||||
'handler/create/ws': (thisArg: ConnectionHandler<C>) => VoidReturn
|
'handler/create/ws': (thisArg: ConnectionHandler<C>) => VoidReturn;
|
||||||
'handler/init': (thisArg: Handler<C>) => VoidReturn
|
'handler/init': (thisArg: Handler<C>) => VoidReturn;
|
||||||
'handler/error': (thisArg: Handler<C> | ConnectionHandler<C>, e: Error) => VoidReturn
|
'handler/error': (thisArg: Handler<C> | ConnectionHandler<C>, e: Error) => VoidReturn;
|
||||||
'handler/api/before': (thisArg: ApiHandler<C>) => VoidReturn
|
'handler/api/before': (thisArg: ApiHandler<C>) => VoidReturn;
|
||||||
'api/before': (args: any) => VoidReturn
|
'api/before': (args: any) => VoidReturn;
|
||||||
[k: `handler/${HookType}/${string}`]: (thisArg: any) => VoidReturn
|
[k: `handler/${HookType}/${string}`]: (thisArg: any) => VoidReturn;
|
||||||
[k: `handler/${HookWithMethod}/${string}/${Methods}`]: (thisArg: any) => VoidReturn
|
[k: `handler/${HookWithMethod}/${string}/${Methods}`]: (thisArg: any) => VoidReturn;
|
||||||
[k: `handler/register/${string}`]: (HandlerClass: new (...args: any[]) => any) => VoidReturn
|
[k: `handler/register/${string}`]: (HandlerClass: new (...args: any[]) => any) => VoidReturn;
|
||||||
[k: `handler/error/${string}`]: (thisArg: Handler<C>, e: Error) => VoidReturn
|
[k: `handler/error/${string}`]: (thisArg: Handler<C>, e: Error) => VoidReturn;
|
||||||
[k: `handler/api/before/${string}`]: (thisArg: ApiHandler<C>) => VoidReturn
|
[k: `handler/api/before/${string}`]: (thisArg: ApiHandler<C>) => VoidReturn;
|
||||||
[k: `api/before/${string}`]: (args: any) => VoidReturn
|
[k: `api/before/${string}`]: (args: any) => VoidReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'cordis' {
|
declare module 'cordis' {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hydrooj/utils": "workspace:^",
|
"@hydrooj/utils": "workspace:^",
|
||||||
"emoji-regex": "^10.4.0",
|
"emoji-regex": "^10.4.0",
|
||||||
"fs-extra": "^11.3.0",
|
"fs-extra": "^11.3.1",
|
||||||
"koa": "^3.0.1",
|
"koa": "^3.0.1",
|
||||||
"koa-body": "^6.0.1",
|
"koa-body": "^6.0.1",
|
||||||
"koa-compress": "^5.1.1",
|
"koa-compress": "^5.1.1",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable ts/no-unsafe-declaration-merging */
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import { tmpdir } from 'os';
|
import { tmpdir } from 'os';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
@@ -77,7 +78,8 @@ export interface HydroResponse {
|
|||||||
type: string;
|
type: string;
|
||||||
status: number;
|
status: number;
|
||||||
template?: string;
|
template?: string;
|
||||||
/** If set, and pjax content was request from client,
|
/**
|
||||||
|
* If set, and pjax content was request from client,
|
||||||
* The template will be used for rendering.
|
* The template will be used for rendering.
|
||||||
*/
|
*/
|
||||||
pjax?: string;
|
pjax?: string;
|
||||||
@@ -87,14 +89,14 @@ export interface HydroResponse {
|
|||||||
attachment: (name: string, stream?: any) => void;
|
attachment: (name: string, stream?: any) => void;
|
||||||
addHeader: (name: string, value: string) => void;
|
addHeader: (name: string, value: string) => void;
|
||||||
}
|
}
|
||||||
type HydroContext = {
|
interface HydroContext {
|
||||||
request: HydroRequest;
|
request: HydroRequest;
|
||||||
response: HydroResponse;
|
response: HydroResponse;
|
||||||
args: Record<string, any>;
|
args: Record<string, any>;
|
||||||
UiContext: Record<string, any>;
|
UiContext: Record<string, any>;
|
||||||
domain: { _id: string };
|
domain: { _id: string };
|
||||||
user: { _id: number };
|
user: { _id: number };
|
||||||
};
|
}
|
||||||
export type KoaContext = Koa.Context & {
|
export type KoaContext = Koa.Context & {
|
||||||
HydroContext: HydroContext;
|
HydroContext: HydroContext;
|
||||||
handler: any;
|
handler: any;
|
||||||
@@ -103,14 +105,14 @@ export type KoaContext = Koa.Context & {
|
|||||||
holdFiles: (string | File)[];
|
holdFiles: (string | File)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TextRenderer = {
|
export interface TextRenderer {
|
||||||
output: 'html' | 'json' | 'text';
|
output: 'html' | 'json' | 'text';
|
||||||
render: (name: string, args: Record<string, any>, context: Record<string, any>) => string | Promise<string>;
|
render: (name: string, args: Record<string, any>, context: Record<string, any>) => string | Promise<string>;
|
||||||
};
|
}
|
||||||
export type BinaryRenderer = {
|
export interface BinaryRenderer {
|
||||||
output: 'binary';
|
output: 'binary';
|
||||||
render: (name: string, args: Record<string, any>, context: Record<string, any>) => Buffer | Promise<Buffer>;
|
render: (name: string, args: Record<string, any>, context: Record<string, any>) => Buffer | Promise<Buffer>;
|
||||||
};
|
}
|
||||||
export type Renderer = (BinaryRenderer | TextRenderer) & {
|
export type Renderer = (BinaryRenderer | TextRenderer) & {
|
||||||
name: string;
|
name: string;
|
||||||
accept: readonly string[];
|
accept: readonly string[];
|
||||||
@@ -139,7 +141,7 @@ export interface UserModel {
|
|||||||
_id: number;
|
_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HandlerCommon<C> { } // eslint-disable-line @typescript-eslint/no-unused-vars
|
export interface HandlerCommon<C> { } // eslint-disable-line ts/no-unused-vars
|
||||||
export class HandlerCommon<C> {
|
export class HandlerCommon<C> {
|
||||||
static [kHandler]: string | boolean = 'HandlerCommon';
|
static [kHandler]: string | boolean = 'HandlerCommon';
|
||||||
session: Record<string, any>;
|
session: Record<string, any>;
|
||||||
@@ -507,7 +509,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
this.activeHandlers.set(h, { start: Date.now(), name });
|
this.activeHandlers.set(h, { start: Date.now(), name });
|
||||||
try {
|
try {
|
||||||
const operation = (method === 'post' && ctx.request.body?.operation)
|
const operation = (method === 'post' && ctx.request.body?.operation)
|
||||||
? `_${ctx.request.body.operation}`.replace(/_([a-z])/gm, (s) => s[1].toUpperCase())
|
// eslint-disable-next-line regexp/no-unused-capturing-group
|
||||||
|
? `_${ctx.request.body.operation}`.replace(/_([a-z])/g, (s) => s[1].toUpperCase())
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
// FIXME: should pass type check
|
// FIXME: should pass type check
|
||||||
@@ -777,7 +780,7 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
this.ctx.on(`handler/register/${name}`, callback as any);
|
this.ctx.on(`handler/register/${name}`, callback as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line ts/naming-convention
|
||||||
public Route(name: string, path: string, RouteHandler: typeof Handler<C>, ...permPrivChecker) {
|
public Route(name: string, path: string, RouteHandler: typeof Handler<C>, ...permPrivChecker) {
|
||||||
// if (name === 'contest_scoreboard') {
|
// if (name === 'contest_scoreboard') {
|
||||||
// console.log('+++', this.ctx);
|
// console.log('+++', this.ctx);
|
||||||
@@ -786,7 +789,7 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
|
|||||||
return this.register('route', name, path, RouteHandler, ...permPrivChecker);
|
return this.register('route', name, path, RouteHandler, ...permPrivChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line ts/naming-convention
|
||||||
public Connection(name: string, path: string, RouteHandler: typeof ConnectionHandler<C>, ...permPrivChecker) {
|
public Connection(name: string, path: string, RouteHandler: typeof ConnectionHandler<C>, ...permPrivChecker) {
|
||||||
return this.register('conn', name, path, RouteHandler, ...permPrivChecker);
|
return this.register('conn', name, path, RouteHandler, ...permPrivChecker);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,23 +90,23 @@ const saslprepString = <T = string>(regex?: RegExp, cb?: (i: string) => boolean,
|
|||||||
|
|
||||||
export const Types = {
|
export const Types = {
|
||||||
Content: [(v) => v.toString().trim(), (v) => v?.toString()?.trim() && v.toString().trim().length < 65536],
|
Content: [(v) => v.toString().trim(), (v) => v?.toString()?.trim() && v.toString().trim().length < 65536],
|
||||||
Key: saslprepString(/^[a-zA-Z0-9-_]{1,255}$/),
|
Key: saslprepString(/^[\w-]{1,255}$/),
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
Name: saslprepString(/^.{1,255}$/),
|
Name: saslprepString(/^.{1,255}$/),
|
||||||
Filename: saslprepString(/^[^\\/?#~!|*]{1,255}$/, (i) => sanitize(i) === i),
|
Filename: saslprepString(/^[^\\/?#~!|*]{1,255}$/, (i) => sanitize(i) === i),
|
||||||
UidOrName: saslprepString(/^(.{3,31}|[\u4e00-\u9fa5]{2}|-?[0-9]+)$/),
|
UidOrName: saslprepString(/^(?:.{3,31}|[\u4E00-\u9FA5]{2}|-?[0-9]+)$/),
|
||||||
Username: saslprepString(/^(.{3,31}|[\u4e00-\u9fa5]{2})$/),
|
Username: saslprepString(/^(?:.{3,31}|[\u4E00-\u9FA5]{2})$/),
|
||||||
Password: basicString(/^.{6,255}$/),
|
Password: basicString(/^.{6,255}$/),
|
||||||
ProblemId: saslprepString(/^([a-zA-Z0-9]{1,10}-)?[a-zA-Z0-9]+$/i, () => true, (s) => (Number.isSafeInteger(+s) ? +s : s)),
|
ProblemId: saslprepString(/^(?:[a-z0-9]{1,10}-)?[a-z0-9]+$/i, () => true, (s) => (Number.isSafeInteger(+s) ? +s : s)),
|
||||||
Email: saslprepString(/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/i),
|
Email: saslprepString(/^[\w.+-]+@[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i),
|
||||||
DomainId: saslprepString(/^[a-zA-Z][a-zA-Z0-9_]{3,31}$/),
|
DomainId: saslprepString(/^[a-zA-Z]\w{3,31}$/),
|
||||||
Role: saslprepString(/^[_0-9A-Za-z\u4e00-\u9fa5]{1,31}$/i),
|
Role: saslprepString(/^[\w\u4E00-\u9FA5]{1,31}$/),
|
||||||
Title: basicString(/^.{1,64}$/, (i) => !!i.trim()),
|
Title: basicString(/^.{1,64}$/, (i) => !!i.trim()),
|
||||||
ShortString: basicString(/^.{1,255}$/),
|
ShortString: basicString(/^.{1,255}$/),
|
||||||
String: basicString(),
|
String: basicString(),
|
||||||
|
|
||||||
Int: [(v) => +v, (v) => /^[+-]?[0-9]+$/.test(v.toString().trim()) && Number.isSafeInteger(+v)],
|
Int: [(v) => +v, (v) => /^[+-]?[0-9]+$/.test(v.toString().trim()) && Number.isSafeInteger(+v)],
|
||||||
UnsignedInt: [(v) => +v, (v) => /^(-0|\+?[0-9]+)$/.test(v.toString().trim()) && Number.isSafeInteger(+v)],
|
UnsignedInt: [(v) => +v, (v) => /^(?:-0|\+?[0-9]+)$/.test(v.toString().trim()) && Number.isSafeInteger(+v)],
|
||||||
PositiveInt: [(v) => +v, (v) => /^\+?[1-9][0-9]*$/.test(v.toString().trim()) && Number.isSafeInteger(+v)],
|
PositiveInt: [(v) => +v, (v) => /^\+?[1-9][0-9]*$/.test(v.toString().trim()) && Number.isSafeInteger(+v)],
|
||||||
Float: [(v) => +v, (v) => Number.isFinite(+v)],
|
Float: [(v) => +v, (v) => Number.isFinite(+v)],
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable node/no-deprecated-api */
|
||||||
const map = {};
|
const map = {};
|
||||||
require('source-map-support').install({
|
require('source-map-support').install({
|
||||||
handleUncaughtExceptions: false,
|
handleUncaughtExceptions: false,
|
||||||
@@ -27,7 +28,7 @@ const remove = [
|
|||||||
/(const|let|var)\s+__dirname\s*=\s*(path\.)?dirname\s*\(\s*__filename\s*\)\s*;?/g,
|
/(const|let|var)\s+__dirname\s*=\s*(path\.)?dirname\s*\(\s*__filename\s*\)\s*;?/g,
|
||||||
// by tsdown
|
// by tsdown
|
||||||
/(const|let|var)\s+getFilename\s*=\s*\(\s*\)\s*=>\s*fileURLToPath\s*\(\s*import\.meta\.url\s*\)\s*;?/g,
|
/(const|let|var)\s+getFilename\s*=\s*\(\s*\)\s*=>\s*fileURLToPath\s*\(\s*import\.meta\.url\s*\)\s*;?/g,
|
||||||
/(const|let|var)\s+__filename\s*=\s*(\/\*\s*@__PURE__\s*\*\/)?\s*getFilename\s*\(\s*\)\s*;?/g,
|
/(const|let|var)\s+__filename\s*=\s*(?:(\/\*\s*@__PURE__\s*\*\/)\s*)?getFilename\s*\(\s*\)\s*;?/g,
|
||||||
];
|
];
|
||||||
function tryTransform(filename, content, tsx = true) {
|
function tryTransform(filename, content, tsx = true) {
|
||||||
for (const regex of remove) content = content.replace(regex, '');
|
for (const regex of remove) content = content.replace(regex, '');
|
||||||
@@ -93,10 +94,10 @@ require.extensions['.jsc'] = function loader(module, filename) {
|
|||||||
if (![12, 13, 14, 15, 16, 17].filter((i) => process.version.startsWith(`v${i}`)).length) {
|
if (![12, 13, 14, 15, 16, 17].filter((i) => process.version.startsWith(`v${i}`)).length) {
|
||||||
bytecode.subarray(16, 20).copy(buf, 16);
|
bytecode.subarray(16, 20).copy(buf, 16);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-return-assign
|
|
||||||
const length = buf.subarray(8, 12).reduce((sum, number, power) => sum += number * (256 ** power), 0);
|
const length = buf.subarray(8, 12).reduce((sum, number, power) => sum += number * (256 ** power), 0);
|
||||||
let dummyCode = '';
|
let dummyCode = '';
|
||||||
if (length > 1) dummyCode = `"${'\u200b'.repeat(length - 2)}"`;
|
if (length > 1) dummyCode = `"${'\u200B'.repeat(length - 2)}"`;
|
||||||
const script = new vm.Script(dummyCode, {
|
const script = new vm.Script(dummyCode, {
|
||||||
filename,
|
filename,
|
||||||
lineOffset: 0,
|
lineOffset: 0,
|
||||||
|
|||||||
+3
-3
@@ -21,8 +21,8 @@
|
|||||||
"test": "node -r @hydrooj/register packages/common/tests/subtask.spec.ts && node test/entry.js",
|
"test": "node -r @hydrooj/register packages/common/tests/subtask.spec.ts && node test/entry.js",
|
||||||
"benchmark": "cross-env BENCHMARK=true node test/entry.js",
|
"benchmark": "cross-env BENCHMARK=true node test/entry.js",
|
||||||
"oxlint": "cd framework/eslint-config && node -r @hydrooj/register build.ts && cd ../../ && oxlint --config framework/eslint-config/.oxlintrc.json",
|
"oxlint": "cd framework/eslint-config && node -r @hydrooj/register build.ts && cd ../../ && oxlint --config framework/eslint-config/.oxlintrc.json",
|
||||||
"lint": "eslint --flag v10_config_lookup_from_file packages plugins modules framework --ext .ts,.tsx,.jsx --fix",
|
"lint": "eslint --flag v10_config_lookup_from_file packages plugins modules framework --ext .ts,.tsx,.jsx,.vue,.yaml,.yml --fix",
|
||||||
"lint:ci": "eslint --flag v10_config_lookup_from_file packages plugins modules framework --ext .ts,.tsx,.jsx --max-warnings=0",
|
"lint:ci": "eslint --flag v10_config_lookup_from_file packages plugins modules framework --ext .ts,.tsx,.jsx,.vue,.yaml,.yml --max-warnings=0",
|
||||||
"debug": "node --trace-warnings --inspect=192.168.99.234:1111 --async-stack-traces --trace-deprecation packages/hydrooj/bin/hydrooj --debug --template",
|
"debug": "node --trace-warnings --inspect=192.168.99.234:1111 --async-stack-traces --trace-deprecation packages/hydrooj/bin/hydrooj --debug --template",
|
||||||
"start": "packages/hydrooj/bin/hydrooj.js",
|
"start": "packages/hydrooj/bin/hydrooj.js",
|
||||||
"postinstall": "node build/prepare.js",
|
"postinstall": "node build/prepare.js",
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
"eslint": "^9.32.0",
|
"eslint": "^9.32.0",
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-import-resolver-webpack": "^0.13.10",
|
"eslint-import-resolver-webpack": "^0.13.10",
|
||||||
"fs-extra": "^11.3.0",
|
"fs-extra": "^11.3.1",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
"globby": "~14.0.2",
|
"globby": "~14.0.2",
|
||||||
"inspectpack": "^4.7.1",
|
"inspectpack": "^4.7.1",
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ export async function startPerformanceTest(args: { enable5: boolean }, report) {
|
|||||||
await ProblemModel.addTestdata('system', docId, 'config.yaml', Buffer.from(yaml.dump({
|
await ProblemModel.addTestdata('system', docId, 'config.yaml', Buffer.from(yaml.dump({
|
||||||
time: '3s',
|
time: '3s',
|
||||||
memory: args.enable5 ? '2g' : '512m',
|
memory: args.enable5 ? '2g' : '512m',
|
||||||
cases: new Array(20).fill({
|
cases: Array.from({ length: 20 }).fill({
|
||||||
input: '1.in',
|
input: '1.in',
|
||||||
output: '1.out',
|
output: '1.out',
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { findFileSync, parseMemoryMB, parseTimeMS } from '@hydrooj/utils';
|
import { findFileSync, parseMemoryMB, parseTimeMS } from '@hydrooj/utils';
|
||||||
import { CompilableSource, ProblemConfigFile } from './types';
|
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) {
|
export async function readYamlCases(cfg: ProblemConfigFile = {}, checkFile = (s: CompilableSource, errMsg: string) => s) {
|
||||||
const config: any = {
|
const config: any = {
|
||||||
checker_type: cfg.checker_type || 'default',
|
checker_type: cfg.checker_type || 'default',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export * from './lang';
|
export * from './lang';
|
||||||
export * from './status';
|
export * from './status';
|
||||||
export * from './types';
|
|
||||||
export * from './subtask';
|
export * from './subtask';
|
||||||
|
export * from './types';
|
||||||
@@ -2,18 +2,18 @@ import { parseMemoryMB, parseTimeMS } from '@hydrooj/utils/lib/common';
|
|||||||
|
|
||||||
export function convertIniConfig(ini: string) {
|
export function convertIniConfig(ini: string) {
|
||||||
const f = ini.split('\n');
|
const f = ini.split('\n');
|
||||||
const count = parseInt(f[0], 10);
|
const count = Number.parseInt(f[0], 10);
|
||||||
const res = { subtasks: [] };
|
const res = { subtasks: [] };
|
||||||
for (let i = 1; i <= count; i++) {
|
for (let i = 1; i <= count; i++) {
|
||||||
if (!f[i]?.trim()) throw new Error('Testdata count incorrect.');
|
if (!f[i]?.trim()) throw new Error('Testdata count incorrect.');
|
||||||
const [input, output, time, score, memory] = f[i].split('|');
|
const [input, output, time, score, memory] = f[i].split('|');
|
||||||
const cur = {
|
const cur = {
|
||||||
cases: [{ input, output }],
|
cases: [{ input, output }],
|
||||||
score: parseInt(score, 10),
|
score: Number.parseInt(score, 10),
|
||||||
time: `${time}s`,
|
time: `${time}s`,
|
||||||
memory: '256m',
|
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);
|
res.subtasks.push(cur);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -29,7 +29,8 @@ interface MatchRule {
|
|||||||
|
|
||||||
const SubtaskMatcher: 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']
|
output: (a) => ['out', 'ans']
|
||||||
.flatMap((i) => [i, i.toUpperCase(), `${i}.txt`, `${i.toUpperCase()}.TXT`])
|
.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')])
|
.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'),
|
preferredScorerType: (a) => (a[3] ? 'min' : 'sum'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
regex: /^([^\d]*)\.(in|IN)(\d+)$/,
|
regex: /^(\D*)\.(in|IN)(\d+)$/,
|
||||||
output: (a) => [
|
output: (a) => [
|
||||||
`${a[1]}.${a[2] === 'in' ? 'ou' : 'OU'}${a[3]}`,
|
`${a[1]}.${a[2] === 'in' ? 'ou' : 'OU'}${a[3]}`,
|
||||||
`${a[1]}.${a[2] === 'in' ? 'out' : 'OUT'}${a[3]}`,
|
`${a[1]}.${a[2] === 'in' ? 'out' : 'OUT'}${a[3]}`,
|
||||||
@@ -49,14 +50,14 @@ const SubtaskMatcher: MatchRule[] = [
|
|||||||
preferredScorerType: () => 'sum',
|
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}`),
|
output: (a) => ['out', 'ans', 'OUT', 'ANS'].flatMap((i) => `${a[1]}${a[2]}${a[3]}${a[4]}.${i}`),
|
||||||
id: (a) => +a[4],
|
id: (a) => +a[4],
|
||||||
subtask: (a) => +a[2],
|
subtask: (a) => +a[2],
|
||||||
preferredScorerType: () => 'min',
|
preferredScorerType: () => 'min',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
regex: /^(([0-9]+)[-_](?:.*))\.(in|IN)$/,
|
regex: /^(([0-9]+)[-_].*)\.(in|IN)$/,
|
||||||
output: (a) => ['out', 'ans', 'OUT', 'ANS'].flatMap((i) => `${a[1]}.${i}`),
|
output: (a) => ['out', 'ans', 'OUT', 'ANS'].flatMap((i) => `${a[1]}.${i}`),
|
||||||
id: (a) => +a[2],
|
id: (a) => +a[2],
|
||||||
subtask: () => 1,
|
subtask: () => 1,
|
||||||
|
|||||||
@@ -66,13 +66,13 @@ export interface ProblemConfigFile {
|
|||||||
|
|
||||||
export interface FileInfo {
|
export interface FileInfo {
|
||||||
/** storage path */
|
/** storage path */
|
||||||
_id: string,
|
_id: string;
|
||||||
/** filename */
|
/** filename */
|
||||||
name: string,
|
name: string;
|
||||||
/** file size (in bytes) */
|
/** file size (in bytes) */
|
||||||
size: number,
|
size: number;
|
||||||
etag: string,
|
etag: string;
|
||||||
lastModified: Date,
|
lastModified: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JudgeMeta {
|
export interface JudgeMeta {
|
||||||
@@ -113,7 +113,7 @@ export interface RecordPayload extends RecordJudgeInfo {
|
|||||||
/** 0 if pretest&script */
|
/** 0 if pretest&script */
|
||||||
contest?: string;
|
contest?: string;
|
||||||
|
|
||||||
files?: Record<string, string>
|
files?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JudgeRequest extends Omit<RecordPayload, 'testCases'> {
|
export interface JudgeRequest extends Omit<RecordPayload, 'testCases'> {
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ function MonacoContainer({
|
|||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
const current = editor.getValue({ lineEnding: '\n', preserveBOM: false });
|
const current = editor.getValue({ lineEnding: '\n', preserveBOM: false });
|
||||||
const diff = diffLines(current, config);
|
const diff = diffLines(current, config);
|
||||||
const ops: { range: any; text: string }[] = [];
|
const ops: { range: any, text: string }[] = [];
|
||||||
let cursor = 1;
|
let cursor = 1;
|
||||||
for (const line of diff) {
|
for (const line of diff) {
|
||||||
if (line.added) {
|
if (line.added) {
|
||||||
@@ -109,11 +109,7 @@ function MonacoContainer({
|
|||||||
}
|
}
|
||||||
model.pushEditOperations([], ops, undefined);
|
model.pushEditOperations([], ops, undefined);
|
||||||
}, [editor, config]);
|
}, [editor, config]);
|
||||||
return (
|
return <div ref={initializeEditor} style={{ width: '100%', height: '500px' }} />;
|
||||||
<div ref={initializeEditor} style={{
|
|
||||||
width: '100%', height: '500px',
|
|
||||||
}} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ConfigEditor({
|
export default function ConfigEditor({
|
||||||
@@ -166,8 +162,12 @@ export default function ConfigEditor({
|
|||||||
<Allotment onChange={setSize}>
|
<Allotment onChange={setSize}>
|
||||||
<Allotment.Pane>
|
<Allotment.Pane>
|
||||||
<MonacoContainer
|
<MonacoContainer
|
||||||
editorCallback={registerAction} schema={schema} monaco={monaco}
|
editorCallback={registerAction}
|
||||||
config={stringConfig} setValue={updateFromMonaco} setError={setInfo}
|
schema={schema}
|
||||||
|
monaco={monaco}
|
||||||
|
config={stringConfig}
|
||||||
|
setValue={updateFromMonaco}
|
||||||
|
setError={setInfo}
|
||||||
size={size[0]}
|
size={size[0]}
|
||||||
dark={document.documentElement.className.includes('theme--dark')}
|
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 AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, ref: React.Ref<AutoCompleteHandle<T>>) {
|
||||||
const {
|
const {
|
||||||
multi = false, width = '100%', height = 'auto',
|
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,
|
disabled = false, disabledHint = '', draggable = multi,
|
||||||
} = props;
|
} = props;
|
||||||
const queryItems = props.queryItems ?? (() => []);
|
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 itemText = props.itemText ?? ((item) => item.toString());
|
||||||
const itemKey = props.itemKey ?? itemText;
|
const itemKey = props.itemKey ?? itemText;
|
||||||
const onChange = props.onChange ?? (() => { });
|
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 [focused, setFocused] = useState(false); // is focused
|
||||||
const [selectedKeys, setSelectedKeys] = useState(props.selectedKeys || []); // keys of selected items
|
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 ConfigEditor } from './ConfigEditor';
|
||||||
export { default as Icon } from './Icon';
|
export { default as Icon } from './Icon';
|
||||||
export { default as Notification } from './Notification';
|
export { default as Notification } from './Notification';
|
||||||
export { default as AutoComplete, type AutoCompleteHandle, type AutoCompleteProps } from './autocomplete/AutoComplete';
|
export { default as ComponentsProvider } from './provider';
|
||||||
export { default as CustomSelectAutoComplete } from './autocomplete/CustomSelectAutoComplete';
|
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"@types/diff": "^8.0.0",
|
"@types/diff": "^8.0.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/lodash": "^4.17.20",
|
"@types/lodash": "^4.17.20",
|
||||||
"sass": "^1.89.2"
|
"sass": "^1.90.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class FpsProblemImportHandler extends Handler {
|
|||||||
samples: p.sample_input?.map((input, i) => [input, p.sample_output[i]]),
|
samples: p.sample_input?.map((input, i) => [input, p.sample_output[i]]),
|
||||||
hint: p.hint?.[0],
|
hint: p.hint?.[0],
|
||||||
source: p.source?.join(' '),
|
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 = {
|
const config: ProblemConfigFile = {
|
||||||
time: p.time_limit[0]._ + p.time_limit[0].$.unit,
|
time: p.time_limit[0]._ + p.time_limit[0].$.unit,
|
||||||
memory: p.memory_limit[0]._ + p.memory_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);
|
const reader = new Reader(buffer);
|
||||||
|
|
||||||
export interface Result {
|
export interface Result {
|
||||||
location?: string,
|
location?: string;
|
||||||
continent?: string,
|
continent?: string;
|
||||||
country?: string,
|
country?: string;
|
||||||
city?: string,
|
city?: string;
|
||||||
display: string
|
display: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class GeoIPService extends Service {
|
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:
|
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:
|
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:
|
pas:
|
||||||
compile: /usr/bin/fpc -O2 -ofoo foo.pas
|
compile: /usr/bin/fpc -O2 -ofoo foo.pas
|
||||||
java:
|
java:
|
||||||
compile: /usr/bin/bash -c "javac -d /w -encoding utf8 ./Main.java && jar cvf Main.jar *.class >/dev/null"
|
compile: /usr/bin/bash -c "javac -d /w -encoding utf8 ./Main.java && jar cvf Main.jar *.class >/dev/null"
|
||||||
code_file: Main.java
|
code_file: Main.java
|
||||||
target: Main.jar
|
target: Main.jar
|
||||||
execute: /usr/bin/java -Dfile.encoding="UTF-8" -cp Main.jar Main
|
execute: /usr/bin/java -Dfile.encoding="UTF-8" -cp Main.jar Main
|
||||||
time_limit_rate: 2
|
time_limit_rate: 2
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
compile_memory_limit: 1024
|
compile_memory_limit: 1024
|
||||||
py3:
|
py3:
|
||||||
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
compile: /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py', '/w/foo', doraise=True)"
|
||||||
code_file: foo.py
|
code_file: foo.py
|
||||||
execute: /usr/bin/python3 foo
|
execute: /usr/bin/python3 foo
|
||||||
php:
|
php:
|
||||||
execute: /usr/bin/php foo.php
|
execute: /usr/bin/php foo.php
|
||||||
rs:
|
rs:
|
||||||
compile: /usr/bin/rustc -O -o /w/foo /w/foo.rs
|
compile: /usr/bin/rustc -O -o /w/foo /w/foo.rs
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
hs:
|
hs:
|
||||||
compile: /usr/bin/ghc -O -outputdir /tmp -o /w/foo /w/foo.hs
|
compile: /usr/bin/ghc -O -outputdir /tmp -o /w/foo /w/foo.hs
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
js:
|
js:
|
||||||
execute: /usr/bin/jsc foo.js
|
execute: /usr/bin/jsc foo.js
|
||||||
go:
|
go:
|
||||||
compile: /usr/bin/go build -o /w/foo /w/foo.go
|
compile: /usr/bin/go build -o /w/foo /w/foo.go
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
rb:
|
rb:
|
||||||
execute: /usr/bin/ruby foo.rb
|
execute: /usr/bin/ruby foo.rb
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
cs:
|
cs:
|
||||||
compile: /usr/bin/mcs csc -optimize+ -out:/w/foo.exe /w/foo.cs
|
compile: /usr/bin/mcs csc -optimize+ -out:/w/foo.exe /w/foo.cs
|
||||||
execute: /usr/bin/mono foo.exe
|
execute: /usr/bin/mono foo.exe
|
||||||
compile_time_limit: 30000
|
compile_time_limit: 30000
|
||||||
@@ -36,4 +36,4 @@ Unknown language type.: 未知的语言类型。
|
|||||||
Unsupported language {0}.: 不支持的语言 {0}。
|
Unsupported language {0}.: 不支持的语言 {0}。
|
||||||
User output longer than standard answer.: 选手输出比标准输出长。
|
User output longer than standard answer.: 选手输出比标准输出长。
|
||||||
You did not specify a checker.: 你没有指定比较器文件。
|
You did not specify a checker.: 你没有指定比较器文件。
|
||||||
Your program returned {0}.: 您的程序返回了{0}。
|
Your program returned {0}.: 您的程序返回了{0}。
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hydrooj/common": "workspace:^",
|
"@hydrooj/common": "workspace:^",
|
||||||
"@hydrooj/utils": "workspace:^",
|
"@hydrooj/utils": "workspace:^",
|
||||||
"@zip.js/zip.js": "^2.7.71",
|
"@zip.js/zip.js": "^2.7.72",
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"p-queue": "^8.1.0",
|
"p-queue": "^8.1.0",
|
||||||
"schemastery": "^3.16.1",
|
"schemastery": "^3.16.1",
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ export interface CheckConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Checker = (config: CheckConfig) => Promise<{
|
type Checker = (config: CheckConfig) => Promise<{
|
||||||
status: number,
|
status: number;
|
||||||
score: number,
|
score: number;
|
||||||
message: string,
|
message: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
function parseDiffMsg(msg: string) {
|
function parseDiffMsg(msg: string) {
|
||||||
@@ -206,7 +206,6 @@ const checkers: Record<string, Checker> = new Proxy({
|
|||||||
* stderr:输出错误报告
|
* stderr:输出错误报告
|
||||||
*/
|
*/
|
||||||
async syzoj(config) {
|
async syzoj(config) {
|
||||||
// eslint-disable-next-line prefer-const
|
|
||||||
let { status, stdout, stderr } = await runQueued(config.execute, {
|
let { status, stdout, stderr } = await runQueued(config.execute, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
input: config.input,
|
input: config.input,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { STATUS_TEXTS } from '@hydrooj/common';
|
import { STATUS_TEXTS } from '@hydrooj/common';
|
||||||
|
|
||||||
interface CompileErrorInfo {
|
interface CompileErrorInfo {
|
||||||
stdout?: string,
|
stdout?: string;
|
||||||
stderr?: string,
|
stderr?: string;
|
||||||
status?: number
|
status?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileError extends Error {
|
export class CompileError extends Error {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface Task {
|
|||||||
compile: () => Promise<void>;
|
compile: () => Promise<void>;
|
||||||
judgeCase: (c: NormalizedCase) => (
|
judgeCase: (c: NormalizedCase) => (
|
||||||
(ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => Promise<JudgeResultBody['case']>
|
(ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => Promise<JudgeResultBody['case']>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Score = {
|
const Score = {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export async function judge({
|
|||||||
if (!Object.keys(config.answers).length) throw new FormatError('Invalid standard answer.');
|
if (!Object.keys(config.answers).length) throw new FormatError('Invalid standard answer.');
|
||||||
for (const key in config.answers) {
|
for (const key in config.answers) {
|
||||||
const ansInfo = config.answers[key] as [string | string[], number] | Record<string, number>;
|
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 report = (status: STATUS, score: number, message: string) => {
|
||||||
const [subtaskId, caseId] = key.split('-').map(Number);
|
const [subtaskId, caseId] = key.split('-').map(Number);
|
||||||
totalScore += score;
|
totalScore += score;
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export class Stream extends EventEmitter {
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
this.emit('output', {
|
this.emit('output', {
|
||||||
index: (data[1] >> 4) & 0xf,
|
index: (data[1] >> 4) & 0xF,
|
||||||
fd: (data[1]) & 0xf,
|
fd: (data[1]) & 0xF,
|
||||||
content: data.subarray(2),
|
content: data.subarray(2),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { DetailType, STATUS } from '@hydrooj/common';
|
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) {
|
export function parse(output: string, fullscore: number, detail: DetailType) {
|
||||||
let status = STATUS.STATUS_WRONG_ANSWER;
|
let status = STATUS.STATUS_WRONG_ANSWER;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { CompilableSource } from '@hydrooj/common';
|
|||||||
import { fs } from '@hydrooj/utils';
|
import { fs } from '@hydrooj/utils';
|
||||||
import { FormatError } from './error';
|
import { FormatError } from './error';
|
||||||
|
|
||||||
const EMPTY_STR = /^[ \r\n\t]*$/i;
|
const EMPTY_STR = /^[ \r\n\t]*$/;
|
||||||
|
|
||||||
export const cmd = parse;
|
export const cmd = parse;
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ export function compilerText(...messages: string[]) {
|
|||||||
function restrictFile(p: string) {
|
function restrictFile(p: string) {
|
||||||
if (!p) return '/';
|
if (!p) return '/';
|
||||||
if (p[0] === '/') p = '';
|
if (p[0] === '/') p = '';
|
||||||
return p.replace(/\.\./gmi, '');
|
return p.replace(/\.\./g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ensureFile(folder: string) {
|
export function ensureFile(folder: string) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable import/no-dynamic-require */
|
|
||||||
import cac from 'cac';
|
import cac from 'cac';
|
||||||
import { getAddons } from '../src/options';
|
import { getAddons } from '../src/options';
|
||||||
|
|
||||||
|
|||||||
@@ -114,4 +114,4 @@ user_lostpass: Lost Password
|
|||||||
user_register: Register
|
user_register: Register
|
||||||
user_sudo: Entering Sudo Mode
|
user_sudo: Entering Sudo Mode
|
||||||
wiki_about: About
|
wiki_about: About
|
||||||
wiki_help: Help
|
wiki_help: Help
|
||||||
@@ -41,9 +41,6 @@ __langname: 한국어
|
|||||||
(All Problems): (전체 문제)
|
(All Problems): (전체 문제)
|
||||||
(None): (N/A)
|
(None): (N/A)
|
||||||
(Not changed): 수정되지 않음
|
(Not changed): 수정되지 않음
|
||||||
#Sample Input: Sample Input
|
|
||||||
#Sample Output: Sample Output
|
|
||||||
#Star: Star
|
|
||||||
Aborted: 중지됨
|
Aborted: 중지됨
|
||||||
About {0}: '{0}에 대하여'
|
About {0}: '{0}에 대하여'
|
||||||
About Markdown: Markdown에 대하여
|
About Markdown: Markdown에 대하여
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ __id: zh
|
|||||||
__interface: yes
|
__interface: yes
|
||||||
__langname: 简体中文
|
__langname: 简体中文
|
||||||
'"{data}" copied to clipboard!': “{data}”已复制到剪贴板!
|
'"{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} comments': '{0} 条评论'
|
||||||
'{0} discussions': '{0} 条讨论'
|
'{0} discussions': '{0} 条讨论'
|
||||||
'{0} is a moderator of this domain.': '{}是这个域的管理者之一。'
|
'{0} is a moderator of this domain.': '{}是这个域的管理者之一。'
|
||||||
@@ -38,7 +38,7 @@ __langname: 简体中文
|
|||||||
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
||||||
'Yes': 是
|
'Yes': 是
|
||||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
||||||
"Or upload a file:": 或者上传一个文件:
|
'Or upload a file:': 或者上传一个文件:
|
||||||
"Split by ',' '.'": 由“, ”或“,”分隔。
|
"Split by ',' '.'": 由“, ”或“,”分隔。
|
||||||
"What's file?": 什么是文件?
|
"What's file?": 什么是文件?
|
||||||
(All Problems): (所有题目)
|
(All Problems): (所有题目)
|
||||||
@@ -64,8 +64,8 @@ Add new data: 添加新数据
|
|||||||
Add User: 添加用户
|
Add User: 添加用户
|
||||||
Add: 添加
|
Add: 添加
|
||||||
Additional File: 附加文件
|
Additional File: 附加文件
|
||||||
Admin {0} delete your discussion "{1}". : 管理员 {0} 删除了你的讨论 "{1}"。
|
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 tail reply {1} in "{2}"({3}).: 管理员 {0} 从讨论 {2}({3}) 中删除了你的楼中楼回复 {1}。
|
||||||
All {0} Contests: 所有 {0} 比赛
|
All {0} Contests: 所有 {0} 比赛
|
||||||
All Contests: 所有比赛
|
All Contests: 所有比赛
|
||||||
All Homeworks: 所有作业
|
All Homeworks: 所有作业
|
||||||
@@ -947,4 +947,4 @@ You've already attended this contest.: 您已经参加本次比赛。
|
|||||||
You've already claimed this homework.: 您已认领过该作业。
|
You've already claimed this homework.: 您已认领过该作业。
|
||||||
You've already voted.: 您已经投过票。
|
You've already voted.: 您已经投过票。
|
||||||
Your permission: 您的权限
|
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 claimed this homework.: 您已認領該功課。
|
||||||
You've already voted.: 您已經投過票。
|
You've already voted.: 您已經投過票。
|
||||||
Your permission: 您的許可權
|
Your permission: 您的許可權
|
||||||
Your role: 您的角色
|
Your role: 您的角色
|
||||||
@@ -23,14 +23,14 @@
|
|||||||
"@hydrooj/utils": "workspace:^",
|
"@hydrooj/utils": "workspace:^",
|
||||||
"@simplewebauthn/server": "13.1.2",
|
"@simplewebauthn/server": "13.1.2",
|
||||||
"@undefined-moe/require-resolve-hook": "^1.1.0",
|
"@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",
|
"adm-zip": "0.5.16",
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"cordis": "4.0.0-beta.5",
|
"cordis": "4.0.0-beta.5",
|
||||||
"csv-stringify": "^6.6.0",
|
"csv-stringify": "^6.6.0",
|
||||||
"emoji-regex": "^10.4.0",
|
"emoji-regex": "^10.4.0",
|
||||||
"emojis-list": "2.1.0",
|
"emojis-list": "2.1.0",
|
||||||
"fs-extra": "^11.3.0",
|
"fs-extra": "^11.3.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"koa-proxies": "^0.12.4",
|
"koa-proxies": "^0.12.4",
|
||||||
"koa-static-cache": "^5.1.4",
|
"koa-static-cache": "^5.1.4",
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ langs:
|
|||||||
__hint__:
|
__hint__:
|
||||||
- ~ All keys starting with _ will be ignored
|
- ~ All keys starting with _ will be ignored
|
||||||
- ~ Entries named like [foo], [foo].[bar] [foo].[baz]
|
- ~ 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
|
- ~ 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]
|
- ~ code_file option defaults to foo.[name]
|
||||||
- ~ highlight option defaults to [name]
|
- ~ highlight option defaults to [name]
|
||||||
- ~ if highlight option was set, monaco option defaults
|
- ~ 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
|
- ~ Compiling be be ignored if compile option wasn't set
|
||||||
- ~ Key marked with a ? means the option is optional
|
- ~ Key marked with a ? means the option is optional
|
||||||
- (name):
|
- (name):
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable ts/no-unsafe-declaration-merging */
|
||||||
import LoggerService from '@cordisjs/plugin-logger';
|
import LoggerService from '@cordisjs/plugin-logger';
|
||||||
import { TimerService } from '@cordisjs/plugin-timer';
|
import { TimerService } from '@cordisjs/plugin-timer';
|
||||||
import * as cordis from 'cordis';
|
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 type Fiber = cordis.Fiber<Context>;
|
||||||
|
|
||||||
export { Disposable, Plugin, FiberState } from 'cordis';
|
export { Disposable, FiberState, Plugin } from 'cordis';
|
||||||
|
|
||||||
export interface Context extends cordis.Context {
|
export interface Context extends cordis.Context {
|
||||||
[Context.events]: EventMap & cordis.Events<Context>;
|
[Context.events]: EventMap & cordis.Events<Context>;
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
|
|
||||||
const argv = cac().parse();
|
const argv = cac().parse();
|
||||||
const tmpdir = path.resolve(os.tmpdir(), 'hydro');
|
const tmpdir = path.resolve(os.tmpdir(), 'hydro');
|
||||||
const COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
const COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
|
||||||
const ARR = /=>.*$/mg;
|
const ARR = /=>.*$/gm;
|
||||||
function parseParameters(fn: Function) {
|
function parseParameters(fn: Function) {
|
||||||
const code = fn.toString()
|
const code = fn.toString()
|
||||||
.replace(COMMENTS, '')
|
.replace(COMMENTS, '')
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable import/no-dynamic-require */
|
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import '../lib/index';
|
import '../lib/index';
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export class ContestListHandler extends Handler {
|
|||||||
if (group && !groups.includes(group)) throw new NotAssignedError(group);
|
if (group && !groups.includes(group)) throw new NotAssignedError(group);
|
||||||
const rules = Object.keys(contest.RULES).filter((i) => !contest.RULES[i].hidden);
|
const rules = Object.keys(contest.RULES).filter((i) => !contest.RULES[i].hidden);
|
||||||
const escaped = escapeRegExp(q.toLowerCase());
|
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 = {
|
const filter = {
|
||||||
...(this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) && !group)
|
...(this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) && !group)
|
||||||
? {}
|
? {}
|
||||||
@@ -626,10 +626,10 @@ export class ContestBalloonHandler extends ContestManagementBaseHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuiltinInput = {
|
interface BuiltinInput {
|
||||||
tdoc: Tdoc;
|
tdoc: Tdoc;
|
||||||
groups: any[];
|
groups: any[];
|
||||||
};
|
}
|
||||||
type AnyFunction = (...args: any) => any;
|
type AnyFunction = (...args: any) => any;
|
||||||
type ParseArgs<T extends { [key: string]: keyof BuiltinInput | AnyFunction | Type<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
|
[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> }>(
|
addView<T extends { [key: string]: keyof BuiltinInput | AnyFunction | Type<any> }>(
|
||||||
id: string, name: string, args: T,
|
id: string, name: string, args: T,
|
||||||
{ display, supportedRules, cacheTime }: {
|
{ display, supportedRules, cacheTime }: {
|
||||||
display: (this: ContestScoreboardHandler, args: ParseArgs<T>) => Promise<void>,
|
display: (this: ContestScoreboardHandler, args: ParseArgs<T>) => Promise<void>;
|
||||||
supportedRules: string[],
|
supportedRules: string[];
|
||||||
cacheTime?: number,
|
cacheTime?: number;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if (this.views[id]) throw new Error(`View ${id} already exists`);
|
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;
|
config.lockAt = this.tdoc.lockAt;
|
||||||
}
|
}
|
||||||
const [, rows, udict, pdict] = await contest.getScoreboard.call(this, tdoc.domainId, tdoc._id, config);
|
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'
|
const page_name = tdoc.rule === 'homework'
|
||||||
? 'homework_scoreboard'
|
? 'homework_scoreboard'
|
||||||
: 'contest_scoreboard';
|
: 'contest_scoreboard';
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class DiscussionNodeHandler extends DiscussionHandler {
|
|||||||
async get(domainId: string, type: string, _name: string, page = 1) {
|
async get(domainId: string, type: string, _name: string, page = 1) {
|
||||||
let name: ObjectId | string | number;
|
let name: ObjectId | string | number;
|
||||||
if (ObjectId.isValid(_name)) name = new ObjectId(_name);
|
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;
|
else name = _name;
|
||||||
const hidden = this.user.own(this.vnode) || this.user.hasPerm(PERM.PERM_EDIT_DISCUSSION) ? {} : { hidden: false };
|
const hidden = this.user.own(this.vnode) || this.user.hasPerm(PERM.PERM_EDIT_DISCUSSION) ? {} : { hidden: false };
|
||||||
const [ddocs, dpcount] = await this.paginate(
|
const [ddocs, dpcount] = await this.paginate(
|
||||||
|
|||||||
@@ -534,7 +534,7 @@ class HomeDomainCreateHandler extends Handler {
|
|||||||
@param('name', Types.Title)
|
@param('name', Types.Title)
|
||||||
@param('bulletin', Types.Content)
|
@param('bulletin', Types.Content)
|
||||||
@param('avatar', Types.Content, true)
|
@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) {
|
async post(_: string, id: string, name: string, bulletin: string, avatar: string) {
|
||||||
const doc = await domain.get(id);
|
const doc = await domain.get(id);
|
||||||
if (doc) throw new DomainAlreadyExistsError(id);
|
if (doc) throw new DomainAlreadyExistsError(id);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class HomeworkMainHandler extends Handler {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
...group ? { assign: { $in: [group] } } : {},
|
...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({
|
}).sort({
|
||||||
penaltySince: -1, endAt: -1, beginAt: -1, _id: -1,
|
penaltySince: -1, endAt: -1, beginAt: -1, _id: -1,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export class JudgeResultCallbackContext {
|
|||||||
private relatedId = new ObjectId();
|
private relatedId = new ObjectId();
|
||||||
private meta: { rejudge?: JudgeMeta['rejudge'] };
|
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.meta = task.meta as JudgeMeta || {};
|
||||||
this.finishPromise = new Promise((resolve) => {
|
this.finishPromise = new Promise((resolve) => {
|
||||||
this.resolve = resolve;
|
this.resolve = resolve;
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ class SystemUserImportHandler extends SystemHandler {
|
|||||||
@param('draft', Types.Boolean)
|
@param('draft', Types.Boolean)
|
||||||
async post(domainId: string, _users: string, draft: boolean) {
|
async post(domainId: string, _users: string, draft: boolean) {
|
||||||
const users = _users.split('\n');
|
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 messages = [];
|
||||||
const mapping = Object.create(null);
|
const mapping = Object.create(null);
|
||||||
const groups: Record<string, string[]> = 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 defaultSearch = async (domainId: string, q: string, options?: ProblemSearchOptions) => {
|
||||||
const escaped = escapeRegExp(q.toLowerCase());
|
const escaped = escapeRegExp(q.toLowerCase());
|
||||||
const projection: (keyof ProblemDoc)[] = ['domainId', 'docId', 'pid'];
|
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 filter = { $or: [{ pid: { $regex } }, { title: { $regex } }, { tag: q }] };
|
||||||
const pdocs = await problem.getMulti(domainId, filter, projection)
|
const pdocs = await problem.getMulti(domainId, filter, projection)
|
||||||
.skip(options.skip || 0).limit(options.limit || system.get('pagination.problem')).toArray();
|
.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';
|
this.response.template = 'problem_main.html';
|
||||||
if (!limit || limit > this.ctx.setting.get('pagination.problem') || page > 1) limit = this.ctx.setting.get('pagination.problem');
|
if (!limit || limit > this.ctx.setting.get('pagination.problem') || page > 1) limit = this.ctx.setting.get('pagination.problem');
|
||||||
this.queryContext.query = buildQuery(this.user);
|
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 query = this.queryContext.query;
|
||||||
const psdict = {};
|
const psdict = {};
|
||||||
const search = Object.values(global.Hydro.module.problemSearch)[0] || defaultSearch;
|
const search = Object.values(global.Hydro.module.problemSearch)[0] || defaultSearch;
|
||||||
@@ -607,7 +607,7 @@ export class ProblemEditHandler extends ProblemManageHandler {
|
|||||||
@route('pid', Types.ProblemId)
|
@route('pid', Types.ProblemId)
|
||||||
@post('title', Types.Title)
|
@post('title', Types.Title)
|
||||||
@post('content', Types.Content)
|
@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('hidden', Types.Boolean)
|
||||||
@post('tag', Types.Content, true, null, parseCategory)
|
@post('tag', Types.Content, true, null, parseCategory)
|
||||||
@post('difficulty', Types.PositiveInt, (i) => +i <= 10, true)
|
@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)) {
|
if (!accepted || !this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION_ACCEPT)) {
|
||||||
this.checkPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM_SOLUTION);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line prefer-const
|
|
||||||
let [psdocs, pcount, pscount] = await this.paginate(
|
let [psdocs, pcount, pscount] = await this.paginate(
|
||||||
solution.getMulti(domainId, this.pdoc.docId),
|
solution.getMulti(domainId, this.pdoc.docId),
|
||||||
page,
|
page,
|
||||||
@@ -995,7 +995,7 @@ export class ProblemCreateHandler extends Handler {
|
|||||||
|
|
||||||
@post('title', Types.Title)
|
@post('title', Types.Title)
|
||||||
@post('content', Types.Content)
|
@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('hidden', Types.Boolean)
|
||||||
@post('difficulty', Types.PositiveInt, (i) => +i <= 10, true)
|
@post('difficulty', Types.PositiveInt, (i) => +i <= 10, true)
|
||||||
@post('tag', Types.Content, true, null, parseCategory)
|
@post('tag', Types.Content, true, null, parseCategory)
|
||||||
@@ -1006,7 +1006,7 @@ export class ProblemCreateHandler extends Handler {
|
|||||||
if (typeof pid !== 'string') pid = `P${pid}`;
|
if (typeof pid !== 'string') pid = `P${pid}`;
|
||||||
if (pid && await problem.get(domainId, pid)) throw new ProblemAlreadyExistError(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 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 = [];
|
const tasks = [];
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (this.user._files.find((i) => i.name === file)) {
|
if (this.user._files.find((i) => i.name === file)) {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class RecordListHandler extends ContestDetailBaseHandler {
|
|||||||
}
|
}
|
||||||
if (pid) {
|
if (pid) {
|
||||||
if (typeof pid === 'string' && tdoc && /^[A-Z]$/.test(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);
|
const pdoc = await problem.get(domainId, pid);
|
||||||
if (pdoc) q.pid = pdoc.docId;
|
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.pid === 'number' && rdoc.pid !== this.pid) return;
|
||||||
if (typeof this.uid === 'number' && rdoc.uid !== this.uid) return;
|
if (typeof this.uid === 'number' && rdoc.uid !== this.uid) return;
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-const
|
|
||||||
let [udoc, pdoc] = await Promise.all([
|
let [udoc, pdoc] = await Promise.all([
|
||||||
user.getById(this.args.domainId, rdoc.uid),
|
user.getById(this.args.domainId, rdoc.uid),
|
||||||
problem.get(rdoc.domainId, rdoc.pid),
|
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');
|
if (versionNum < 22) throw new Error('NodeJS >=22 required');
|
||||||
|
|
||||||
console.log('Process', process.pid, 'running as', process.env.NODE_APP_INSTANCE === '0' ? 'master' : 'worker');
|
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 { AttestationFormat, CredentialDeviceType } from '@simplewebauthn/server';
|
||||||
import type { ParsedAuthenticatorData } from '@simplewebauthn/server/helpers';
|
import type { ParsedAuthenticatorData } from '@simplewebauthn/server/helpers';
|
||||||
import type fs from 'fs';
|
import type fs from 'fs';
|
||||||
@@ -16,43 +17,43 @@ export * from '@hydrooj/common/types';
|
|||||||
type document = typeof import('./model/document');
|
type document = typeof import('./model/document');
|
||||||
|
|
||||||
export interface System {
|
export interface System {
|
||||||
_id: string,
|
_id: string;
|
||||||
value: any,
|
value: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemKeys {
|
export interface SystemKeys {
|
||||||
'smtp.user': string,
|
'smtp.user': string;
|
||||||
'smtp.from': string,
|
'smtp.from': string;
|
||||||
'smtp.pass': string,
|
'smtp.pass': string;
|
||||||
'smtp.host': string,
|
'smtp.host': string;
|
||||||
'smtp.port': number,
|
'smtp.port': number;
|
||||||
'smtp.secure': boolean,
|
'smtp.secure': boolean;
|
||||||
'installid': string,
|
installid: string;
|
||||||
'server.name': string,
|
'server.name': string;
|
||||||
'server.url': string,
|
'server.url': string;
|
||||||
'server.xff': string,
|
'server.xff': string;
|
||||||
'server.xhost': string,
|
'server.xhost': string;
|
||||||
'server.host': string,
|
'server.host': string;
|
||||||
'server.port': number,
|
'server.port': number;
|
||||||
'server.language': string,
|
'server.language': string;
|
||||||
'limit.problem_files_max': number,
|
'limit.problem_files_max': number;
|
||||||
'problem.categories': string,
|
'problem.categories': string;
|
||||||
'session.keys': string[],
|
'session.keys': string[];
|
||||||
'session.saved_expire_seconds': number,
|
'session.saved_expire_seconds': number;
|
||||||
'session.unsaved_expire_seconds': number,
|
'session.unsaved_expire_seconds': number;
|
||||||
'user.quota': number,
|
'user.quota': number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Setting {
|
export interface Setting {
|
||||||
family: string,
|
family: string;
|
||||||
key: string,
|
key: string;
|
||||||
range: [string, string][] | Record<string, string>,
|
range: [string, string][] | Record<string, string>;
|
||||||
value: any,
|
value: any;
|
||||||
type: string,
|
type: string;
|
||||||
subType?: string,
|
subType?: string;
|
||||||
name: string,
|
name: string;
|
||||||
desc: string,
|
desc: string;
|
||||||
flag: number,
|
flag: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Authenticator {
|
export interface Authenticator {
|
||||||
@@ -119,7 +120,7 @@ export interface UserPreferenceDoc {
|
|||||||
content: string;
|
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 User = import('./model/user').User;
|
||||||
export type Udict = Record<number, User>;
|
export type Udict = Record<number, User>;
|
||||||
@@ -190,11 +191,11 @@ export type { ProblemDoc } from './model/problem';
|
|||||||
export type ProblemDict = NumericDictionary<ProblemDoc>;
|
export type ProblemDict = NumericDictionary<ProblemDoc>;
|
||||||
|
|
||||||
export interface StatusDocBase {
|
export interface StatusDocBase {
|
||||||
_id: ObjectId,
|
_id: ObjectId;
|
||||||
docId: any,
|
docId: any;
|
||||||
docType: number,
|
docType: number;
|
||||||
domainId: string,
|
domainId: string;
|
||||||
uid: number,
|
uid: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProblemStatusDoc extends StatusDocBase {
|
export interface ProblemStatusDoc extends StatusDocBase {
|
||||||
@@ -241,10 +242,10 @@ export type ScoreboardRow = ScoreboardNode[] & { raw?: any };
|
|||||||
export type PenaltyRules = Dictionary<number>;
|
export type PenaltyRules = Dictionary<number>;
|
||||||
|
|
||||||
export interface TrainingNode {
|
export interface TrainingNode {
|
||||||
_id: number,
|
_id: number;
|
||||||
title: string,
|
title: string;
|
||||||
requireNids: number[],
|
requireNids: number[];
|
||||||
pids: number[],
|
pids: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Tdoc extends Document {
|
export interface Tdoc extends Document {
|
||||||
@@ -287,28 +288,28 @@ export interface Tdoc extends Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TrainingDoc extends Omit<Tdoc, 'docType'> {
|
export interface TrainingDoc extends Omit<Tdoc, 'docType'> {
|
||||||
docType: document['TYPE_TRAINING'],
|
docType: document['TYPE_TRAINING'];
|
||||||
description: string;
|
description: string;
|
||||||
pin?: number;
|
pin?: number;
|
||||||
dag: TrainingNode[];
|
dag: TrainingNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DomainDoc extends Record<string, any> {
|
export interface DomainDoc extends Record<string, any> {
|
||||||
_id: string,
|
_id: string;
|
||||||
owner: number,
|
owner: number;
|
||||||
roles: Dictionary<string>,
|
roles: Dictionary<string>;
|
||||||
avatar: string,
|
avatar: string;
|
||||||
bulletin: string,
|
bulletin: string;
|
||||||
_join?: any,
|
_join?: any;
|
||||||
host?: string[],
|
host?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
export interface MessageDoc {
|
export interface MessageDoc {
|
||||||
from: number,
|
from: number;
|
||||||
to: number,
|
to: number;
|
||||||
content: string,
|
content: string;
|
||||||
flag: number,
|
flag: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blacklist
|
// Blacklist
|
||||||
@@ -382,22 +383,22 @@ export interface ContestClarificationDoc extends Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenDoc {
|
export interface TokenDoc {
|
||||||
_id: string,
|
_id: string;
|
||||||
tokenType: number,
|
tokenType: number;
|
||||||
createAt: Date,
|
createAt: Date;
|
||||||
updateAt: Date,
|
updateAt: Date;
|
||||||
expireAt: Date,
|
expireAt: Date;
|
||||||
[key: string]: any,
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OplogDoc extends Record<string, any> {
|
export interface OplogDoc extends Record<string, any> {
|
||||||
_id: ObjectId,
|
_id: ObjectId;
|
||||||
type: string,
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContestStat extends Record<string, any> {
|
export interface ContestStat extends Record<string, any> {
|
||||||
detail: Record<number, Record<string, any>>,
|
detail: Record<number, Record<string, any>>;
|
||||||
unrank?: boolean,
|
unrank?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScoreboardConfig {
|
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 type ProblemImporter = (url: string, handler: any) => Promise<[ProblemDoc, fs.ReadStream?]> | [ProblemDoc, fs.ReadStream?];
|
||||||
|
|
||||||
export interface Script {
|
export interface Script {
|
||||||
run: (args: any, report: Function) => any,
|
run: (args: any, report: Function) => any;
|
||||||
description: string,
|
description: string;
|
||||||
validate: any,
|
validate: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Task {
|
export interface Task {
|
||||||
@@ -528,66 +529,66 @@ export interface LockDoc {
|
|||||||
|
|
||||||
declare module './service/db' {
|
declare module './service/db' {
|
||||||
interface Collections {
|
interface Collections {
|
||||||
'blacklist': BlacklistDoc;
|
blacklist: BlacklistDoc;
|
||||||
'domain': DomainDoc;
|
domain: DomainDoc;
|
||||||
'domain.user': any;
|
'domain.user': any;
|
||||||
'record': RecordDoc;
|
record: RecordDoc;
|
||||||
'record.stat': RecordStatDoc;
|
'record.stat': RecordStatDoc;
|
||||||
'record.history': RecordHistoryDoc;
|
'record.history': RecordHistoryDoc;
|
||||||
'document': any;
|
document: any;
|
||||||
'document.status': StatusDocBase & {
|
'document.status': StatusDocBase & {
|
||||||
[K in keyof DocStatusType]: { docType: K } & DocStatusType[K];
|
[K in keyof DocStatusType]: { docType: K } & DocStatusType[K];
|
||||||
}[keyof DocStatusType];
|
}[keyof DocStatusType];
|
||||||
'discussion.history': DiscussionHistoryDoc;
|
'discussion.history': DiscussionHistoryDoc;
|
||||||
'user': Udoc;
|
user: Udoc;
|
||||||
'user.preference': UserPreferenceDoc;
|
'user.preference': UserPreferenceDoc;
|
||||||
'vuser': VUdoc;
|
vuser: VUdoc;
|
||||||
'user.group': GDoc;
|
'user.group': GDoc;
|
||||||
'check': System;
|
check: System;
|
||||||
'message': MessageDoc;
|
message: MessageDoc;
|
||||||
'token': TokenDoc;
|
token: TokenDoc;
|
||||||
'status': any;
|
status: any;
|
||||||
'oauth': OauthMap;
|
oauth: OauthMap;
|
||||||
'system': System;
|
system: System;
|
||||||
'task': Task;
|
task: Task;
|
||||||
'storage': FileNode;
|
storage: FileNode;
|
||||||
'oplog': OplogDoc;
|
oplog: OplogDoc;
|
||||||
'event': EventDoc;
|
event: EventDoc;
|
||||||
'opcount': OpCountDoc;
|
opcount: OpCountDoc;
|
||||||
'schedule': Schedule;
|
schedule: Schedule;
|
||||||
'contest.balloon': ContestBalloonDoc;
|
'contest.balloon': ContestBalloonDoc;
|
||||||
'lock': LockDoc;
|
lock: LockDoc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Model {
|
export interface Model {
|
||||||
blacklist: typeof import('./model/blacklist').default,
|
blacklist: typeof import('./model/blacklist').default;
|
||||||
builtin: typeof import('./model/builtin'),
|
builtin: typeof import('./model/builtin');
|
||||||
contest: typeof import('./model/contest'),
|
contest: typeof import('./model/contest');
|
||||||
discussion: typeof import('./model/discussion'),
|
discussion: typeof import('./model/discussion');
|
||||||
document: Omit<typeof import('./model/document'), 'apply'>,
|
document: Omit<typeof import('./model/document'), 'apply'>;
|
||||||
domain: typeof import('./model/domain').default,
|
domain: typeof import('./model/domain').default;
|
||||||
message: typeof import('./model/message').default,
|
message: typeof import('./model/message').default;
|
||||||
opcount: typeof import('./model/opcount'),
|
opcount: typeof import('./model/opcount');
|
||||||
problem: typeof import('./model/problem').default,
|
problem: typeof import('./model/problem').default;
|
||||||
record: typeof import('./model/record').default,
|
record: typeof import('./model/record').default;
|
||||||
setting: typeof import('./model/setting'),
|
setting: typeof import('./model/setting');
|
||||||
solution: typeof import('./model/solution').default,
|
solution: typeof import('./model/solution').default;
|
||||||
system: typeof import('./model/system'),
|
system: typeof import('./model/system');
|
||||||
task: typeof import('./model/task').default,
|
task: typeof import('./model/task').default;
|
||||||
schedule: typeof import('./model/schedule').default;
|
schedule: typeof import('./model/schedule').default;
|
||||||
oplog: typeof import('./model/oplog'),
|
oplog: typeof import('./model/oplog');
|
||||||
token: typeof import('./model/token').default,
|
token: typeof import('./model/token').default;
|
||||||
training: typeof import('./model/training'),
|
training: typeof import('./model/training');
|
||||||
user: typeof import('./model/user').default,
|
user: typeof import('./model/user').default;
|
||||||
oauth: typeof import('./model/oauth').default,
|
oauth: typeof import('./model/oauth').default;
|
||||||
storage: typeof import('./model/storage').default,
|
storage: typeof import('./model/storage').default;
|
||||||
rp: typeof import('./script/rating').RpTypes,
|
rp: typeof import('./script/rating').RpTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GeoIP {
|
export interface GeoIP {
|
||||||
provider: string,
|
provider: string;
|
||||||
lookup: (ip: string, locale?: string) => any,
|
lookup: (ip: string, locale?: string) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProblemSearchResponse {
|
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 type UIInjectableFields = 'ProblemAdd' | 'Notification' | 'Nav' | 'UserDropdown' | 'DomainManage' | 'ControlPanel';
|
||||||
export interface UI {
|
export interface UI {
|
||||||
nodes: Record<UIInjectableFields, any[]>,
|
nodes: Record<UIInjectableFields, any[]>;
|
||||||
getNodes: typeof import('./lib/ui').getNodes,
|
getNodes: typeof import('./lib/ui').getNodes;
|
||||||
inject: typeof import('./lib/ui').inject,
|
inject: typeof import('./lib/ui').inject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModuleInterfaces {
|
export interface ModuleInterfaces {
|
||||||
@@ -629,8 +630,8 @@ export interface HydroGlobal {
|
|||||||
declare global {
|
declare global {
|
||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
interface Global {
|
interface Global {
|
||||||
Hydro: HydroGlobal,
|
Hydro: HydroGlobal;
|
||||||
addons: string[],
|
addons: string[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ type AvatarProvider = (src: string, size: number) => string;
|
|||||||
export const providers: Record<string, AvatarProvider> = {
|
export const providers: Record<string, AvatarProvider> = {
|
||||||
gravatar: (email, size) => `${global.Hydro.model.system.get('avatar.gravatar_url')}`
|
gravatar: (email, size) => `${global.Hydro.model.system.get('avatar.gravatar_url')}`
|
||||||
+ `${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${size || 32}`,
|
+ `${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)}`,
|
github: (id, size) => `//github.com/${id}.png?size=${Math.min(size, 460)}`,
|
||||||
url: (url) => url,
|
url: (url) => url,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
export interface ProblemSource {
|
export interface ProblemSource {
|
||||||
description?: string,
|
description?: string;
|
||||||
input?: string,
|
input?: string;
|
||||||
output?: string,
|
output?: string;
|
||||||
samples?: [string, string][],
|
samples?: [string, string][];
|
||||||
samplesRaw?: string,
|
samplesRaw?: string;
|
||||||
hint?: string,
|
hint?: string;
|
||||||
source?: string,
|
source?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildContent(source: ProblemSource, type: 'markdown' | 'html' = 'markdown', translate?: (s: string) => string) {
|
export function buildContent(source: ProblemSource, type: 'markdown' | 'html' = 'markdown', translate?: (s: string) => string) {
|
||||||
|
|||||||
@@ -91,14 +91,14 @@ class RatingCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface RatingInputUser {
|
interface RatingInputUser {
|
||||||
old: number,
|
old: number;
|
||||||
uid: number,
|
uid: number;
|
||||||
rank: number,
|
rank: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RatingOutputUser {
|
interface RatingOutputUser {
|
||||||
new: number,
|
new: number;
|
||||||
uid: number,
|
uid: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculate(users: RatingInputUser[]): RatingOutputUser[] {
|
function calculate(users: RatingInputUser[]): RatingOutputUser[] {
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { ObjectId as ObjectID } from 'mongodb';
|
|||||||
import Schema from 'schemastery';
|
import Schema from 'schemastery';
|
||||||
import superagent from 'superagent';
|
import superagent from 'superagent';
|
||||||
import { Context } from './context';
|
import { Context } from './context';
|
||||||
export { ObjectId, Filter } from 'mongodb';
|
|
||||||
export { WebSocket, WebSocketServer } from '@hydrooj/framework';
|
|
||||||
export * from './utils';
|
|
||||||
export * from './interface';
|
export * from './interface';
|
||||||
export * from './typeutils';
|
export * from './typeutils';
|
||||||
|
export * from './utils';
|
||||||
|
export { WebSocket, WebSocketServer } from '@hydrooj/framework';
|
||||||
|
export { Filter, ObjectId } from 'mongodb';
|
||||||
export {
|
export {
|
||||||
Schema, superagent, _, ObjectID, Zip,
|
_, ObjectID, Schema, superagent, Zip,
|
||||||
};
|
};
|
||||||
/** @deprecated Use ZipReader/ZipWriter instead */
|
/** @deprecated Use ZipReader/ZipWriter instead */
|
||||||
export const AdmZip = _AdmZip;
|
export const AdmZip = _AdmZip;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable import/no-dynamic-require */
|
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
/* eslint-disable simple-import-sort/imports */
|
/* eslint-disable simple-import-sort/imports */
|
||||||
import './init';
|
import './init';
|
||||||
@@ -181,7 +180,7 @@ export async function load() {
|
|||||||
if (process.env.DEV) {
|
if (process.env.DEV) {
|
||||||
const q = await simpleGit().listRemote(['--get-url']);
|
const q = await simpleGit().listRemote(['--get-url']);
|
||||||
if (!q.includes('hydro-dev/Hydro')) {
|
if (!q.includes('hydro-dev/Hydro')) {
|
||||||
console.warn('\x1b[93m');
|
console.warn('\x1B[93m');
|
||||||
console.warn('DISCLAIMER:');
|
console.warn('DISCLAIMER:');
|
||||||
console.warn(' You are under development mode.');
|
console.warn(' You are under development mode.');
|
||||||
console.warn(' The Hydro project is licensed under AGPL3,');
|
console.warn(' The Hydro project is licensed under AGPL3,');
|
||||||
@@ -195,7 +194,7 @@ export async function load() {
|
|||||||
console.warn(' 这意味着除非你获得了原作者的其他授权,');
|
console.warn(' 这意味着除非你获得了原作者的其他授权,');
|
||||||
console.warn(' 你需要同样以 AGPL3 协议开源所有的修改,');
|
console.warn(' 你需要同样以 AGPL3 协议开源所有的修改,');
|
||||||
console.warn(' 并保留所有的版权声明。');
|
console.warn(' 并保留所有的版权声明。');
|
||||||
console.warn('\x1b[39m');
|
console.warn('\x1B[39m');
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log('Hydro will start in 5s.');
|
console.log('Hydro will start in 5s.');
|
||||||
console.log('Hydro 将在五秒后继续启动。');
|
console.log('Hydro 将在五秒后继续启动。');
|
||||||
|
|||||||
@@ -641,7 +641,7 @@ const homework = buildContestRule({
|
|||||||
);
|
);
|
||||||
if (exceedSeconds < 0) return rate * jdoc.score;
|
if (exceedSeconds < 0) return rate * jdoc.score;
|
||||||
let coefficient = 1;
|
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) {
|
for (const i of keys) {
|
||||||
if (i * 3600 <= exceedSeconds) coefficient = tdoc.penaltyRules[i];
|
if (i * 3600 <= exceedSeconds) coefficient = tdoc.penaltyRules[i];
|
||||||
else break;
|
else break;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable object-curly-newline */
|
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import {
|
import {
|
||||||
Filter, FindCursor, ObjectId, OnlyFieldsOfType, PushOperator, UpdateFilter,
|
Filter, FindCursor, ObjectId, OnlyFieldsOfType, PushOperator, UpdateFilter,
|
||||||
@@ -43,9 +42,9 @@ export interface DocType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DocStatusType {
|
export interface DocStatusType {
|
||||||
[TYPE_PROBLEM]: ProblemStatusDoc,
|
[TYPE_PROBLEM]: ProblemStatusDoc;
|
||||||
// FIXME: this need to be typed
|
// 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']>(
|
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 });
|
const cache = new LRUCache<string, DomainDoc>({ max: 1000, ttl: 300 * 1000 });
|
||||||
|
|
||||||
interface DomainUserArg {
|
interface DomainUserArg {
|
||||||
_id: number,
|
_id: number;
|
||||||
priv: number,
|
priv: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DomainModel {
|
class DomainModel {
|
||||||
@@ -282,7 +282,7 @@ class DomainModel {
|
|||||||
|
|
||||||
@ArgMethod
|
@ArgMethod
|
||||||
static async getPrefixSearch(prefix: string, limit: number = 50) {
|
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({
|
const ddocs = await coll.find({
|
||||||
$or: [{ _id: { $regex } }, { name: { $regex } }],
|
$or: [{ _id: { $regex } }, { name: { $regex } }],
|
||||||
}).limit(limit).toArray();
|
}).limit(limit).toArray();
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable object-curly-newline */
|
|
||||||
import { pick, sum } from 'lodash';
|
import { pick, sum } from 'lodash';
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import {
|
import {
|
||||||
@@ -133,11 +132,11 @@ export default class RecordModel {
|
|||||||
domainId: string, pid: number, uid: number,
|
domainId: string, pid: number, uid: number,
|
||||||
lang: string, code: string, addTask: boolean,
|
lang: string, code: string, addTask: boolean,
|
||||||
args: {
|
args: {
|
||||||
contest?: ObjectId,
|
contest?: ObjectId;
|
||||||
input?: string,
|
input?: string;
|
||||||
files?: Record<string, string>,
|
files?: Record<string, string>;
|
||||||
hackTarget?: ObjectId,
|
hackTarget?: ObjectId;
|
||||||
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate',
|
type: 'judge' | 'rejudge' | 'pretest' | 'hack' | 'generate';
|
||||||
} = { type: 'judge' },
|
} = { type: 'judge' },
|
||||||
) {
|
) {
|
||||||
const data: RecordDoc = {
|
const data: RecordDoc = {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export const Setting = (
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Schemastery {
|
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;
|
family?: string;
|
||||||
secret?: boolean;
|
secret?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ export async function get(domainId: string, tid: ObjectId) {
|
|||||||
if (!tdoc) throw new TrainingNotFoundError(domainId, tid);
|
if (!tdoc) throw new TrainingNotFoundError(domainId, tid);
|
||||||
for (const i in tdoc.dag) {
|
for (const i in tdoc.dag) {
|
||||||
for (const j in tdoc.dag[i].pids) {
|
for (const j in tdoc.dag[i].pids) {
|
||||||
if (Number.isSafeInteger(parseInt(tdoc.dag[i].pids[j], 10))) {
|
if (Number.isSafeInteger(Number.parseInt(tdoc.dag[i].pids[j], 10))) {
|
||||||
tdoc.dag[i].pids[j] = 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 { LoginError, UserAlreadyExistError, UserNotFoundError } from '../error';
|
||||||
import {
|
import {
|
||||||
Authenticator, BaseUserDict, FileInfo, GDoc,
|
Authenticator, BaseUserDict, FileInfo, GDoc,
|
||||||
ownerInfo, Udict, Udoc, VUdoc,
|
OwnerInfo, Udict, Udoc, VUdoc,
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
import avatar from '../lib/avatar';
|
import avatar from '../lib/avatar';
|
||||||
import pwhash from '../lib/hash.hydro';
|
import pwhash from '../lib/hash.hydro';
|
||||||
@@ -116,9 +116,9 @@ export class User {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
own<T extends ownerInfo>(doc: T, checkPerm: bigint): 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, exact: boolean): boolean;
|
||||||
own<T extends ownerInfo>(doc: T): boolean;
|
own<T extends OwnerInfo>(doc: T): boolean;
|
||||||
own<T extends { owner: number, maintainer?: number[] }>(doc: T): boolean;
|
own<T extends { owner: number, maintainer?: number[] }>(doc: T): boolean;
|
||||||
own(doc: any, arg1: any = false): boolean {
|
own(doc: any, arg1: any = false): boolean {
|
||||||
if (typeof arg1 === 'bigint' && !this.hasPerm(arg1)) return false;
|
if (typeof arg1 === 'bigint' && !this.hasPerm(arg1)) return false;
|
||||||
@@ -143,7 +143,7 @@ export class User {
|
|||||||
throw new LoginError(this.uname);
|
throw new LoginError(this.uname);
|
||||||
}
|
}
|
||||||
if (this.hashType !== 'hydro') {
|
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);
|
UserModel.setPassword(this._id, password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export async function iterateAllPsdoc(filter: Filter<ProblemStatusDoc>, cb: (psd
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PartialProblemDoc extends ProblemDoc {
|
interface PartialProblemDoc extends ProblemDoc {
|
||||||
[key: string]: any,
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function iterateAllProblemInDomain(
|
export async function iterateAllProblemInDomain(
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
/* eslint-disable perfectionist/sort-exports */
|
||||||
import pwsh from './lib/hash.hydro';
|
import pwsh from './lib/hash.hydro';
|
||||||
import db from './service/db';
|
import db from './service/db';
|
||||||
|
|
||||||
export { nanoid } from 'nanoid';
|
export { nanoid } from 'nanoid';
|
||||||
export { default as moment, isMoment } from 'moment-timezone';
|
export { isMoment, default as moment } from 'moment-timezone';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
WebService, Router, HandlerCommon, httpServer,
|
Apis, APIS, HandlerCommon, httpServer,
|
||||||
Query, Mutation, Subscription, Apis, APIS,
|
Mutation, Query, Router, Subscription, WebService,
|
||||||
} from '@hydrooj/framework';
|
} from '@hydrooj/framework';
|
||||||
|
|
||||||
export * from './pipelineUtils';
|
export * from './pipelineUtils';
|
||||||
@@ -37,10 +38,10 @@ export { default as TaskModel } from './model/task';
|
|||||||
export * from './model/builtin';
|
export * from './model/builtin';
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
export * as JudgeHandler from './handler/judge';
|
export * as JudgeHandler from './handler/judge';
|
||||||
export { postJudge, JudgeResultCallbackContext } from './handler/judge';
|
export { JudgeResultCallbackContext, postJudge } from './handler/judge';
|
||||||
export { Collections } from './service/db';
|
export { Collections } from './service/db';
|
||||||
export { Handler, ConnectionHandler, requireSudo } from './service/server';
|
export { ConnectionHandler, Handler, requireSudo } from './service/server';
|
||||||
export { Service, Context } from './context';
|
export { Context, Service } from './context';
|
||||||
export { buildContent } from './lib/content';
|
export { buildContent } from './lib/content';
|
||||||
export { default as mime } from './lib/mime';
|
export { default as mime } from './lib/mime';
|
||||||
export { default as difficultyAlgorithm } from './lib/difficulty';
|
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 { ObjectId } from 'mongodb';
|
||||||
import Schema from 'schemastery';
|
import Schema from 'schemastery';
|
||||||
import blacklist from '../model/blacklist';
|
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 current = global.Hydro.version;
|
||||||
const message = [''];
|
const message = [''];
|
||||||
for (const name in current) {
|
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';
|
export const description = 'Calculate rp of a domain, or all domains';
|
||||||
|
|
||||||
type ND = NumericDictionary<number>;
|
type ND = NumericDictionary<number>;
|
||||||
|
type Report = (data: any) => void;
|
||||||
|
|
||||||
interface RpDef {
|
interface RpDef {
|
||||||
run(domainIds: string[], udict: ND, report: Function): Promise<void>;
|
run(domainIds: string[], udict: ND, report: Report): Promise<void>;
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
base: number;
|
base: number;
|
||||||
}
|
}
|
||||||
@@ -101,7 +102,7 @@ export const RpTypes: Record<string, RpDef> = {
|
|||||||
};
|
};
|
||||||
global.Hydro.model.rp = RpTypes;
|
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 });
|
await domain.setMultiUserInDomain(domainId, {}, { level: 0, rank: null });
|
||||||
let last = { rp: null };
|
let last = { rp: null };
|
||||||
let rank = 0;
|
let rank = 0;
|
||||||
@@ -135,7 +136,7 @@ export async function calcLevel(domainId: string, report: Function) {
|
|||||||
await bulk.execute();
|
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 results: Record<keyof typeof RpTypes, ND> = {};
|
||||||
const udict = Counter();
|
const udict = Counter();
|
||||||
await db.collection('domain.user').updateMany({ domainId }, { $set: { rpInfo: {} } });
|
await db.collection('domain.user').updateMany({ domainId }, { $set: { rpInfo: {} } });
|
||||||
@@ -160,7 +161,7 @@ async function runInDomain(domainId: string, report: Function) {
|
|||||||
await calcLevel(domainId, report);
|
await calcLevel(domainId, report);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function run({ domainId }, report: Function) {
|
export async function run({ domainId }, report: Report) {
|
||||||
if (!domainId) {
|
if (!domainId) {
|
||||||
const domains = await domain.getMulti().toArray();
|
const domains = await domain.getMulti().toArray();
|
||||||
await report({ message: `Found ${domains.length} domains` });
|
await report({ message: `Found ${domains.length} domains` });
|
||||||
|
|||||||
@@ -15,95 +15,95 @@ export type Disposable = () => void;
|
|||||||
export type VoidReturn = Promise<any> | any;
|
export type VoidReturn = Promise<any> | any;
|
||||||
|
|
||||||
export interface EventMap {
|
export interface EventMap {
|
||||||
'app/listen': () => void
|
'app/listen': () => void;
|
||||||
'app/started': () => void
|
'app/started': () => void;
|
||||||
'app/ready': () => VoidReturn
|
'app/ready': () => VoidReturn;
|
||||||
'app/exit': () => VoidReturn
|
'app/exit': () => VoidReturn;
|
||||||
'app/before-reload': (entries: Set<string>) => VoidReturn
|
'app/before-reload': (entries: Set<string>) => VoidReturn;
|
||||||
'app/reload': (entries: Set<string>) => VoidReturn
|
'app/reload': (entries: Set<string>) => VoidReturn;
|
||||||
|
|
||||||
'subscription/init': (h: ConnectionHandler<Context>, privileged: boolean) => VoidReturn
|
'subscription/init': (h: ConnectionHandler<Context>, privileged: boolean) => VoidReturn;
|
||||||
'subscription/subscribe': (channel: string, user: User, metadata: Record<string, string>) => VoidReturn
|
'subscription/subscribe': (channel: string, user: User, metadata: Record<string, string>) => VoidReturn;
|
||||||
'subscription/enable': (
|
'subscription/enable': (
|
||||||
channel: string, h: ConnectionHandler<Context>, privileged: boolean, onDispose: (disposable: () => void) => void
|
channel: string, h: ConnectionHandler<Context>, privileged: boolean, onDispose: (disposable: () => void) => void
|
||||||
) => VoidReturn
|
) => VoidReturn;
|
||||||
|
|
||||||
'app/watch/change': (path: string) => VoidReturn
|
'app/watch/change': (path: string) => VoidReturn;
|
||||||
'app/watch/unlink': (path: string) => VoidReturn
|
'app/watch/unlink': (path: string) => VoidReturn;
|
||||||
|
|
||||||
'database/connect': (db: Db) => void
|
'database/connect': (db: Db) => void;
|
||||||
'database/config': () => VoidReturn
|
'database/config': () => VoidReturn;
|
||||||
|
|
||||||
'system/setting': (args: Record<string, any>) => VoidReturn
|
'system/setting': (args: Record<string, any>) => VoidReturn;
|
||||||
'bus/broadcast': (event: keyof EventMap, payload: any, trace?: string) => VoidReturn
|
'bus/broadcast': (event: keyof EventMap, payload: any, trace?: string) => VoidReturn;
|
||||||
'monitor/update': (type: 'server' | 'judge', $set: any) => VoidReturn
|
'monitor/update': (type: 'server' | 'judge', $set: any) => VoidReturn;
|
||||||
'monitor/collect': (info: any) => VoidReturn
|
'monitor/collect': (info: any) => VoidReturn;
|
||||||
'api/update': () => void;
|
'api/update': () => void;
|
||||||
'task/daily': () => void;
|
'task/daily': () => void;
|
||||||
'task/daily/finish': (pref: Record<string, number>) => void;
|
'task/daily/finish': (pref: Record<string, number>) => void;
|
||||||
|
|
||||||
'user/message': (uid: number[], mdoc: Omit<MessageDoc, 'to'>) => void
|
'user/message': (uid: number[], mdoc: Omit<MessageDoc, 'to'>) => void;
|
||||||
'user/get': (udoc: User) => void
|
'user/get': (udoc: User) => void;
|
||||||
'user/delcache': (content: string | true) => void
|
'user/delcache': (content: string | true) => void;
|
||||||
|
|
||||||
'user/import/parse': (payload: any) => VoidReturn
|
'user/import/parse': (payload: any) => VoidReturn;
|
||||||
'user/import/create': (uid: number, udoc: any) => VoidReturn
|
'user/import/create': (uid: number, udoc: any) => VoidReturn;
|
||||||
|
|
||||||
'domain/create': (ddoc: DomainDoc) => VoidReturn
|
'domain/create': (ddoc: DomainDoc) => VoidReturn;
|
||||||
'domain/before-get': (query: Filter<DomainDoc>) => VoidReturn
|
'domain/before-get': (query: Filter<DomainDoc>) => VoidReturn;
|
||||||
'domain/get': (ddoc: DomainDoc) => VoidReturn
|
'domain/get': (ddoc: DomainDoc) => VoidReturn;
|
||||||
'domain/before-update': (domainId: string, $set: Partial<DomainDoc>) => VoidReturn
|
'domain/before-update': (domainId: string, $set: Partial<DomainDoc>) => VoidReturn;
|
||||||
'domain/update': (domainId: string, $set: Partial<DomainDoc>, ddoc: DomainDoc) => VoidReturn
|
'domain/update': (domainId: string, $set: Partial<DomainDoc>, ddoc: DomainDoc) => VoidReturn;
|
||||||
'domain/delete': (domainId: string) => VoidReturn
|
'domain/delete': (domainId: string) => VoidReturn;
|
||||||
'domain/delete-cache': (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>(
|
'document/set': <T extends keyof DocType>(
|
||||||
domainId: string, docType: T, docId: DocType[T],
|
domainId: string, docType: T, docId: DocType[T],
|
||||||
$set: any, $unset: OnlyFieldsOfType<DocType[T], any, true | '' | 1>
|
$set: any, $unset: OnlyFieldsOfType<DocType[T], any, true | '' | 1>
|
||||||
) => VoidReturn
|
) => VoidReturn;
|
||||||
|
|
||||||
'discussion/before-add': (payload: Partial<DiscussionDoc>) => VoidReturn
|
'discussion/before-add': (payload: Partial<DiscussionDoc>) => VoidReturn;
|
||||||
'discussion/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/before-add': (domainId: string, content: string, owner: number, docId: number, doc: Partial<ProblemDoc>) => VoidReturn;
|
||||||
'problem/add': (doc: Partial<ProblemDoc>, docId: number) => VoidReturn
|
'problem/add': (doc: Partial<ProblemDoc>, docId: number) => VoidReturn;
|
||||||
'problem/before-edit': (doc: Partial<ProblemDoc>, $unset: OnlyFieldsOfType<ProblemDoc, any, true | '' | 1>) => VoidReturn
|
'problem/before-edit': (doc: Partial<ProblemDoc>, $unset: OnlyFieldsOfType<ProblemDoc, any, true | '' | 1>) => VoidReturn;
|
||||||
'problem/edit': (doc: ProblemDoc) => VoidReturn
|
'problem/edit': (doc: ProblemDoc) => VoidReturn;
|
||||||
'problem/before-del': (domainId: string, docId: number) => VoidReturn
|
'problem/before-del': (domainId: string, docId: number) => VoidReturn;
|
||||||
'problem/del': (domainId: string, docId: number) => VoidReturn
|
'problem/del': (domainId: string, docId: number) => VoidReturn;
|
||||||
'problem/list': (query: Filter<ProblemDoc>, handler: any, sort?: string[]) => VoidReturn
|
'problem/list': (query: Filter<ProblemDoc>, handler: any, sort?: string[]) => VoidReturn;
|
||||||
'problem/get': (doc: ProblemDoc, handler: any) => VoidReturn
|
'problem/get': (doc: ProblemDoc, handler: any) => VoidReturn;
|
||||||
'problem/delete': (domainId: string, docId: number) => VoidReturn
|
'problem/delete': (domainId: string, docId: number) => VoidReturn;
|
||||||
'problem/addTestdata': (domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => 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/renameTestdata': (domainId: string, docId: number, name: string, newName: string) => VoidReturn;
|
||||||
'problem/delTestdata': (domainId: string, docId: number, name: 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/addAdditionalFile': (domainId: string, docId: number, name: string, payload: Omit<FileInfo, '_id'>) => VoidReturn;
|
||||||
'problem/renameAdditionalFile': (domainId: string, docId: number, name: string, newName: string) => VoidReturn
|
'problem/renameAdditionalFile': (domainId: string, docId: number, name: string, newName: string) => VoidReturn;
|
||||||
'problem/delAdditionalFile': (domainId: string, docId: number, name: string[]) => VoidReturn
|
'problem/delAdditionalFile': (domainId: string, docId: number, name: string[]) => VoidReturn;
|
||||||
|
|
||||||
'contest/before-add': (payload: Partial<Tdoc>) => VoidReturn
|
'contest/before-add': (payload: Partial<Tdoc>) => VoidReturn;
|
||||||
'contest/add': (payload: Partial<Tdoc>, id: ObjectId) => VoidReturn
|
'contest/add': (payload: Partial<Tdoc>, id: ObjectId) => VoidReturn;
|
||||||
'contest/edit': (payload: Tdoc) => VoidReturn
|
'contest/edit': (payload: Tdoc) => VoidReturn;
|
||||||
'contest/list': (query: Filter<Tdoc>, handler: any) => VoidReturn
|
'contest/list': (query: Filter<Tdoc>, handler: any) => VoidReturn;
|
||||||
'contest/scoreboard': (tdoc: Tdoc, rows: ScoreboardRow[], udict: BaseUserDict, pdict: ProblemDict) => VoidReturn
|
'contest/scoreboard': (tdoc: Tdoc, rows: ScoreboardRow[], udict: BaseUserDict, pdict: ProblemDict) => VoidReturn;
|
||||||
'contest/balloon': (domainId: string, tid: ObjectId, bdoc: ContestBalloonDoc) => VoidReturn
|
'contest/balloon': (domainId: string, tid: ObjectId, bdoc: ContestBalloonDoc) => VoidReturn;
|
||||||
'contest/del': (domainId: string, tid: ObjectId) => VoidReturn
|
'contest/del': (domainId: string, tid: ObjectId) => VoidReturn;
|
||||||
|
|
||||||
'oplog/log': (type: string, handler: Handler<Context> | ConnectionHandler<Context>, args: any, data: any) => VoidReturn;
|
'oplog/log': (type: string, handler: Handler<Context> | ConnectionHandler<Context>, args: any, data: any) => VoidReturn;
|
||||||
|
|
||||||
'training/list': (query: Filter<TrainingDoc>, handler: any) => VoidReturn
|
'training/list': (query: Filter<TrainingDoc>, handler: any) => VoidReturn;
|
||||||
'training/get': (tdoc: TrainingDoc, handler: any) => VoidReturn
|
'training/get': (tdoc: TrainingDoc, handler: any) => VoidReturn;
|
||||||
|
|
||||||
'record/change': (rdoc: RecordDoc, $set?: any, $push?: any, body?: any) => void
|
'record/change': (rdoc: RecordDoc, $set?: any, $push?: any, body?: any) => void;
|
||||||
'record/judge': (rdoc: RecordDoc, updated: boolean, pdoc?: ProblemDoc, updater?: any) => VoidReturn
|
'record/judge': (rdoc: RecordDoc, updated: boolean, pdoc?: ProblemDoc, updater?: any) => VoidReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apply(ctx: Context) {
|
export function apply(ctx: Context) {
|
||||||
try {
|
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) => {
|
pm2.launchBus((err, bus) => {
|
||||||
if (err) throw new Error();
|
if (err) throw new Error('cannot launch pm2 bus');
|
||||||
bus.on('hydro:broadcast', (packet) => {
|
bus.on('hydro:broadcast', (packet) => {
|
||||||
(app.parallel as any)(packet.data.event, ...BSON.EJSON.parse(packet.data.payload));
|
(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 { }
|
export interface Collections { }
|
||||||
|
|
||||||
interface MongoConfig {
|
interface MongoConfig {
|
||||||
protocol?: string,
|
protocol?: string;
|
||||||
username?: string,
|
username?: string;
|
||||||
password?: string,
|
password?: string;
|
||||||
host?: string,
|
host?: string;
|
||||||
port?: string,
|
port?: string;
|
||||||
name?: string,
|
name?: string;
|
||||||
url?: string,
|
url?: string;
|
||||||
uri?: string,
|
uri?: string;
|
||||||
prefix?: string,
|
prefix?: string;
|
||||||
collectionMap?: Record<string, string>,
|
collectionMap?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '../context' {
|
declare module '../context' {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// This file was adapted from @koishijs, MIT licensed.
|
// This file was adapted from @koishijs, MIT licensed.
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable consistent-return */
|
||||||
/* eslint-disable import/no-dynamic-require */
|
/* eslint-disable ts/no-shadow */
|
||||||
/* eslint-disable @typescript-eslint/no-shadow */
|
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { relative } from 'path';
|
import { relative } from 'path';
|
||||||
import { codeFrameColumns } from '@babel/code-frame';
|
import { codeFrameColumns } from '@babel/code-frame';
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const ignoredLimit = `,${argv.options.ignoredLimit},`;
|
|||||||
const logger = new Logger('server');
|
const logger = new Logger('server');
|
||||||
|
|
||||||
declare module '@hydrooj/framework' {
|
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;
|
domain: DomainDoc;
|
||||||
|
|
||||||
paginate<T>(cursor: FindCursor<T>, page: number, key: string): Promise<[docs: T[], numPages: number, count: number]>;
|
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/decorators';
|
||||||
export * from '@hydrooj/framework/validator';
|
export * from '@hydrooj/framework/validator';
|
||||||
export { Query, Mutation } from '@hydrooj/framework/api';
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For security concern, some API requires sudo privilege to access.
|
* 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))
|
? (!host.includes(this.request.host))
|
||||||
: this.request.host !== host)
|
: this.request.host !== host)
|
||||||
)) withDomainId ||= domainId;
|
)) 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 (anchor) res = `${res}#${anchor}`;
|
||||||
if (withDomainId) res = `/d/${withDomainId}${res}`;
|
if (withDomainId) res = `/d/${withDomainId}${res}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable consistent-return */
|
/* 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 no-await-in-loop */
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable ts/naming-convention */
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import { randomstring, sleep } from '@hydrooj/utils';
|
import { randomstring, sleep } from '@hydrooj/utils';
|
||||||
@@ -51,7 +51,7 @@ export const coreScripts: MigrationScript[] = [
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
// Init
|
// Init
|
||||||
...new Array(47).fill(unsupportedUpgrade), // oxlint-disable-line no-new-array
|
...Array.from({ length: 47 }).fill(unsupportedUpgrade) as any,
|
||||||
async function _48_49() {
|
async function _48_49() {
|
||||||
await RecordModel.coll.updateMany({ input: { $exists: true } }, { $set: { contest: new ObjectId('000000000000000000000000') } });
|
await RecordModel.coll.updateMany({ input: { $exists: true } }, { $set: { contest: new ObjectId('000000000000000000000000') } });
|
||||||
return true;
|
return true;
|
||||||
@@ -221,7 +221,7 @@ export const coreScripts: MigrationScript[] = [
|
|||||||
config.answers[cnt] = l;
|
config.answers[cnt] = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
function processSingleLanguage(content: string) { // eslint-disable-line no-inner-declarations
|
function processSingleLanguage(content: string) {
|
||||||
let text = '';
|
let text = '';
|
||||||
try {
|
try {
|
||||||
let scnt = 0;
|
let scnt = 0;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
|
||||||
function unescapedString(escapedString: string) {
|
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, '/');
|
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 */
|
/* eslint-disable no-await-in-loop */
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import mariadb from 'mariadb';
|
import mariadb from 'mariadb';
|
||||||
@@ -70,7 +70,7 @@ export async function run({
|
|||||||
host = 'localhost', port = 3306, name = 'jol',
|
host = 'localhost', port = 3306, name = 'jol',
|
||||||
username, password, domainId, contestType = 'oi',
|
username, password, domainId, contestType = 'oi',
|
||||||
dataDir, uploadDir = '/home/judge/src/web/upload/', rerun = true, randomMail = false,
|
dataDir, uploadDir = '/home/judge/src/web/upload/', rerun = true, randomMail = false,
|
||||||
}, report: Function) {
|
}, report: (data: any) => void) {
|
||||||
let remoteUsed = false;
|
let remoteUsed = false;
|
||||||
const src = await mariadb.createConnection({
|
const src = await mariadb.createConnection({
|
||||||
host,
|
host,
|
||||||
@@ -191,7 +191,7 @@ export async function run({
|
|||||||
samples: [[pdoc.sample_input.trim(), pdoc.sample_output.trim()]],
|
samples: [[pdoc.sample_input.trim(), pdoc.sample_output.trim()]],
|
||||||
hint: pdoc.hint,
|
hint: pdoc.hint,
|
||||||
source: pdoc.source,
|
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);
|
const uploadFiles = content.matchAll(/(?:src|href)="\/upload\/([^"/]+)(?:\/([^"/]+))?\/([^"/]+\.[^"/.]+)"/g);
|
||||||
for (const file of uploadFiles) {
|
for (const file of uploadFiles) {
|
||||||
try {
|
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])));
|
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]}` });
|
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);
|
const assignUserList = allowedUser.map((i) => uidMap[i.user_id]).filter((i) => i);
|
||||||
if (isAssignMode) {
|
if (isAssignMode) {
|
||||||
await ContestModel.edit(domainId, tid, {
|
await ContestModel.edit(domainId, tid, {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable no-tabs */
|
|
||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
|
/* eslint-disable style/no-tabs */
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
import mariadb from 'mariadb';
|
import mariadb from 'mariadb';
|
||||||
@@ -34,7 +35,7 @@ export async function run({
|
|||||||
host = 'localhost', port = 3306, name,
|
host = 'localhost', port = 3306, name,
|
||||||
username, password, domainId, contestType = 'oi',
|
username, password, domainId, contestType = 'oi',
|
||||||
dataDir, imageDir, rerun = true, randomMail = false,
|
dataDir, imageDir, rerun = true, randomMail = false,
|
||||||
}, report: Function) {
|
}, report: (data: any) => void) {
|
||||||
const src = await mariadb.createConnection({
|
const src = await mariadb.createConnection({
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
@@ -150,6 +151,7 @@ export async function run({
|
|||||||
hint: pdoc.hint,
|
hint: pdoc.hint,
|
||||||
source: pdoc.source,
|
source: pdoc.source,
|
||||||
}, 'html');
|
}, 'html');
|
||||||
|
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
||||||
const uploadFiles = content.matchAll(/(?:src|href)="\/images\/([^"]+\/([^"]+))"/g);
|
const uploadFiles = content.matchAll(/(?:src|href)="\/images\/([^"]+\/([^"]+))"/g);
|
||||||
for (const file of uploadFiles) {
|
for (const file of uploadFiles) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export async function run({
|
|||||||
host = 'localhost', port = 3306, name = 'syzoj',
|
host = 'localhost', port = 3306, name = 'syzoj',
|
||||||
username, password, domainId, dataDir,
|
username, password, domainId, dataDir,
|
||||||
rerun = true, randomMail = 'never',
|
rerun = true, randomMail = 'never',
|
||||||
}, report: Function) {
|
}, report: (data: any) => void) {
|
||||||
const src = await mariadb.createConnection({
|
const src = await mariadb.createConnection({
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
@@ -199,7 +199,7 @@ export async function run({
|
|||||||
`publicize_time` datetime NULL DEFAULT NULL, 公开时间
|
`publicize_time` datetime NULL DEFAULT NULL, 公开时间
|
||||||
`type` enum('traditional','submit-answer','interaction') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'traditional',
|
`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 pidMap: Record<string, number> = {};
|
||||||
const configMap: Record<string, string> = {};
|
const configMap: Record<string, string> = {};
|
||||||
const problemAdditionalFile = {};
|
const problemAdditionalFile = {};
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export async function run({
|
|||||||
host = '172.17.0.2', port = 3306, name = 'app_uoj233',
|
host = '172.17.0.2', port = 3306, name = 'app_uoj233',
|
||||||
username, password, domainId, dataDir,
|
username, password, domainId, dataDir,
|
||||||
rerun = true, randomMail = false,
|
rerun = true, randomMail = false,
|
||||||
}, report: Function) {
|
}, report: (data: any) => void) {
|
||||||
const src = await mariadb.createConnection({
|
const src = await mariadb.createConnection({
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
@@ -506,10 +506,10 @@ export async function run({
|
|||||||
if (confInfo.n_subtasks
|
if (confInfo.n_subtasks
|
||||||
&& Object.keys(confInfo.subtask_end).length === +confInfo.n_subtasks
|
&& Object.keys(confInfo.subtask_end).length === +confInfo.n_subtasks
|
||||||
&& Object.keys(confInfo.subtask_score).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,
|
id: +i,
|
||||||
score: confInfo.subtask_score[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}`,
|
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}`,
|
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,
|
id: 1,
|
||||||
score: 97,
|
score: 97,
|
||||||
type: 'sum' as SubtaskType,
|
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}`,
|
input: `${confInfo.input_pre}${i}.${confInfo.input_suf}`,
|
||||||
output: `${confInfo.output_pre}${i}.${confInfo.output_suf}`,
|
output: `${confInfo.output_pre}${i}.${confInfo.output_suf}`,
|
||||||
})),
|
})),
|
||||||
@@ -530,7 +530,7 @@ export async function run({
|
|||||||
config.subtasks.push({
|
config.subtasks.push({
|
||||||
id: Math.max(...config.subtasks.map((i) => i.id)) + 1,
|
id: Math.max(...config.subtasks.map((i) => i.id)) + 1,
|
||||||
score: 3,
|
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}`,
|
input: `ex_${confInfo.input_pre}${i}.${confInfo.input_suf}`,
|
||||||
output: `ex_${confInfo.output_pre}${i}.${confInfo.output_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 */
|
/* eslint-disable no-await-in-loop */
|
||||||
import mongodb, { Db, FindCursor } from 'mongodb';
|
import mongodb, { Db, FindCursor } from 'mongodb';
|
||||||
import {
|
import {
|
||||||
@@ -13,6 +13,8 @@ const pid = (id) => {
|
|||||||
return id;
|
return id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Report = (args: { progress?: number, message?: string }) => void;
|
||||||
|
|
||||||
const tasks = {
|
const tasks = {
|
||||||
user: async (doc) => ({
|
user: async (doc) => ({
|
||||||
_id: doc._id,
|
_id: doc._id,
|
||||||
@@ -93,7 +95,7 @@ const tasks = {
|
|||||||
processer: (rule) => {
|
processer: (rule) => {
|
||||||
const n = {};
|
const n = {};
|
||||||
for (const key in rule) {
|
for (const key in rule) {
|
||||||
n[parseInt(key, 10) / 3600] = rule;
|
n[Number.parseInt(key, 10) / 3600] = rule;
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
},
|
},
|
||||||
@@ -282,7 +284,7 @@ const cursor: NodeJS.Dict<CursorGetter> = {
|
|||||||
file: (s) => s.collection('fs.files').find(),
|
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 });
|
const count = await src.collection('document').countDocuments({ doc_type: 20 });
|
||||||
await report({ progress: 1, message: `discussion.node: ${count}` });
|
await report({ progress: 1, message: `discussion.node: ${count}` });
|
||||||
const total = Math.floor(count / 5);
|
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 });
|
const count = await dst.collection('document').countDocuments({ docType: 10 });
|
||||||
await report({ progress: 1, message: `Fix pid: ${count}` });
|
await report({ progress: 1, message: `Fix pid: ${count}` });
|
||||||
const total = Math.floor(count / 50);
|
const total = Math.floor(count / 50);
|
||||||
@@ -349,7 +351,7 @@ function objid(ts: Date) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME this seems not working
|
// 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();
|
const count = await src.collection('message').countDocuments();
|
||||||
await report({ progress: 1, message: `Messages: ${count}` });
|
await report({ progress: 1, message: `Messages: ${count}` });
|
||||||
const total = Math.floor(count / 50);
|
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 count = await dst.collection('document').countDocuments({ docType: 10 });
|
||||||
const bulk = dst.collection('document').initializeUnorderedBulkOp();
|
const bulk = dst.collection('document').initializeUnorderedBulkOp();
|
||||||
await report({ progress: 1, message: `Remove pid: ${count}` });
|
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)
|
.find({ docType: 10 }).skip(i * 50).limit(50)
|
||||||
.toArray();
|
.toArray();
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
const id = parseInt(doc.pid, 10);
|
const id = Number.parseInt(doc.pid, 10);
|
||||||
if (Number.isSafeInteger(id)) {
|
if (Number.isSafeInteger(id)) {
|
||||||
bulk.find({ _id: doc._id }).updateOne({ $unset: { pid: '' } });
|
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}` });
|
await report({ progress: 1, message: `${name}` });
|
||||||
let lastProgress = -1;
|
let lastProgress = -1;
|
||||||
for (let i = 0; ; i++) {
|
for (let i = 0; ; i++) {
|
||||||
@@ -466,7 +468,7 @@ async function task(name: any, src: Db, report: Function) {
|
|||||||
|
|
||||||
export async function run({
|
export async function run({
|
||||||
host = 'localhost', port = 27017, name = 'vijos4', username, password,
|
host = 'localhost', port = 27017, name = 'vijos4', username, password,
|
||||||
}, report: Function) {
|
}, report: Report) {
|
||||||
let mongourl = 'mongodb://';
|
let mongourl = 'mongodb://';
|
||||||
if (username) mongourl += `${username}:${password}@`;
|
if (username) mongourl += `${username}:${password}@`;
|
||||||
mongourl += `${host}:${port}/${name}`;
|
mongourl += `${host}:${port}/${name}`;
|
||||||
|
|||||||
@@ -139,25 +139,25 @@ export function apply(ctx: Context) {
|
|||||||
solved: +i === STATUS.STATUS_ACCEPTED,
|
solved: +i === STATUS.STATUS_ACCEPTED,
|
||||||
})),
|
})),
|
||||||
getFeed('languages', {
|
getFeed('languages', {
|
||||||
id: 'c', name: 'C', 'entry_point_required': false, extensions: ['c'],
|
id: 'c', name: 'C', entry_point_required: false, extensions: ['c'],
|
||||||
}),
|
}),
|
||||||
getFeed('languages', {
|
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', {
|
getFeed('languages', {
|
||||||
id: 'java', name: 'Java', 'entry_point_required': true, extensions: ['java'],
|
id: 'java', name: 'Java', entry_point_required: true, extensions: ['java'],
|
||||||
}),
|
}),
|
||||||
getFeed('languages', {
|
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', {
|
getFeed('languages', {
|
||||||
id: 'kotlin', name: 'Kotlin', 'entry_point_required': true, extensions: ['kt'],
|
id: 'kotlin', name: 'Kotlin', entry_point_required: true, extensions: ['kt'],
|
||||||
}),
|
}),
|
||||||
getFeed('languages', {
|
getFeed('languages', {
|
||||||
id: 'rust', name: 'Rust', 'entry_point_required': true, extensions: ['rs'],
|
id: 'rust', name: 'Rust', entry_point_required: true, extensions: ['rs'],
|
||||||
}),
|
}),
|
||||||
getFeed('languages', {
|
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: 'participants', name: '正式队伍' }),
|
||||||
getFeed('groups', { id: 'observers', name: '打星队伍' }),
|
getFeed('groups', { id: 'observers', name: '打星队伍' }),
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
|
||||||
const file = fs.readFileSync(path.join(__dirname, 'public/assets/board.html'), 'utf8');
|
const file = fs.readFileSync(path.join(__dirname, 'public/assets/board.html'), 'utf8');
|
||||||
const indexJs = file.match(/index-([A-Za-z0-9_-]+)\.js"/)?.[1];
|
const indexJs = file.match(/index-([\w-]+)\.js"/)?.[1];
|
||||||
const indexCss = file.match(/index-([A-Za-z0-9_-]+)\.css"/)?.[1];
|
const indexCss = file.match(/index-([\w-]+)\.css"/)?.[1];
|
||||||
const status = {
|
const status = {
|
||||||
[STATUS.STATUS_WRONG_ANSWER]: 'WRONG_ANSWER',
|
[STATUS.STATUS_WRONG_ANSWER]: 'WRONG_ANSWER',
|
||||||
[STATUS.STATUS_ACCEPTED]: 'CORRECT',
|
[STATUS.STATUS_ACCEPTED]: 'CORRECT',
|
||||||
@@ -72,9 +72,9 @@ export async function apply(ctx: Context) {
|
|||||||
},
|
},
|
||||||
organization: 'School',
|
organization: 'School',
|
||||||
status_time_display: {
|
status_time_display: {
|
||||||
'correct': true,
|
correct: true,
|
||||||
'incorrect': true,
|
incorrect: true,
|
||||||
'pending': true,
|
pending: true,
|
||||||
},
|
},
|
||||||
medal: {
|
medal: {
|
||||||
official: {
|
official: {
|
||||||
|
|||||||
+10
-10
@@ -1,23 +1,23 @@
|
|||||||
/* eslint-disable import/first */
|
/* eslint-disable import/first */
|
||||||
export * from './utils';
|
|
||||||
export { default as Notification } from './components/notification';
|
|
||||||
export * from './components/dialog';
|
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 loadMonaco } from './components/monaco/loader';
|
||||||
|
export { default as Notification } from './components/notification';
|
||||||
export { default as Socket } from './components/socket/index';
|
export { default as Socket } from './components/socket/index';
|
||||||
export { default as uploadFiles } from './components/upload';
|
export { default as uploadFiles } from './components/upload';
|
||||||
export * as redux from 'react-redux';
|
|
||||||
export * from './components/zipDownloader';
|
export * from './components/zipDownloader';
|
||||||
export * from '@hydrooj/utils/lib/common';
|
|
||||||
export { default as download } from './components/zipDownloader';
|
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 'jquery';
|
||||||
export { default as _ } from 'lodash';
|
export { default as _ } from 'lodash';
|
||||||
export { default as React } from 'react';
|
export { default as React } from 'react';
|
||||||
export { default as ReactDOM } from 'react-dom/client';
|
export { default as ReactDOM } from 'react-dom/client';
|
||||||
export * from './misc/Page';
|
export * as redux from 'react-redux';
|
||||||
export { initPageLoader } from './hydro';
|
|
||||||
export * from './lazyload';
|
|
||||||
import { load } from './lazyload';
|
import { load } from './lazyload';
|
||||||
|
|
||||||
export default load;
|
export default load;
|
||||||
@@ -30,7 +30,7 @@ import ProblemSelectAutoComplete from './components/autocomplete/ProblemSelectAu
|
|||||||
import UserSelectAutoComplete from './components/autocomplete/UserSelectAutoComplete';
|
import UserSelectAutoComplete from './components/autocomplete/UserSelectAutoComplete';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AutoComplete, UserSelectAutoComplete, ProblemSelectAutoComplete, DomainSelectAutoComplete, CustomSelectAutoComplete,
|
AutoComplete, CustomSelectAutoComplete, DomainSelectAutoComplete, ProblemSelectAutoComplete, UserSelectAutoComplete,
|
||||||
};
|
};
|
||||||
export function addPage(page: import('./misc/Page').Page | (() => Promise<void> | void)) {
|
export function addPage(page: import('./misc/Page').Page | (() => Promise<void> | void)) {
|
||||||
window.Hydro.extraPages.push(page);
|
window.Hydro.extraPages.push(page);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import esbuild from 'esbuild';
|
|||||||
|
|
||||||
declare module 'hydrooj' {
|
declare module 'hydrooj' {
|
||||||
interface UI {
|
interface UI {
|
||||||
esbuildPlugins?: esbuild.Plugin[]
|
esbuildPlugins?: esbuild.Plugin[];
|
||||||
}
|
}
|
||||||
interface SystemKeys {
|
interface SystemKeys {
|
||||||
'ui-default.nav_logo_dark': string;
|
'ui-default.nav_logo_dark': string;
|
||||||
@@ -91,7 +91,7 @@ export async function buildUI() {
|
|||||||
const entryPoints: string[] = [];
|
const entryPoints: string[] = [];
|
||||||
const lazyModules: string[] = [];
|
const lazyModules: string[] = [];
|
||||||
const newFiles = ['entry.js'];
|
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');
|
let publicPath = resolve(addon, 'frontend');
|
||||||
if (!fs.existsSync(publicPath)) publicPath = resolve(addon, 'public');
|
if (!fs.existsSync(publicPath)) publicPath = resolve(addon, 'public');
|
||||||
if (!fs.existsSync(publicPath)) continue;
|
if (!fs.existsSync(publicPath)) continue;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ function parseNextNumber(str, pos, max) {
|
|||||||
value: '',
|
value: '',
|
||||||
};
|
};
|
||||||
let code = str.charCodeAt(pos);
|
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);
|
code = str.charCodeAt(++pos);
|
||||||
}
|
}
|
||||||
result.ok = true;
|
result.ok = true;
|
||||||
@@ -24,9 +24,9 @@ function parseImageSize(str, pos, max) {
|
|||||||
width: '',
|
width: '',
|
||||||
height: '',
|
height: '',
|
||||||
};
|
};
|
||||||
if (pos >= max) { return result; }
|
if (pos >= max) return result;
|
||||||
let code = str.charCodeAt(pos);
|
let code = str.charCodeAt(pos);
|
||||||
if (code !== 0x3d /* = */) { return result; }
|
if (code !== 0x3D /* = */) return result;
|
||||||
pos++;
|
pos++;
|
||||||
code = str.charCodeAt(pos);
|
code = str.charCodeAt(pos);
|
||||||
if (code !== 0x78 /* x */ && (code < 0x30 || code > 0x39) /* [0-9] */) {
|
if (code !== 0x78 /* x */ && (code < 0x30 || code > 0x39) /* [0-9] */) {
|
||||||
@@ -67,14 +67,14 @@ export default function plugin(md) {
|
|||||||
const oldPos = state.pos;
|
const oldPos = state.pos;
|
||||||
const max = state.posMax;
|
const max = state.posMax;
|
||||||
|
|
||||||
if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; }
|
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 + 1) !== 0x5B/* [ */) return false;
|
||||||
|
|
||||||
const labelStart = state.pos + 2;
|
const labelStart = state.pos + 2;
|
||||||
const labelEnd = md.helpers.parseLinkLabel(state, state.pos + 1, false);
|
const labelEnd = md.helpers.parseLinkLabel(state, state.pos + 1, false);
|
||||||
|
|
||||||
// parser failed to find ']', so it's not a valid link
|
// parser failed to find ']', so it's not a valid link
|
||||||
if (labelEnd < 0) { return false; }
|
if (labelEnd < 0) return false;
|
||||||
|
|
||||||
pos = labelEnd + 1;
|
pos = labelEnd + 1;
|
||||||
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
|
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
|
||||||
@@ -89,7 +89,7 @@ export default function plugin(md) {
|
|||||||
code = state.src.charCodeAt(pos);
|
code = state.src.charCodeAt(pos);
|
||||||
if (code !== 0x20 && code !== 0x0A) { break; }
|
if (code !== 0x20 && code !== 0x0A) { break; }
|
||||||
}
|
}
|
||||||
if (pos >= max) { return false; }
|
if (pos >= max) return false;
|
||||||
|
|
||||||
// [link]( <href> "title" )
|
// [link]( <href> "title" )
|
||||||
// ^^^^^^ parsing link destination
|
// ^^^^^^ parsing link destination
|
||||||
@@ -162,7 +162,7 @@ export default function plugin(md) {
|
|||||||
//
|
//
|
||||||
// Link reference
|
// Link reference
|
||||||
//
|
//
|
||||||
if (typeof state.env.references === 'undefined') { return false; }
|
if (typeof state.env.references === 'undefined') return false;
|
||||||
|
|
||||||
// [foo] [bar]
|
// [foo] [bar]
|
||||||
// ^^ optional whitespace (can include newlines)
|
// ^^ optional whitespace (can include newlines)
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export default function plugin(md) {
|
|||||||
const render = (latex: string, displayMode = false) => {
|
const render = (latex: string, displayMode = false) => {
|
||||||
options.displayMode = displayMode;
|
options.displayMode = displayMode;
|
||||||
try {
|
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);
|
return katex.renderToString(latex, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (options.throwOnError) console.error(error);
|
if (options.throwOnError) console.error(error);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
/* eslint-disable max-len */
|
/* 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 type MarkdownIt from 'markdown-it';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
@@ -10,6 +13,7 @@ function youtubeParser(url: string) {
|
|||||||
const match = url.match(ytRegex);
|
const match = url.match(ytRegex);
|
||||||
return match && match[7].length === 11 ? match[7] : url;
|
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+)(?:$|\/|\?)/;
|
const vimeoRegex = /https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|)(\d+)(?:$|\/|\?)/;
|
||||||
function vimeoParser(url: string) {
|
function vimeoParser(url: string) {
|
||||||
const match = url.match(vimeoRegex);
|
const match = url.match(vimeoRegex);
|
||||||
@@ -25,7 +29,7 @@ function preziParser(url: string) {
|
|||||||
const match = url.match(preziRegex);
|
const match = url.match(preziRegex);
|
||||||
return match ? match[1] : url;
|
return match ? match[1] : url;
|
||||||
}
|
}
|
||||||
const EMBED_REGEX = /@\[([a-zA-Z].+?)]\((.*?)[)]/im;
|
const EMBED_REGEX = /@\[([a-zA-Z].+?)\]\((.*?)\)/;
|
||||||
function extractVideoParameters(url: string) {
|
function extractVideoParameters(url: string) {
|
||||||
const parameterMap = new Map();
|
const parameterMap = new Map();
|
||||||
const params = url.replace(/&/gi, '&').split(/[#?&]/);
|
const params = url.replace(/&/gi, '&').split(/[#?&]/);
|
||||||
@@ -83,7 +87,7 @@ declare module 'hydrooj' {
|
|||||||
interface ModuleInterfaces {
|
interface ModuleInterfaces {
|
||||||
richmedia: {
|
richmedia: {
|
||||||
get: (service: string, src: string, md: MarkdownIt) => string | null;
|
get: (service: string, src: string, md: MarkdownIt) => string | null;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const depedentTags = {
|
|||||||
dt: ['dl'],
|
dt: ['dl'],
|
||||||
dd: ['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({
|
const tagCheck = new FilterXSS({
|
||||||
css: false,
|
css: false,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { moment, ObjectId } from 'hydrooj';
|
import { moment, ObjectId } from 'hydrooj';
|
||||||
import { AnsiUp } from 'ansi_up';
|
import { AnsiUp } from 'ansi_up';
|
||||||
|
|
||||||
export { size, formatSeconds } from 'hydrooj';
|
export { formatSeconds, size } from 'hydrooj';
|
||||||
|
|
||||||
const AU = new AnsiUp();
|
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)));
|
files.push(...await getFiles(path.join(folder, i), path.join(base, i)));
|
||||||
} else files.push(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[]) {
|
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 (obj === undefined || obj === null) return undefined;
|
||||||
if (typeof obj[val] === 'function') {
|
if (typeof obj[val] === 'function') {
|
||||||
const fn = function (...args) {
|
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];
|
fn._original = obj[val];
|
||||||
return fn;
|
return fn;
|
||||||
@@ -255,7 +255,7 @@ export class TemplateService extends Service {
|
|||||||
const pending = Object.values(global.addons);
|
const pending = Object.values(global.addons);
|
||||||
const logger = this.ctx.logger('template');
|
const logger = this.ctx.logger('template');
|
||||||
for (const i of pending) {
|
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()) {
|
if (p && (await fs.stat(p)).isDirectory()) {
|
||||||
try {
|
try {
|
||||||
const files = await getFiles(p);
|
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,
|
devtool: env.production ? 'source-map' : false,
|
||||||
entry: {
|
entry: {
|
||||||
[`hydro-${version}`]: './entry.js',
|
[`hydro-${version}`]: './entry.js',
|
||||||
'sentry': './sentry.ts',
|
sentry: './sentry.ts',
|
||||||
'default.theme': './theme/default.js',
|
'default.theme': './theme/default.js',
|
||||||
'service-worker': './service-worker.ts',
|
'service-worker': './service-worker.ts',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable import/no-import-module-exports */
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { size } from '@hydrooj/utils/lib/utils';
|
import { size } from '@hydrooj/utils/lib/utils';
|
||||||
import cac from 'cac';
|
import cac from 'cac';
|
||||||
@@ -106,7 +105,7 @@ async function main() {
|
|||||||
classNamePrefix: 'icon',
|
classNamePrefix: 'icon',
|
||||||
fontName: 'hydro-icons',
|
fontName: 'hydro-icons',
|
||||||
css: true,
|
css: true,
|
||||||
startUnicode: 0xea01,
|
startUnicode: 0xEA01,
|
||||||
svg2ttf: {
|
svg2ttf: {
|
||||||
timestamp: 1577836800, // 2020-1-1
|
timestamp: 1577836800, // 2020-1-1
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ export default class DOMAttachedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor($dom: JQuery<HTMLElement>, monitorDetach = false) {
|
constructor($dom: JQuery<HTMLElement>, monitorDetach = false) {
|
||||||
// eslint-disable-next-line no-constructor-return
|
|
||||||
if ($dom == null) return null;
|
if ($dom == null) return null;
|
||||||
this.$dom = $dom;
|
this.$dom = $dom;
|
||||||
this.id = ++DOMAttachedObject.uniqueIdCounter;
|
this.id = ++DOMAttachedObject.uniqueIdCounter;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { CustomSelectAutoComplete as CustomSelectAutoCompleteFC } from '@hydrooj
|
|||||||
import AutoComplete, { AutoCompleteOptions } from '.';
|
import AutoComplete, { AutoCompleteOptions } from '.';
|
||||||
|
|
||||||
interface CustomSelectOptions {
|
interface CustomSelectOptions {
|
||||||
data: any[]
|
data: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CustomSelectAutoComplete<Multi extends boolean> extends AutoComplete<CustomSelectOptions> {
|
export default class CustomSelectAutoComplete<Multi extends boolean> extends AutoComplete<CustomSelectOptions> {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface AutoCompleteOptions<Multi extends boolean = boolean> {
|
|||||||
text?: () => string;
|
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';
|
static DOMAttachKey = 'ucwAutoCompleteInstance';
|
||||||
ref = null;
|
ref = null;
|
||||||
container = document.createElement('div');
|
container = document.createElement('div');
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ export default class Calendar {
|
|||||||
const daysByWeek = _.chunk(days, 7);
|
const daysByWeek = _.chunk(days, 7);
|
||||||
|
|
||||||
const numberOfWeeks = days.length / 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 beginDate = days[0].date.clone();
|
||||||
const endDate = _.last(days).date.clone();
|
const endDate = _.last(days).date.clone();
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ export default class Calendar {
|
|||||||
]))
|
]))
|
||||||
.map((banners) => {
|
.map((banners) => {
|
||||||
const dayBitmap = _
|
const dayBitmap = _
|
||||||
.fill(new Array(7), 1)
|
.fill(Array.from({ length: 7 }), 1)
|
||||||
.map(() => []);
|
.map(() => []);
|
||||||
banners.forEach((banner) => {
|
banners.forEach((banner) => {
|
||||||
const beginDay = banner.beginAt.day();
|
const beginDay = banner.beginAt.day();
|
||||||
@@ -292,7 +292,7 @@ export default class Calendar {
|
|||||||
// merge adjacent cells and arrange banners by vertical index
|
// merge adjacent cells and arrange banners by vertical index
|
||||||
const vMaxLength = _.max(_.range(0, 7).map((day) => dayBitmap[day].length));
|
const vMaxLength = _.max(_.range(0, 7).map((day) => dayBitmap[day].length));
|
||||||
const weekBanners = _
|
const weekBanners = _
|
||||||
.fill(new Array(vMaxLength), 1)
|
.fill(Array.from({ length: vMaxLength }), 1)
|
||||||
.map(() => []);
|
.map(() => []);
|
||||||
for (let vIndex = 0; vIndex < vMaxLength; ++vIndex) {
|
for (let vIndex = 0; vIndex < vMaxLength; ++vIndex) {
|
||||||
let last = { span: 1, banner: dayBitmap[0][vIndex] };
|
let last = { span: 1, banner: dayBitmap[0][vIndex] };
|
||||||
|
|||||||
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