mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
修复windows下路径的问题,添加设置项,完善翻译
This commit is contained in:
1 parent
5980080302
commit
6ad41dc2ae
29 files changed
+368
-278
No files matched your search
@@ -1,8 +1,9 @@
|
||||
@requireCsrfToken
|
||||
为model.system添加缓存
|
||||
bug修复
|
||||
修复discussion前端权限节点显示异常
|
||||
web端运行脚本似乎异常
|
||||
讨论回复通知
|
||||
比赛举办者发布通知
|
||||
举报功能
|
||||
SETTINGS_PRIVACY.allowRegisteredUsers
|
||||
model.setting.ui.name
|
||||
@@ -31,6 +31,9 @@ const build = async (type) => {
|
||||
},
|
||||
],
|
||||
},
|
||||
node: {
|
||||
__dirname: false,
|
||||
},
|
||||
resolve: { extensions: ['.js', '.ts'] },
|
||||
plugins: [
|
||||
new webpack.ProgressPlugin(),
|
||||
|
||||
+30
-18
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable no-eval */
|
||||
import os from 'os';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import yaml from 'js-yaml';
|
||||
@@ -30,20 +29,18 @@ export const builtinScript = [
|
||||
'blacklist', 'setSuperadmin', 'deleteUser',
|
||||
];
|
||||
|
||||
function getFiles(folder, base = '') {
|
||||
function getFiles(folder: string, base = ''): string[] {
|
||||
const files = [];
|
||||
const f = fs.readdirSync(folder);
|
||||
for (const i of f) {
|
||||
if (!i.startsWith('.')) {
|
||||
if (fs.statSync(path.join(folder, i)).isDirectory()) {
|
||||
files.push(...getFiles(path.join(folder, i), path.join(base, i)));
|
||||
} else files.push(path.join(base, i));
|
||||
}
|
||||
if (fs.statSync(path.join(folder, i)).isDirectory()) {
|
||||
files.push(...getFiles(path.join(folder, i), path.join(base, i)));
|
||||
} else files.push(path.join(base, i));
|
||||
}
|
||||
return files;
|
||||
return files.map((item) => item.replace(/\\/gmi, '/'));
|
||||
}
|
||||
|
||||
export async function handler(pending, fail) {
|
||||
export async function handler(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
const p = path.resolve(i, 'handler.js');
|
||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||
@@ -60,7 +57,7 @@ export async function handler(pending, fail) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function locale(pending, fail) {
|
||||
export async function locale(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
let p = path.resolve(i, 'locales');
|
||||
if (!fs.existsSync(p)) p = path.resolve(i, 'locale');
|
||||
@@ -82,7 +79,7 @@ export async function locale(pending, fail) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function setting(pending, fail, modelSetting: typeof import('../model/setting')) {
|
||||
export async function setting(pending: string[], fail: string[], modelSetting: typeof import('../model/setting')) {
|
||||
const map = {
|
||||
system: modelSetting.SystemSetting,
|
||||
account: modelSetting.AccountSetting,
|
||||
@@ -109,7 +106,7 @@ export async function setting(pending, fail, modelSetting: typeof import('../mod
|
||||
}
|
||||
}
|
||||
|
||||
export async function template(pending, fail) {
|
||||
export async function template(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
let p = path.resolve(i, 'templates');
|
||||
if (!fs.existsSync(p)) p = path.resolve(i, 'template');
|
||||
@@ -117,7 +114,9 @@ export async function template(pending, fail) {
|
||||
try {
|
||||
const files = getFiles(p);
|
||||
for (const file of files) {
|
||||
global.Hydro.template[file] = fs.readFileSync(path.resolve(p, file)).toString();
|
||||
global.Hydro.ui.template[file] = fs.readFileSync(
|
||||
path.resolve(p, file),
|
||||
).toString();
|
||||
}
|
||||
console.log(`Template init: ${i}`);
|
||||
} catch (e) {
|
||||
@@ -128,7 +127,20 @@ export async function template(pending, fail) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function model(pending, fail) {
|
||||
export async function uistatic(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
const p = path.resolve(i, 'public', 'static-manifest.json');
|
||||
if (fs.existsSync(p) && fs.statSync(p).isFile() && !fail.includes(i)) {
|
||||
try {
|
||||
Object.assign(global.Hydro.ui.manifest, eval('require')(p));
|
||||
} catch (e) {
|
||||
fail.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function model(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
const p = path.resolve(i, 'model.js');
|
||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||
@@ -145,7 +157,7 @@ export async function model(pending, fail) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function lib(pending, fail) {
|
||||
export async function lib(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
const p = path.resolve(i, 'lib.js');
|
||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||
@@ -163,7 +175,7 @@ export async function lib(pending, fail) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function service(pending, fail) {
|
||||
export async function service(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
const p = path.resolve(i, 'service.js');
|
||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||
@@ -181,7 +193,7 @@ export async function service(pending, fail) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function script(pending, fail, active) {
|
||||
export async function script(pending: string[], fail: string[], active: string[]) {
|
||||
for (const i of pending) {
|
||||
const p = path.resolve(i, 'script.js');
|
||||
if (fs.existsSync(p) && !fail.includes(i)) {
|
||||
@@ -199,7 +211,7 @@ export async function script(pending, fail, active) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function wiki(pending, fail) {
|
||||
export async function wiki(pending: string[], fail: string[]) {
|
||||
for (const i of pending) {
|
||||
const p = path.resolve(i, 'wiki');
|
||||
if (fs.existsSync(p) && fs.statSync(p).isDirectory() && !fail.includes(i)) {
|
||||
|
||||
+15
-5
@@ -4,7 +4,8 @@ import path from 'path';
|
||||
import AdmZip from 'adm-zip';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
const moduleRoots = Array.from(new Set([
|
||||
const moduleRoots = [];
|
||||
const _moduleRoots = Array.from(new Set([
|
||||
path.resolve(process.cwd(), 'node_modules', '@hydrooj'),
|
||||
path.resolve(process.cwd(), 'modules'),
|
||||
path.resolve(process.cwd(), 'module'),
|
||||
@@ -16,17 +17,26 @@ const moduleRoots = Array.from(new Set([
|
||||
path.resolve(__dirname, '.build'),
|
||||
__dirname,
|
||||
path.resolve(os.homedir(), '.hydro', 'module'),
|
||||
// TODO add npm global path
|
||||
'/usr/local/share/.config/yarn/global/node_modules/@hydrooj',
|
||||
path.resolve(os.homedir(), 'AppData', 'Local', 'Yarn', 'Data', 'global', 'node_modules', '@hydrooj'),
|
||||
]));
|
||||
for (const moduleRoot of _moduleRoots) {
|
||||
if (fs.existsSync(moduleRoot)) moduleRoots.push(moduleRoot);
|
||||
}
|
||||
|
||||
if (process.env.debug) console.log('Module roots: ', moduleRoots);
|
||||
|
||||
const moduleTemp = path.resolve(os.tmpdir(), 'hydro', 'module');
|
||||
const publicTemp = path.resolve(os.tmpdir(), 'hydro', 'public');
|
||||
const tmp = path.resolve(os.tmpdir(), 'hydro', '__');
|
||||
|
||||
process.on('unhandledRejection', (e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
export async function load() {
|
||||
process.on('unhandledRejection', (e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
fs.ensureDirSync(moduleTemp);
|
||||
fs.ensureDirSync(publicTemp);
|
||||
for (const moduleRoot of moduleRoots) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
import {
|
||||
locale, template, lib, service, model, handler, script, setting,
|
||||
locale, template, lib, service, model, handler, script, setting, wiki, uistatic,
|
||||
builtinLib, builtinScript, builtinHandler, builtinModel,
|
||||
} from './common';
|
||||
|
||||
@@ -13,7 +13,12 @@ export async function load() {
|
||||
require('../utils');
|
||||
require('../error');
|
||||
require('../options');
|
||||
await Promise.all([locale(pending, fail), template(pending, fail)]);
|
||||
await Promise.all([
|
||||
locale(pending, fail),
|
||||
template(pending, fail),
|
||||
uistatic(pending, fail),
|
||||
wiki(pending, fail),
|
||||
]);
|
||||
const bus = require('../service/bus');
|
||||
await new Promise((resolve) => {
|
||||
const h = () => {
|
||||
@@ -51,6 +56,7 @@ export async function load() {
|
||||
}
|
||||
}
|
||||
for (const i of builtinScript) require(`../script/${i}`);
|
||||
await wiki(pending, fail);
|
||||
await script(pending, fail, active);
|
||||
for (const postInit of global.Hydro.postInit) {
|
||||
await postInit();
|
||||
|
||||
@@ -280,7 +280,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
|
||||
ttype: document.TYPE_CONTEST,
|
||||
lang,
|
||||
code,
|
||||
});
|
||||
}, true);
|
||||
await contest.updateStatus(domainId, this.tdoc.docId, this.user._id, rid, this.pdoc.docId);
|
||||
if (!this.canShowRecord(this.tdoc)) {
|
||||
this.response.body = { tid };
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { ObjectID } from 'mongodb';
|
||||
import {
|
||||
VerifyPasswordError, UserAlreadyExistError, InvalidTokenError,
|
||||
NotFoundError,
|
||||
UserNotFoundError,
|
||||
PermissionError,
|
||||
NotFoundError, UserNotFoundError, PermissionError,
|
||||
} from '../error';
|
||||
import * as bus from '../service/bus';
|
||||
import {
|
||||
@@ -321,7 +319,7 @@ class HomeMessagesHandler extends Handler {
|
||||
const udoc = await user.getById('system', uid);
|
||||
if (!udoc) throw new UserNotFoundError(uid);
|
||||
if (udoc.gravatar) udoc.gravatar = misc.gravatar(udoc.gravatar);
|
||||
const mdoc = await message.send(this.user._id, uid, content);
|
||||
const mdoc = await message.send(this.user._id, uid, content, message.FLAG_UNREAD);
|
||||
// TODO(twd2): improve here: projection
|
||||
if (this.user._id !== uid) {
|
||||
bus.publish(`user_message-${uid}`, { mdoc, udoc });
|
||||
@@ -340,8 +338,9 @@ class HomeMessagesHandler extends Handler {
|
||||
@param('messageId', Types.ObjectID)
|
||||
async postRead(domainId: string, messageId: ObjectID) {
|
||||
const msg = await message.get(messageId);
|
||||
if ([msg.from, msg.to].includes(this.user._id)) await message.setFlag(messageId, message.FLAG_UNREAD);
|
||||
else throw new PermissionError();
|
||||
if ([msg.from, msg.to].includes(this.user._id)) {
|
||||
await message.setFlag(messageId, message.FLAG_UNREAD);
|
||||
} else throw new PermissionError();
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
|
||||
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid);
|
||||
const rid = await record.add(domainId, {
|
||||
pid, lang, code, uid: this.user._id, tid, hidden: true, ttype: document.TYPE_HOMEWORK,
|
||||
});
|
||||
}, true);
|
||||
await contest.updateStatus(domainId, tid, this.user._id,
|
||||
rid, pid, false, 0, document.TYPE_HOMEWORK);
|
||||
this.response.body = { tid, rid };
|
||||
|
||||
@@ -126,7 +126,7 @@ class PretestHandler extends Handler {
|
||||
lang,
|
||||
code,
|
||||
hidden: true,
|
||||
});
|
||||
}, true);
|
||||
this.response.body = { rid };
|
||||
this.response.redirect = this.url('record_detail', { rid });
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as system from '../model/system';
|
||||
import { STATUS, PRIV } from '../model/builtin';
|
||||
import * as record from '../model/record';
|
||||
import {
|
||||
Route, Connection, Handler, ConnectionHandler,
|
||||
Route, Connection, Handler, ConnectionHandler, param, Types,
|
||||
} from '../service/server';
|
||||
import { validate } from '../lib/validator';
|
||||
import * as hpm from '../lib/hpm';
|
||||
@@ -101,18 +101,21 @@ class SystemScriptHandler extends SystemHandler {
|
||||
this.response.body.path.push(['manage_script', null]);
|
||||
}
|
||||
|
||||
async post({ domainId, id, args = '{}' }) {
|
||||
@param('id', Types.String)
|
||||
@param('args', Types.String, true)
|
||||
async post(domainId: string, id: string, args = '{}') {
|
||||
if (!global.Hydro.script[id]) throw new ValidationError('id');
|
||||
args = JSON.parse(args);
|
||||
validate(global.Hydro.script[id].validate, args);
|
||||
const rid = await record.add(domainId, {
|
||||
pid: id,
|
||||
pid: 1,
|
||||
uid: this.user._id,
|
||||
lang: null,
|
||||
code: null,
|
||||
status: STATUS.STATUS_JUDGING,
|
||||
hidden: true,
|
||||
}, false);
|
||||
judge.next({ domainId, rid, message: `Running script: ${id}` });
|
||||
async function report(data) {
|
||||
judge.next({ domainId, rid, ...data });
|
||||
}
|
||||
@@ -125,13 +128,13 @@ class SystemScriptHandler extends SystemHandler {
|
||||
domainId,
|
||||
rid,
|
||||
status: STATUS.STATUS_ACCEPTED,
|
||||
judge_text: ret,
|
||||
judge_text: ret.toString(),
|
||||
judger: 1,
|
||||
time_ms: time,
|
||||
memory_kb: 0,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch((err: Error) => {
|
||||
const time = new Date().getTime() - start;
|
||||
judge.end({
|
||||
domainId,
|
||||
|
||||
@@ -234,7 +234,7 @@ class ProblemPretestHandler extends ProblemDetailHandler {
|
||||
this.limitRate('add_record', 60, 100);
|
||||
const rid = await record.add(domainId, {
|
||||
uid: this.user._id, lang, code, pid: this.pdoc.docId, input,
|
||||
});
|
||||
}, true);
|
||||
await record.judge(domainId, rid);
|
||||
this.response.body = { rid };
|
||||
}
|
||||
|
||||
+5
-2
@@ -259,8 +259,11 @@ declare global {
|
||||
},
|
||||
stat: any,
|
||||
wiki: { [category: string]: { [page: string]: any } },
|
||||
template: { [key: string]: string },
|
||||
ui: any,
|
||||
ui: {
|
||||
manifest: Dict<string>,
|
||||
nav: any,
|
||||
template: { [key: string]: string },
|
||||
},
|
||||
error: typeof import('./error'),
|
||||
locales: Dict<Dict<string>>,
|
||||
postInit: Function[],
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import yaml from 'js-yaml';
|
||||
import serialize from 'serialize-javascript';
|
||||
|
||||
function wrap(func: Function, type: string) {
|
||||
return (...args: any) => {
|
||||
@@ -12,7 +12,7 @@ function wrap(func: Function, type: string) {
|
||||
args[i] = `${args[i].message}\n${args[i].stack}`;
|
||||
}
|
||||
}
|
||||
global.Hydro.model.message.send(1, 1, yaml.dump({ time, type, args }, {}));
|
||||
global.Hydro.model.message.send(1, 1, serialize({ time, type, args }), 0);
|
||||
} catch (e) {
|
||||
func(e.message);
|
||||
}
|
||||
|
||||
+14
-9
@@ -3,16 +3,15 @@ import path from 'path';
|
||||
import serialize from 'serialize-javascript';
|
||||
import nunjucks from 'nunjucks';
|
||||
import * as markdown from './markdown';
|
||||
import { STATUS, PERM, PRIV } from '../model/builtin';
|
||||
import * as misc from './misc';
|
||||
|
||||
class Loader extends nunjucks.Loader {
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
getSource(name: string) {
|
||||
if (!process.env.debug) {
|
||||
if (!global.Hydro.template[name]) throw new Error(`Cannot get template ${name}`);
|
||||
if (!global.Hydro.ui.template[name]) throw new Error(`Cannot get template ${name}`);
|
||||
return {
|
||||
src: global.Hydro.template[name],
|
||||
src: global.Hydro.ui.template[name],
|
||||
path: name,
|
||||
noCache: false,
|
||||
};
|
||||
@@ -47,18 +46,24 @@ class Nunjucks extends nunjucks.Environment {
|
||||
}
|
||||
const env = new Nunjucks();
|
||||
|
||||
export function render(name: string, state: any): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
export async function render(name: string, state: any): Promise<string> {
|
||||
const cdnPrefix = await global.Hydro.model.system.get('server.cdn');
|
||||
return await new Promise((resolve, reject) => {
|
||||
env.render(name, {
|
||||
page_name: name.split('.')[0],
|
||||
...state,
|
||||
typeof: (o) => typeof o,
|
||||
static_url: (str) => `/${str}`,
|
||||
static_url: (assetName: string) => {
|
||||
if (global.Hydro.ui.manifest[assetName]) {
|
||||
return `${cdnPrefix}${global.Hydro.ui.manifest[assetName]}`;
|
||||
}
|
||||
return `${cdnPrefix}${assetName}`;
|
||||
},
|
||||
datetimeSpan: misc.datetimeSpan,
|
||||
paginate: misc.paginate,
|
||||
perm: PERM,
|
||||
PRIV,
|
||||
STATUS,
|
||||
perm: global.Hydro.model.builtin.PERM,
|
||||
PRIV: global.Hydro.model.builtin.PRIV,
|
||||
STATUS: global.Hydro.model.builtin.STATUS,
|
||||
size: misc.size,
|
||||
gravatar: misc.gravatar,
|
||||
model: global.Hydro.model,
|
||||
|
||||
+5
-2
@@ -19,8 +19,11 @@ global.Hydro = {
|
||||
// @ts-ignore
|
||||
lib: {},
|
||||
wiki: {},
|
||||
template: {},
|
||||
ui: {},
|
||||
ui: {
|
||||
manifest: {},
|
||||
nav: [],
|
||||
template: {},
|
||||
},
|
||||
// @ts-ignore
|
||||
error: {},
|
||||
locales: {},
|
||||
|
||||
@@ -5,14 +5,17 @@ import * as db from '../service/db';
|
||||
const coll = db.collection('message');
|
||||
|
||||
export const FLAG_UNREAD = 1;
|
||||
export const FLAG_NOTIFICATION = 2;
|
||||
export const FLAG_ALERT = 2;
|
||||
|
||||
export async function send(from: number, to: number, content: string, flag: number): Promise<Mdoc> {
|
||||
export async function send(
|
||||
from: number, to: number,
|
||||
content: string, flag: number = FLAG_UNREAD,
|
||||
): Promise<Mdoc> {
|
||||
const res = await coll.insertOne({
|
||||
from, to, content, flag: FLAG_UNREAD | flag,
|
||||
from, to, content, flag,
|
||||
});
|
||||
return {
|
||||
from, to, content, _id: res.insertedId, flag: FLAG_UNREAD | flag,
|
||||
from, to, content, _id: res.insertedId, flag,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -60,7 +63,7 @@ export function ensureIndexes() {
|
||||
|
||||
global.Hydro.model.message = {
|
||||
FLAG_UNREAD,
|
||||
FLAG_NOTIFICATION,
|
||||
FLAG_ALERT,
|
||||
|
||||
count,
|
||||
get,
|
||||
|
||||
@@ -46,7 +46,7 @@ export interface JudgeTask {
|
||||
type?: string,
|
||||
}
|
||||
|
||||
export async function add(domainId: string, data: RdocBase, addTask = true) {
|
||||
export async function add(domainId: string, data: RdocBase, addTask: boolean) {
|
||||
_.defaults(data, {
|
||||
status: STATUS.STATUS_WAITING,
|
||||
type: 'judge',
|
||||
|
||||
@@ -117,13 +117,15 @@ SystemSetting(
|
||||
Setting('setting_server', 'server.hostname', null, null, 'text', 'Server Hostname'),
|
||||
Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'),
|
||||
Setting('setting_server', 'server.url', null, null, 'text', 'Server BaseURL'),
|
||||
Setting('setting_server', 'server.cdn', null, '/', 'text', 'CDN Prefix', 'Ends with /'),
|
||||
Setting('setting_server', 'server.port', null, 8888, 'number', 'Server Port'),
|
||||
Setting('setting_server', 'server.xff', null, null, 'text', 'IP Header'),
|
||||
Setting('setting_server', 'server.xff', null, null, 'text', 'IP Header', 'e.g. x-forwarded-for (lowercase)'),
|
||||
Setting('setting_server', 'server.log', null, false, 'boolean', 'Disable Access Log'),
|
||||
Setting('setting_session', 'session.keys', null, [String.random(32)], 'text', 'session.keys', null, FLAG_DISABLED | FLAG_HIDDEN),
|
||||
Setting('setting_session', 'session.secure', null, false, 'boolean', 'session.secure'),
|
||||
Setting('setting_session', 'session.saved_expire_seconds', null, 3600 * 24 * 30, 'number', 'Saved session expire seconds'),
|
||||
Setting('setting_session', 'session.unsaved_expire_seconds', null, 3600 * 3, 'number', 'Unsaved session expire seconds'),
|
||||
Setting('setting_ui', 'ui.name', null, 'Hydro', 'text', 'Site Name'),
|
||||
Setting('setting_ui', 'ui.header_logo', null, '/components/header/header-logo.png', 'text', 'Header Logo'),
|
||||
Setting('setting_ui', 'ui.header_logo_2x', null, '/components/header/header-logo@2x.png', 'text', 'Header Logo@2x'),
|
||||
Setting('setting_ui', 'ui.header_background', null, '/components/header/header-background.png', 'text', 'Header Background'),
|
||||
|
||||
+33
-12
@@ -1,15 +1,31 @@
|
||||
import { FindOneAndUpdateOption } from 'mongodb';
|
||||
import Lru from 'lru-cache';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('system');
|
||||
const cache = new Lru({
|
||||
maxAge: 5000,
|
||||
});
|
||||
|
||||
export async function get(_id: string) {
|
||||
const res = cache.get(_id);
|
||||
if (res !== undefined) return res;
|
||||
const doc = await coll.findOne({ _id });
|
||||
if (doc) return doc.value;
|
||||
if (doc) {
|
||||
cache.set(_id, doc.value);
|
||||
return doc.value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getMany(keys: string[]): Promise<any[]> {
|
||||
const r = [];
|
||||
let success = true;
|
||||
for (const key of keys) {
|
||||
const res = cache.get(key);
|
||||
if (res !== undefined) r.push(res);
|
||||
else success = false;
|
||||
}
|
||||
if (success) return r;
|
||||
const docs = await coll.find({ _id: { $in: keys } }).toArray();
|
||||
const dict = {};
|
||||
const res = [];
|
||||
@@ -22,24 +38,29 @@ export async function getMany(keys: string[]): Promise<any[]> {
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function update(_id: string, operation: any, config: FindOneAndUpdateOption) {
|
||||
await coll.findOneAndUpdate({ _id }, operation, config);
|
||||
return get(_id);
|
||||
}
|
||||
|
||||
export async function set(_id: string, value: any) {
|
||||
await coll.findOneAndUpdate({ _id }, { $set: { value } }, { upsert: true });
|
||||
return get(_id);
|
||||
cache.set(_id, value);
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ _id },
|
||||
{ $set: { value } },
|
||||
{ upsert: true, returnOriginal: false },
|
||||
);
|
||||
return res.value.value;
|
||||
}
|
||||
|
||||
export function inc(field: string) {
|
||||
return update(field, { $inc: { value: 1 } }, { upsert: true });
|
||||
export async function inc(_id: string) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ _id },
|
||||
{ $inc: { value: 1 } },
|
||||
{ upsert: true, returnOriginal: false },
|
||||
);
|
||||
cache.set(_id, res.value.value);
|
||||
return res.value.value;
|
||||
}
|
||||
|
||||
global.Hydro.model.system = {
|
||||
get,
|
||||
getMany,
|
||||
update,
|
||||
inc,
|
||||
set,
|
||||
};
|
||||
@@ -653,6 +653,7 @@ Solve: 解决
|
||||
Solved Problems: 解决题目
|
||||
Sorry, there is no problem in the problem set: 题库中没有题目
|
||||
Sorry, there is no training plan.: 目前没有训练计划。
|
||||
Sorry, there is no user in this domain.: 对不起,此域中没有用户。
|
||||
Sorry: 对不起
|
||||
Sort by: 排序
|
||||
Source type: 来源类型
|
||||
|
||||
+4
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.8.0",
|
||||
"version": "2.8.1",
|
||||
"main": "hydro/loader.ts",
|
||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||
"author": "masnn",
|
||||
@@ -10,7 +10,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hydrooj/markdown-it-katex": "^2.1.1",
|
||||
"@types/lru-cache": "^5.1.0",
|
||||
"adm-zip": "^0.4.16",
|
||||
"ansi_up": "^4.0.4",
|
||||
"detect-browser": "^5.1.1",
|
||||
@@ -51,12 +50,14 @@
|
||||
"@types/koa-router": "^7.4.1",
|
||||
"@types/koa-static-cache": "^5.1.0",
|
||||
"@types/lodash": "^4.14.157",
|
||||
"@types/lru-cache": "^5.1.0",
|
||||
"@types/markdown-it": "^10.0.1",
|
||||
"@types/mongodb": "^3.5.25",
|
||||
"@types/node": "^14.0.22",
|
||||
"@types/nodemailer": "^6.4.0",
|
||||
"@types/nunjucks": "^3.1.3",
|
||||
"@types/prismjs": "^1.16.1",
|
||||
"@types/serialize-javascript": "^4.0.0",
|
||||
"@types/sockjs": "^0.3.32",
|
||||
"@types/superagent": "^4.1.8",
|
||||
"@types/superagent-proxy": "^2.0.0",
|
||||
@@ -93,10 +94,5 @@
|
||||
".build/module/**"
|
||||
]
|
||||
},
|
||||
"snyk": true,
|
||||
"optionalDependencies": {
|
||||
"@hydrooj/geoip": "^1.1.1",
|
||||
"@hydrooj/migrate-vijos": "^1.1.0",
|
||||
"@hydrooj/pastebin": "^1.0.0"
|
||||
}
|
||||
"snyk": true
|
||||
}
|
||||
+57
-21
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-eval */
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const yaml = require('js-yaml');
|
||||
@@ -8,42 +9,77 @@ const IGNORE_CHECK = ['en'];
|
||||
const IGNORE_MISSING = [
|
||||
'?', 'AC', 'Gravatar', 'ID', 'MD5', 'URL',
|
||||
];
|
||||
const RE_TEXT = /_\(['"]([\s\S])*?['"]\)/gmi;
|
||||
const RE_TEMPLATE = /_\(['"]([\s\S])*?['"]\)/gmi;
|
||||
const RE_UI = /i18n\(['"]([\s\S])*?['"](,.*?)?\)/gmi;
|
||||
const TEMPLATE_ROOT = path.resolve(__dirname, '..', 'templates');
|
||||
const LOCALE_ROOT = path.resolve(__dirname, '..', 'locales');
|
||||
|
||||
const UI_ROOT = path.resolve(__dirname, '..', 'ui');
|
||||
const IGNORE_NAME = ['node_modules'];
|
||||
const texts = {};
|
||||
const result = {};
|
||||
const locales = fs.readdirSync(LOCALE_ROOT);
|
||||
let currentFile = '';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function _(str) {
|
||||
function _(str, format) {
|
||||
if (!texts[str]) texts[str] = [currentFile];
|
||||
else texts[str].push(currentFile);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const i18n = _;
|
||||
|
||||
function scan(folder, relative = '') {
|
||||
function scanTemplate(folder, relative = 'templates') {
|
||||
const files = fs.readdirSync(folder);
|
||||
for (const file of files) {
|
||||
const p = path.join(folder, file);
|
||||
if (fs.statSync(p).isDirectory()) {
|
||||
scan(p, path.join(relative, file));
|
||||
} else {
|
||||
currentFile = path.join(relative, file);
|
||||
const f = fs.readFileSync(p).toString();
|
||||
f.replace(RE_TEXT, (substr) => {
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(substr);
|
||||
} catch (e) {
|
||||
console.error('Cannot parse: ', substr, ' in file ', p);
|
||||
}
|
||||
});
|
||||
if (!IGNORE_NAME.includes(file)) {
|
||||
const p = path.join(folder, file);
|
||||
if (fs.statSync(p).isDirectory()) {
|
||||
scanTemplate(p, path.join(relative, file));
|
||||
} else {
|
||||
currentFile = path.join(relative, file);
|
||||
const f = fs.readFileSync(p).toString();
|
||||
f.replace(RE_TEMPLATE, (substr) => {
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(substr);
|
||||
} catch (e) {
|
||||
console.error('Cannot parse: ', substr, ' in file ', p);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scan(TEMPLATE_ROOT);
|
||||
const result = {};
|
||||
const locales = fs.readdirSync(LOCALE_ROOT);
|
||||
function scanUi(folder, relative = 'ui') {
|
||||
const files = fs.readdirSync(folder);
|
||||
for (const file of files) {
|
||||
if (!IGNORE_NAME.includes(file)) {
|
||||
const p = path.join(folder, file);
|
||||
if (fs.statSync(p).isDirectory()) {
|
||||
scanUi(p, path.join(relative, file));
|
||||
} else {
|
||||
currentFile = path.join(relative, file);
|
||||
const f = fs.readFileSync(p).toString();
|
||||
f.replace(RE_UI, (substr) => {
|
||||
try {
|
||||
eval(substr);
|
||||
} catch (e) {
|
||||
if (e.message.endsWith('is not defined')) {
|
||||
global[e.message.split(' ')[0]] = undefined;
|
||||
try {
|
||||
eval(substr);
|
||||
} catch (err) {
|
||||
console.error('Cannot parse: ', substr, ' in file ', p);
|
||||
}
|
||||
} else console.error('Cannot parse: ', substr, ' in file ', p);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scanTemplate(TEMPLATE_ROOT);
|
||||
scanUi(UI_ROOT);
|
||||
if (!name) {
|
||||
for (const locale of locales) {
|
||||
if (!IGNORE_CHECK.includes(locale.split('.')[0])) {
|
||||
|
||||
+86
-85
@@ -1,100 +1,101 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
"root": true,
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"babel",
|
||||
"react"
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
plugins: [
|
||||
'babel',
|
||||
'react',
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"jquery": true,
|
||||
"commonjs": true
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
jquery: true,
|
||||
commonjs: true,
|
||||
},
|
||||
"extends": [
|
||||
"airbnb"
|
||||
extends: [
|
||||
'airbnb',
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 7,
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true,
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true,
|
||||
"legacyDecorators": true
|
||||
}
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 7,
|
||||
ecmaFeatures: {
|
||||
impliedStrict: true,
|
||||
experimentalObjectRestSpread: true,
|
||||
jsx: true,
|
||||
legacyDecorators: true,
|
||||
},
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"webpack": {
|
||||
"config": {
|
||||
"resolve": {
|
||||
"alias": {
|
||||
"vj": path.resolve(__dirname),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
webpack: {
|
||||
config: {
|
||||
resolve: {
|
||||
alias: {
|
||||
vj: path.resolve(__dirname),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"globals": {
|
||||
"__webpack_public_path__": true,
|
||||
"__webpack_require__": true,
|
||||
"UiContext": true,
|
||||
"UserContext": true,
|
||||
"Context": true,
|
||||
"LOCALES": true,
|
||||
"window": true,
|
||||
globals: {
|
||||
__webpack_public_path__: true,
|
||||
__webpack_require__: true,
|
||||
UiContext: true,
|
||||
UserContext: true,
|
||||
Context: true,
|
||||
LOCALES: true,
|
||||
window: true,
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
"always-multiline"
|
||||
rules: {
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'always-multiline',
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
indent: [
|
||||
'error',
|
||||
2,
|
||||
{
|
||||
"SwitchCase": 0
|
||||
}
|
||||
SwitchCase: 0,
|
||||
},
|
||||
],
|
||||
"max-len": [
|
||||
"error",
|
||||
150
|
||||
'max-len': [
|
||||
'error',
|
||||
150,
|
||||
],
|
||||
"quotes": "warn",
|
||||
"func-names": "off",
|
||||
"class-methods-use-this": "off",
|
||||
"consistent-return": "warn",
|
||||
"no-restricted-syntax": "warn",
|
||||
"no-unused-vars": "warn",
|
||||
"no-console": "off",
|
||||
"no-continue": "off",
|
||||
"no-mixed-operators": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"no-lonely-if": "off",
|
||||
"import/first": "off",
|
||||
"react/prefer-stateless-function": "off",
|
||||
"react/self-closing-comp": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/jsx-filename-extension": "off",
|
||||
"react/jsx-one-expression-per-line": "off",
|
||||
"react/no-string-refs": "off",
|
||||
"react/require-default-props": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/destructuring-assignment": "off",
|
||||
"react/button-has-type": "off",
|
||||
"react/forbid-prop-types": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"max-classes-per-file": "off",
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"react/static-property-placement": "off",
|
||||
"no-param-reassign": "off"
|
||||
}
|
||||
quotes: 'warn',
|
||||
'func-names': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'consistent-return': 'warn',
|
||||
'no-restricted-syntax': 'warn',
|
||||
'no-unused-vars': 'warn',
|
||||
'no-console': 'off',
|
||||
'no-continue': 'off',
|
||||
'no-mixed-operators': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-await-in-loop': 'off',
|
||||
'no-lonely-if': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-bitwise': 'off',
|
||||
'import/first': 'off',
|
||||
'react/prefer-stateless-function': 'off',
|
||||
'react/self-closing-comp': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'react/jsx-filename-extension': 'off',
|
||||
'react/jsx-one-expression-per-line': 'off',
|
||||
'react/no-string-refs': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/destructuring-assignment': 'off',
|
||||
'react/button-has-type': 'off',
|
||||
'react/forbid-prop-types': 'off',
|
||||
'jsx-a11y/no-static-element-interactions': 'off',
|
||||
'jsx-a11y/anchor-is-valid': 'off',
|
||||
'jsx-a11y/click-events-have-key-events': 'off',
|
||||
'max-classes-per-file': 'off',
|
||||
'react/jsx-props-no-spreading': 'off',
|
||||
'react/static-property-placement': 'off',
|
||||
},
|
||||
};
|
||||
@@ -1,5 +1,9 @@
|
||||
import { AutoloadPage } from 'vj/misc/PageLoader';
|
||||
import { InfoDialog } from 'vj/components/dialog';
|
||||
import Notification from 'vj/components/notification';
|
||||
import { FLAG_ALERT } from 'vj/constant/message';
|
||||
import i18n from 'vj/utils/i18n';
|
||||
import tpl from 'vj/utils/tpl';
|
||||
|
||||
const messagePage = new AutoloadPage('messagePage', () => {
|
||||
async function init() {
|
||||
@@ -8,12 +12,23 @@ const messagePage = new AutoloadPage('messagePage', () => {
|
||||
const sock = new SockJs(`/home/messages-conn?token=${UiContext.token}`);
|
||||
sock.onmessage = (message) => {
|
||||
const msg = JSON.parse(message.data);
|
||||
Notification.show({
|
||||
title: msg.udoc.uname,
|
||||
avatar: msg.udoc.gravatar,
|
||||
message: msg.mdoc.content,
|
||||
duration: 0,
|
||||
});
|
||||
if (msg.mdoc.flag | FLAG_ALERT) {
|
||||
// Is alert
|
||||
const dialog = new InfoDialog({
|
||||
$body: tpl`
|
||||
<div class="typo">
|
||||
<p>${i18n(msg.udoc.content)}</p>
|
||||
</div>`,
|
||||
});
|
||||
dialog.open();
|
||||
} else {
|
||||
// Is message
|
||||
Notification.show({
|
||||
title: msg.udoc.uname,
|
||||
avatar: msg.udoc.gravatar,
|
||||
message: msg.mdoc.content,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import Message from './MessageComponent';
|
||||
const mapStateToProps = (state) => ({
|
||||
activeId: state.activeId,
|
||||
item: state.activeId !== null
|
||||
? state.dialogues[state.activeId]
|
||||
? state.ues[state.activeId]
|
||||
: null,
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,35 @@ let lastNotification = null;
|
||||
let autoHideTimer = null;
|
||||
|
||||
export default class Notification {
|
||||
constructor({
|
||||
avatar, title, message, type = '', duration = 3000, action,
|
||||
}) {
|
||||
this.type = type;
|
||||
if (avatar) this.type += ' avatar';
|
||||
if (title) this.type += ' title';
|
||||
this.action = (() => { }) || action;
|
||||
this.$dom = $(tpl`<div class="notification ${type} hide"></div>`);
|
||||
if (avatar) $(tpl`<img width="64" height="64" class="avatar" src="${avatar}"></img>`).appendTo(this.$dom);
|
||||
if (title) $(tpl`<h2>${title}</h2>`).appendTo(this.$dom);
|
||||
$(tpl`<p>${message}</p>`).appendTo(this.$dom);
|
||||
this.$dom.on('clink', this.handleClick.bind(this));
|
||||
this.$n = this.$dom
|
||||
.css('z-index', zIndexManager.getNext())
|
||||
.appendTo('body');
|
||||
this.$n.width(); // force reflow
|
||||
this.$n.removeClass('hide');
|
||||
this.autoHideTimer = setTimeout(this.hide.bind(this), duration);
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
this.action();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.$n.addClass('hide');
|
||||
setTimeout(() => this.$n.remove(), 200);
|
||||
}
|
||||
|
||||
static success(message, duration) {
|
||||
return Notification.show({ type: 'success', message, duration });
|
||||
}
|
||||
@@ -22,49 +51,37 @@ export default class Notification {
|
||||
}
|
||||
|
||||
static show({
|
||||
avatar, title, message, type = '', duration = 3000,
|
||||
actionButtons = [],
|
||||
avatar, title, message, type = '', duration = 3000, action,
|
||||
}) {
|
||||
if (duration && lastNotification) {
|
||||
Notification.hide();
|
||||
}
|
||||
if (lastNotification) Notification.hide();
|
||||
if (avatar) type += ' avatar';
|
||||
if (title) type += ' title';
|
||||
const $dom = $(tpl`<div class="notification ${type} hide"></div>`);
|
||||
if (avatar) $(tpl`<img width="64" height="64" class="avatar" src="${avatar}"></img>`).appendTo($dom);
|
||||
if (title) $(tpl`<h2>${title}</h2>`).appendTo($dom);
|
||||
$(tpl`<p>${message}</p>`).appendTo($dom);
|
||||
actionButtons.forEach((button) => {
|
||||
$(tpl`<button class="${button.class}" onclick="javascript:window.${button.funcName}();">${button.name}</button>`).appendTo($dom);
|
||||
});
|
||||
$dom.on('clink', action);
|
||||
const $n = $dom
|
||||
.css('z-index', zIndexManager.getNext())
|
||||
.appendTo('body');
|
||||
$n.width(); // force reflow
|
||||
$n.removeClass('hide');
|
||||
if (duration) {
|
||||
lastNotification = $n;
|
||||
autoHideTimer = setTimeout(Notification.hide, duration);
|
||||
}
|
||||
lastNotification = $n;
|
||||
autoHideTimer = setTimeout(Notification.hide, duration);
|
||||
return $n;
|
||||
}
|
||||
|
||||
static hide($node) {
|
||||
if (!$node) {
|
||||
if (!lastNotification) {
|
||||
return;
|
||||
}
|
||||
if (autoHideTimer) {
|
||||
clearTimeout(autoHideTimer);
|
||||
autoHideTimer = null;
|
||||
}
|
||||
const $n = lastNotification;
|
||||
$n.addClass('hide');
|
||||
setTimeout(() => $n.remove(), 200);
|
||||
lastNotification = null;
|
||||
} else {
|
||||
$node.addClass('hide');
|
||||
setTimeout(() => $node.remove(), 200);
|
||||
static hide() {
|
||||
if (!lastNotification) {
|
||||
return;
|
||||
}
|
||||
if (autoHideTimer) {
|
||||
clearTimeout(autoHideTimer);
|
||||
autoHideTimer = null;
|
||||
}
|
||||
const $n = lastNotification;
|
||||
$n.addClass('hide');
|
||||
setTimeout(() => $n.remove(), 200);
|
||||
lastNotification = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const FLAG_UNREAD = 1;
|
||||
export const FLAG_ALERT = 2;
|
||||
@@ -15,28 +15,3 @@ export const FUNCTION_RANGE = {
|
||||
1: 'Enabled',
|
||||
};
|
||||
attachObjectMeta(FUNCTION_RANGE, 'intKey', true);
|
||||
|
||||
export const BACKGROUND_RANGE = {
|
||||
1: 'Bg1',
|
||||
2: 'Bg2',
|
||||
3: 'Bg3',
|
||||
4: 'Bg4',
|
||||
5: 'Bg5',
|
||||
6: 'Bg6',
|
||||
7: 'Bg7',
|
||||
8: 'Bg8',
|
||||
9: 'Bg9',
|
||||
10: 'Bg10',
|
||||
11: 'Bg11',
|
||||
12: 'Bg12',
|
||||
13: 'Bg13',
|
||||
14: 'Bg14',
|
||||
15: 'Bg15',
|
||||
16: 'Bg16',
|
||||
17: 'Bg17',
|
||||
18: 'Bg18',
|
||||
19: 'Bg19',
|
||||
20: 'Bg20',
|
||||
21: 'Bg21',
|
||||
};
|
||||
attachObjectMeta(BACKGROUND_RANGE, 'intKey', true);
|
||||
@@ -30,13 +30,6 @@
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@hydrooj/geoip@^1.1.1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@hydrooj/geoip/-/geoip-1.1.2.tgz#1dfa02ce06c2aa2690bbce6aea089731eb91d137"
|
||||
integrity sha512-a8OefZxUd+AUlv4w1RweVXqtcb7gxNaTk0oce5/WWDN1CogFbDUK790BPHRvfB3wiSWLm1h0GyjJOFQL9hypwA==
|
||||
dependencies:
|
||||
maxmind "^4.1.3"
|
||||
|
||||
"@hydrooj/markdown-it-katex@^2.1.1":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@hydrooj/markdown-it-katex/-/markdown-it-katex-2.1.1.tgz#0b656483b912593e2a927360592462770e329f25"
|
||||
@@ -44,16 +37,6 @@
|
||||
dependencies:
|
||||
katex "^0.11.1"
|
||||
|
||||
"@hydrooj/migrate-vijos@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hydrooj/migrate-vijos/-/migrate-vijos-1.1.0.tgz#1929ec44accd47be58ff792eda69f4576883e807"
|
||||
integrity sha512-3OqrNyMFQjkIx7LQDzHF0Z/HUzYqWaGPJnRO7JkhblCnZ4wBEhXRzC0ZI4Yil1s/w9+E+POb+5HrzwYPrnV7mw==
|
||||
|
||||
"@hydrooj/pastebin@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@hydrooj/pastebin/-/pastebin-1.0.0.tgz#5b3b86097451dfd056bea84d51b9b7c28b00c895"
|
||||
integrity sha512-AFDgqNTR/luBA1zIlP+4tBbcGC35Nq74nzaHtJ/ZCqZL2RZfpP4p90wH3U3Gi0+YD45VXIvDVEVCgruQ4naFvA==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
|
||||
@@ -648,6 +631,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
|
||||
integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==
|
||||
|
||||
"@types/serialize-javascript@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/serialize-javascript/-/serialize-javascript-4.0.0.tgz#ab9c47edf71f6a4590221118d1dffc37b50d71a2"
|
||||
integrity sha512-y9UO8ozDGF30EVRizmsXuCdZzAxHmYpEEiL+/SQjX+7bnxslkvZQU8rLydixJv7yVae6bWyrNDFHsh6fYHkFMA==
|
||||
|
||||
"@types/serve-static@*":
|
||||
version "1.13.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.4.tgz#6662a93583e5a6cabca1b23592eb91e12fa80e7c"
|
||||
@@ -4319,13 +4307,6 @@ markdown-it@^11.0.0:
|
||||
mdurl "^1.0.1"
|
||||
uc.micro "^1.0.5"
|
||||
|
||||
maxmind@^4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.1.3.tgz#ed1060f45085b7ae2dc95cf5c1672f593d7fa076"
|
||||
integrity sha512-6foBs2wNAfZ7Hu3Ag3nFiIs0gVlHtP1UioHHOuYBi2fH24Ngc5XujUi2GLk0r2HJLjRAaYgaSSTnRoIPs/RWNw==
|
||||
dependencies:
|
||||
tiny-lru "7.0.6"
|
||||
|
||||
md5.js@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||
@@ -6681,11 +6662,6 @@ tiny-glob@^0.2.6:
|
||||
globalyzer "^0.1.0"
|
||||
globrex "^0.1.1"
|
||||
|
||||
tiny-lru@7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-7.0.6.tgz#b0c3cdede1e5882aa2d1ae21cb2ceccf2a331f24"
|
||||
integrity sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==
|
||||
|
||||
tmp@0.0.33, tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
|
||||
Reference in new issue
Block a user