mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
Merge branch 'master' of https://github.com/hydro-dev/Hydro
This commit is contained in:
commit
fc0d9873a2
30 files changed
+309
-138
No files matched your search
+3
-10
@@ -39,10 +39,7 @@ const build = async (type) => {
|
||||
}
|
||||
for (const j of prepare) {
|
||||
if (exist(`module/${i}/${j}.js`)) {
|
||||
const file = fs.readFileSync(root(`module/${i}/${j}.js`));
|
||||
if (file.includes('require')) {
|
||||
config.entry[`${i}/${j}`] = root(`module/${i}/${j}.js`);
|
||||
}
|
||||
config.entry[`${i}/${j}`] = root(`module/${i}/${j}.js`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -70,12 +67,7 @@ const build = async (type) => {
|
||||
const current = {};
|
||||
for (const j of prepare) {
|
||||
if (exist(`module/${i}/${j}.js`)) {
|
||||
const file = fs.readFileSync(root(`module/${i}/${j}.js`));
|
||||
if (file.includes('require')) {
|
||||
current[j] = fs.readFileSync(root(`.build/module/${i}/${j}.js`)).toString();
|
||||
} else {
|
||||
current[j] = fs.readFileSync(root(`module/${i}/${j}.js`)).toString();
|
||||
}
|
||||
current[j] = fs.readFileSync(root(`.build/module/${i}/${j}.js`)).toString();
|
||||
}
|
||||
}
|
||||
if (exist(`module/${i}/locale`)) {
|
||||
@@ -101,6 +93,7 @@ const build = async (type) => {
|
||||
current.description = m.description || '';
|
||||
current.requirements = m.requirements || [];
|
||||
current.version = m.version || 'unknown';
|
||||
current.id = m.id;
|
||||
if (m.os) current.os = m.os;
|
||||
const data = zlib.gzipSync(Buffer.from(yaml.safeDump(current)), { level: -1 });
|
||||
fs.writeFileSync(root(`.build/module/${i}.hydro`), data);
|
||||
|
||||
@@ -33,6 +33,7 @@ async function build(type) {
|
||||
'bsod.html',
|
||||
];
|
||||
const builtin = {
|
||||
id: 'builtin',
|
||||
locale: lang,
|
||||
template: template('templates', exclude),
|
||||
file: {},
|
||||
|
||||
@@ -12,6 +12,7 @@ const system = require('../model/system');
|
||||
const { Route, Handler } = require('../service/server');
|
||||
|
||||
const ContestHandler = contest.ContestHandlerMixin(Handler);
|
||||
|
||||
class ContestListHandler extends ContestHandler {
|
||||
async get({ rule = 0, page = 1 }) {
|
||||
this.response.template = 'contest_main.html';
|
||||
@@ -31,11 +32,16 @@ class ContestListHandler extends ContestHandler {
|
||||
const tids = [];
|
||||
for (const tdoc of tdocs) tids.push(tdoc._id);
|
||||
const tsdict = await contest.getListStatus(this.user._id, tids);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['contest_main', null],
|
||||
];
|
||||
this.response.body = {
|
||||
page, tpcount, qs, rule, tdocs, tsdict,
|
||||
page, tpcount, qs, rule, tdocs, tsdict, path,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ContestDetailHandler extends ContestHandler {
|
||||
async _prepare({ tid }) {
|
||||
this.tdoc = await contest.get(tid);
|
||||
@@ -62,6 +68,7 @@ class ContestDetailHandler extends ContestHandler {
|
||||
} else attended = false;
|
||||
const udict = await user.getList([this.tdoc.owner]);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['contest_main', '/c'],
|
||||
[this.tdoc.title, null, true],
|
||||
];
|
||||
@@ -76,10 +83,12 @@ class ContestDetailHandler extends ContestHandler {
|
||||
this.back();
|
||||
}
|
||||
}
|
||||
|
||||
class ContestScoreboardHandler extends ContestDetailHandler {
|
||||
async get({ tid }) {
|
||||
const [tdoc, rows, udict] = await this.getScoreboard(tid);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['contest_main', '/c'],
|
||||
[tdoc.title, `/c/${tid}`, true],
|
||||
['contest_scoreboard', null],
|
||||
@@ -90,6 +99,7 @@ class ContestScoreboardHandler extends ContestDetailHandler {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ContestScoreboardDownloadHandler extends ContestDetailHandler {
|
||||
async get({ tid, ext }) {
|
||||
const getContent = {
|
||||
@@ -101,6 +111,7 @@ class ContestScoreboardDownloadHandler extends ContestDetailHandler {
|
||||
this.binary(await getContent[ext](rows), `${this.tdoc.title}.${ext}`);
|
||||
}
|
||||
}
|
||||
|
||||
class ContestEditHandler extends ContestDetailHandler {
|
||||
async prepare({ tid }) {
|
||||
const tdoc = await contest.get(tid);
|
||||
@@ -113,6 +124,7 @@ class ContestEditHandler extends ContestDetailHandler {
|
||||
for (const i in contest.RULES) { rules[i] = contest.RULES[i].TEXT; }
|
||||
const duration = (this.tdoc.endAt.getTime() - this.tdoc.beginAt.getTime()) / 3600 / 1000;
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['contest_main', '/c'],
|
||||
[this.tdoc.title, `/c/${this.tdoc._id}`, true],
|
||||
['contest_edit', null],
|
||||
@@ -151,6 +163,7 @@ class ContestEditHandler extends ContestDetailHandler {
|
||||
else this.response.redirect = `/c/${this.tdoc._id}`;
|
||||
}
|
||||
}
|
||||
|
||||
class ContestProblemHandler extends ContestDetailHandler {
|
||||
async prepare({ tid, pid }) {
|
||||
[this.tdoc, this.pdoc] = await Promise.all([
|
||||
@@ -175,6 +188,7 @@ class ContestProblemHandler extends ContestDetailHandler {
|
||||
|
||||
async get({ tid }) {
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['contest_main', '/c'],
|
||||
[this.tdoc.title, `/c/${tid}`, true],
|
||||
[this.pdoc.title, null, true],
|
||||
@@ -189,6 +203,7 @@ class ContestProblemHandler extends ContestDetailHandler {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ContestProblemSubmitHandler extends ContestProblemHandler {
|
||||
async get({ tid, pid }) {
|
||||
let rdocs = [];
|
||||
@@ -198,6 +213,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
|
||||
}
|
||||
this.response.template = 'problem_submit.html';
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['contest_main', '/c'],
|
||||
[this.tdoc.title, `/c/${tid}`, true],
|
||||
[this.pdoc.title, `/c/${tid}/p/${pid}`, true],
|
||||
@@ -229,6 +245,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ContestCreateHandler extends ContestHandler {
|
||||
async prepare() {
|
||||
this.checkPerm(PERM_CREATE_CONTEST);
|
||||
@@ -242,8 +259,14 @@ class ContestCreateHandler extends ContestHandler {
|
||||
let ts = now.getTime();
|
||||
ts = ts - (ts % (15 * 60 * 1000)) + 15 * 60 * 1000;
|
||||
const dt = new Date(ts);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['contest_main', '/c'],
|
||||
['contest_create', null],
|
||||
];
|
||||
this.response.body = {
|
||||
rules,
|
||||
path,
|
||||
page_name: 'contest_create',
|
||||
date_text: `${dt.getFullYear()}-${dt.getMonth() + 1}-${dt.getDate()}`,
|
||||
time_text: `${dt.getHours()}:${dt.getMinutes()}`,
|
||||
|
||||
@@ -88,6 +88,7 @@ class DiscussionNodeHandler extends DiscussionHandler {
|
||||
);
|
||||
const udict = await user.getList(ddocs.map((ddoc) => ddoc.owner));
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['discussion_main', '/discuss'],
|
||||
[this.vnode.title, null, true],
|
||||
];
|
||||
@@ -112,6 +113,7 @@ class DiscussionCreateHandler extends DiscussionHandler {
|
||||
|
||||
async get({ type, docId }) {
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['discussion_main', '/discuss'],
|
||||
[this.vnode.title, `/discuss/${type}/${docId}`, true],
|
||||
['discussion_create', null],
|
||||
@@ -155,8 +157,9 @@ class DiscussionDetailHandler extends DiscussionHandler {
|
||||
}
|
||||
const udict = await user.getList(uids);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['discussion_main', '/discuss'],
|
||||
[this.vnode.title, '/discuss/{type}', true],
|
||||
[this.vnode.title, `/discuss/${this.ddoc.parentType}`, true],
|
||||
[this.ddoc.title, null, true],
|
||||
];
|
||||
this.response.template = 'discussion_detail.html';
|
||||
@@ -243,8 +246,15 @@ class DiscussionTailReplyRawHandler extends DiscussionHandler {
|
||||
class DiscussionEditHandler extends DiscussionHandler {
|
||||
async get() {
|
||||
if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['discussion_main', '/discuss'],
|
||||
[this.vnode.title, `/discuss/${this.ddoc.parentType}`, true],
|
||||
[this.ddoc.title, `/discuss/${this.ddoc._id}`, true],
|
||||
['discussion_edit', null],
|
||||
];
|
||||
this.response.template = 'discussion_edit.html';
|
||||
this.response.body = { ddoc: this.ddoc };
|
||||
this.response.body = { ddoc: this.ddoc, path };
|
||||
}
|
||||
|
||||
async post({
|
||||
|
||||
+44
-10
@@ -22,15 +22,25 @@ class ManageMainHandler extends ManageHandler {
|
||||
|
||||
class ManageDashboardHandler extends ManageHandler {
|
||||
async get() {
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['manage', '/manage'],
|
||||
['manage_dashboard', null],
|
||||
];
|
||||
this.response.template = 'manage_dashboard.html';
|
||||
this.response.body = { system: this.system };
|
||||
this.response.body = { system: this.system, path };
|
||||
}
|
||||
}
|
||||
|
||||
class ManageEditHandler extends ManageHandler {
|
||||
async get() {
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['manage', '/manage'],
|
||||
['manage_edit', null],
|
||||
];
|
||||
this.response.template = 'manage_edit.html';
|
||||
this.response.body = { system: this.system };
|
||||
this.response.body = { system: this.system, path };
|
||||
}
|
||||
|
||||
async post({ uname, gravatar, bio }) {
|
||||
@@ -46,7 +56,7 @@ class ManageUserHandler extends ManageHandler {
|
||||
async get() {
|
||||
const uids = [];
|
||||
const rudocs = {};
|
||||
const [udocs, roles, system] = await Promise.all([
|
||||
const [udocs, roles, sys] = await Promise.all([
|
||||
user.getMulti({ role: { $nin: ['default', 'guest'] } }).toArray(),
|
||||
user.getRoles(),
|
||||
user.getById(0),
|
||||
@@ -58,9 +68,14 @@ class ManageUserHandler extends ManageHandler {
|
||||
}
|
||||
const rolesSelect = roles.map((role) => [role._id, role._id]);
|
||||
const udict = await user.getList(uids);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['manage', '/manage'],
|
||||
['manage_user', null],
|
||||
];
|
||||
this.response.template = 'manage_user.html';
|
||||
this.response.body = {
|
||||
roles, rolesSelect, rudocs, udict, system,
|
||||
roles, rolesSelect, rudocs, udict, system: sys, path,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,12 +87,17 @@ class ManageUserHandler extends ManageHandler {
|
||||
|
||||
class ManagePermissionHandler extends ManageHandler {
|
||||
async get() {
|
||||
const [roles, system] = await Promise.all([
|
||||
const [roles, sys] = await Promise.all([
|
||||
user.getRoles(),
|
||||
user.getById(0),
|
||||
]);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['manage', '/manage'],
|
||||
['manage_permission', null],
|
||||
];
|
||||
this.response.template = 'manage_permission.html';
|
||||
this.response.body = { roles, system };
|
||||
this.response.body = { roles, system: sys, path };
|
||||
}
|
||||
|
||||
async post({ roles }) {
|
||||
@@ -95,12 +115,17 @@ class ManagePermissionHandler extends ManageHandler {
|
||||
|
||||
class ManageRoleHandler extends ManageHandler {
|
||||
async get() {
|
||||
const [roles, system] = await Promise.all([
|
||||
const [roles, sys] = await Promise.all([
|
||||
user.getRoles(),
|
||||
user.getById(0),
|
||||
]);
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['manage', '/manage'],
|
||||
['manage_role', null],
|
||||
];
|
||||
this.response.template = 'manage_role.html';
|
||||
this.response.body = { roles, system };
|
||||
this.response.body = { roles, system: sys, path };
|
||||
}
|
||||
|
||||
async postAdd({ role }) {
|
||||
@@ -127,11 +152,20 @@ class ManageRoleHandler extends ManageHandler {
|
||||
class ManageModuleHandler extends ManageHandler {
|
||||
async get() {
|
||||
const installed = await hpm.getInstalled();
|
||||
const active = loader.active.map((module) => module.id);
|
||||
this.response.body = { installed, active };
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['manage', '/manage'],
|
||||
['manage_module', null],
|
||||
];
|
||||
this.response.body = { installed, active: loader.active, path };
|
||||
this.response.template = 'manage_module.html';
|
||||
}
|
||||
|
||||
async postInstall({ url, id }) {
|
||||
await hpm.install(url, id);
|
||||
this.back();
|
||||
}
|
||||
|
||||
async postDelete({ id }) {
|
||||
await hpm.del(id);
|
||||
this.back();
|
||||
|
||||
@@ -32,7 +32,7 @@ class ProblemHandler extends Handler {
|
||||
this.response.template = 'problem_main.html';
|
||||
const q = {};
|
||||
let psdict = {};
|
||||
if (category) q.category = category;
|
||||
if (category) q.$or = [{ category }, { tag: category }];
|
||||
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||
const [pdocs, ppcount, pcount] = await paginate(
|
||||
problem.getMulti(q).sort({ pid: 1 }),
|
||||
@@ -50,6 +50,55 @@ class ProblemHandler extends Handler {
|
||||
path, page, pcount, ppcount, pdocs, psdict, category,
|
||||
};
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
if (this.response.template === 'problem_main.html' && this.preferJson) {
|
||||
const {
|
||||
path, page, pcount, ppcount, pdocs, psdict, category,
|
||||
} = this.response.body;
|
||||
this.response.body = {
|
||||
title: this.renderTitle(category),
|
||||
fragments: (await Promise.all([
|
||||
this.renderHTML('partials/problem_list.html', {
|
||||
page, ppcount, pcount, pdocs, psdict,
|
||||
}),
|
||||
this.renderHTML('partials/problem_stat.html', { pcount }),
|
||||
this.renderHTML('partials/problem_lucky.html', { category }),
|
||||
this.renderHTML('partials/path.html', { path }),
|
||||
])).map((i) => ({ html: i })),
|
||||
raw: {
|
||||
path, page, pcount, ppcount, pdocs, psdict, category,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProblemCategoryHandler extends ProblemHandler {
|
||||
async get({ page = 1, category }) {
|
||||
this.response.template = 'problem_main.html';
|
||||
const q = {
|
||||
$or: [{ category }, { tag: category }],
|
||||
};
|
||||
let psdict = {};
|
||||
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||
const [pdocs, ppcount, pcount] = await paginate(
|
||||
problem.getMulti(q).sort({ pid: 1 }),
|
||||
page,
|
||||
await system.get('PROBLEM_PER_PAGE'),
|
||||
);
|
||||
if (this.user.hasPerm(PERM_LOGGEDIN)) {
|
||||
psdict = await problem.getListStatus(this.user._id, pdocs.map((pdoc) => pdoc._id));
|
||||
}
|
||||
const path = [
|
||||
['Hydro', '/'],
|
||||
['problem_main', '/p'],
|
||||
[category, null, true],
|
||||
];
|
||||
this.response.body = {
|
||||
path, page, pcount, ppcount, pdocs, psdict, category,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ProblemRandomHandler extends ProblemHandler {
|
||||
@@ -59,6 +108,7 @@ class ProblemRandomHandler extends ProblemHandler {
|
||||
const pid = await problem.random(q);
|
||||
if (!pid) throw new NoProblemError();
|
||||
this.response.body = { pid };
|
||||
this.response.redirect = `/p/${pid}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,6 +430,7 @@ class ProblemCreateHandler extends Handler {
|
||||
|
||||
async function apply() {
|
||||
Route('/p', module.exports.ProblemHandler);
|
||||
Route('/p/category/:category', module.exports.ProblemCategoryHandler);
|
||||
Route('/problem/random', module.exports.ProblemRandomHandler);
|
||||
Route('/p/:pid', module.exports.ProblemDetailHandler);
|
||||
Route('/p/:pid/submit', module.exports.ProblemSubmitHandler);
|
||||
@@ -398,6 +449,7 @@ async function apply() {
|
||||
|
||||
global.Hydro.handler.problem = module.exports = {
|
||||
ProblemHandler,
|
||||
ProblemCategoryHandler,
|
||||
ProblemRandomHandler,
|
||||
ProblemDetailHandler,
|
||||
ProblemSubmitHandler,
|
||||
|
||||
+36
-14
@@ -2,6 +2,7 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/* eslint-disable no-eval */
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const zlib = require('zlib');
|
||||
const path = require('path');
|
||||
const yaml = require('js-yaml');
|
||||
@@ -11,29 +12,50 @@ function root(name) {
|
||||
return path.resolve(process.cwd(), name);
|
||||
}
|
||||
|
||||
const moduleRoots = [
|
||||
root('.build/module'),
|
||||
root('module'),
|
||||
root(path.resolve(os.homedir(), '.hydro', 'module')),
|
||||
root('.'),
|
||||
];
|
||||
let moduleRoot;
|
||||
for (const i of moduleRoots) {
|
||||
if (fs.existsSync(i) && fs.statSync(i).isDirectory()) {
|
||||
moduleRoot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function getInstalled() {
|
||||
const files = fs.readdirSync(root('.build/module'));
|
||||
const installed = [];
|
||||
const modules = [];
|
||||
const files = fs.readdirSync(moduleRoot);
|
||||
for (const file of files) {
|
||||
if (file.endsWith('.hydro')) {
|
||||
const f = fs.readFileSync(root(`.build/module/${file}`));
|
||||
const s = fs.statSync(root(`.build/module/${file}`));
|
||||
installed.push({
|
||||
...yaml.safeLoad(zlib.gunzipSync(f)),
|
||||
id: file.split('.')[0],
|
||||
size: s.size,
|
||||
});
|
||||
try {
|
||||
const f = fs.readFileSync(root(`${moduleRoot}/${file}`));
|
||||
const s = fs.statSync(root(`${moduleRoot}/${file}`));
|
||||
const m = {
|
||||
...yaml.safeLoad(zlib.gunzipSync(f)),
|
||||
filename: file.split('.')[0],
|
||||
size: s.size,
|
||||
};
|
||||
modules.push(m);
|
||||
} catch (e) {
|
||||
if (e.code === 'Z_DATA_ERROR') {
|
||||
console.error(`Module Load Fail: ${file} (File Corrupted)`);
|
||||
} else console.error(`Module Load Fail: ${file} ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return installed;
|
||||
return modules;
|
||||
}
|
||||
|
||||
async function del(id) {
|
||||
fs.unlinkSync(root(`.build/module/${id}.hydro`));
|
||||
fs.unlinkSync(root(`${moduleRoot}/${id}.hydro`));
|
||||
}
|
||||
|
||||
async function install(id, url) {
|
||||
await download(url, root(`module/${id}.hydro`));
|
||||
async function install(url) {
|
||||
await download(url, root(`${moduleRoot}/${String.random(16)}.hydro`));
|
||||
}
|
||||
|
||||
module.exports = { getInstalled, del, install };
|
||||
global.Hydro.lib.hpm = module.exports = { getInstalled, del, install };
|
||||
+21
-56
@@ -7,9 +7,6 @@ const zlib = require('zlib');
|
||||
const path = require('path');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
function root(name) {
|
||||
return path.resolve(process.cwd(), name);
|
||||
}
|
||||
function ensureDir(dir) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir);
|
||||
@@ -19,6 +16,7 @@ function ensureDir(dir) {
|
||||
}
|
||||
}
|
||||
|
||||
let pending = [];
|
||||
const active = [];
|
||||
const fail = [];
|
||||
|
||||
@@ -27,42 +25,16 @@ try {
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
const f = require('../.build/module.json');
|
||||
for (const filename in f) {
|
||||
const m = { ...yaml.safeLoad(zlib.gunzipSync(f[filename])), filename };
|
||||
active.push(m);
|
||||
const m = { ...yaml.safeLoad(zlib.gunzipSync(f[filename])), filename, isBuiltin: true };
|
||||
pending.push(m);
|
||||
console.log(filename);
|
||||
}
|
||||
} catch (e) {
|
||||
// Builtin module is in the module directory
|
||||
}
|
||||
|
||||
const moduleRoots = [
|
||||
root('.build/module'),
|
||||
root('module'),
|
||||
root(path.resolve(os.homedir(), '.hydro', 'module')),
|
||||
root('.'),
|
||||
];
|
||||
let moduleRoot;
|
||||
for (const i of moduleRoots) {
|
||||
if (fs.existsSync(i) && fs.statSync(i).isDirectory()) {
|
||||
moduleRoot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async function preload() {
|
||||
const files = fs.readdirSync(moduleRoot);
|
||||
for (const file of files) {
|
||||
if (file.endsWith('.hydro')) {
|
||||
try {
|
||||
const f = fs.readFileSync(root(`${moduleRoot}/${file}`));
|
||||
const m = { ...yaml.safeLoad(zlib.gunzipSync(f)), id: file.split('.')[0] };
|
||||
active.push(m);
|
||||
} catch (e) {
|
||||
console.error(`Module Load Fail: ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
try {
|
||||
if (i.os) {
|
||||
if (!i.os.includes(os.platform().toLowerCase())) throw new Error('Unsupported OS');
|
||||
@@ -90,14 +62,11 @@ async function preload() {
|
||||
}
|
||||
|
||||
async function handler() {
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
if (i.handler && !i.fail) {
|
||||
try {
|
||||
console.log(`Handler init: ${i.id}`);
|
||||
console.time(`Handler init: ${i.id}`);
|
||||
const exports = {};
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const module = { exports, file: i.files || {} };
|
||||
eval(i.handler);
|
||||
console.timeEnd(`Handler init: ${i.id}`);
|
||||
} catch (e) {
|
||||
@@ -110,7 +79,7 @@ async function handler() {
|
||||
}
|
||||
|
||||
async function locale() {
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
if (i.locale && !i.fail) {
|
||||
try {
|
||||
global.Hydro.lib.i18n(i.locale);
|
||||
@@ -125,7 +94,7 @@ async function locale() {
|
||||
}
|
||||
|
||||
async function template() {
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
if (i.template && !i.fail) {
|
||||
try {
|
||||
Object.assign(global.Hydro.template, i.template);
|
||||
@@ -140,15 +109,13 @@ async function template() {
|
||||
}
|
||||
|
||||
async function model() {
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
if (i.model && !i.fail) {
|
||||
try {
|
||||
console.log(`Model init: ${i.id}`);
|
||||
console.time(`Model init: ${i.id}`);
|
||||
const exports = {};
|
||||
const module = { exports, file: i.files || {} };
|
||||
eval(i.model);
|
||||
if ((module.exports || {}).index) await module.exports.index();
|
||||
const m = eval(i.model);
|
||||
if ((m || {}).index) await m.index();
|
||||
console.timeEnd(`Model init: ${i.id}`);
|
||||
} catch (e) {
|
||||
i.fail = true;
|
||||
@@ -160,14 +127,11 @@ async function model() {
|
||||
}
|
||||
|
||||
async function lib() {
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
if (i.lib && !i.fail) {
|
||||
try {
|
||||
console.log(`Lib init: ${i.id}`);
|
||||
console.time(`Lib init: ${i.id}`);
|
||||
const exports = {};
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const module = { exports, file: i.files || {} };
|
||||
eval(i.lib);
|
||||
console.timeEnd(`Lib init: ${i.id}`);
|
||||
} catch (e) {
|
||||
@@ -180,14 +144,12 @@ async function lib() {
|
||||
}
|
||||
|
||||
async function service() {
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
if (i.service && !i.fail) {
|
||||
try {
|
||||
console.time(`Service init: ${i.id}`);
|
||||
const exports = {}; // eslint-disable-line no-unused-vars
|
||||
const module = { exports };
|
||||
eval(i.service);
|
||||
if ((module.exports || {}).init) await module.exports.init();
|
||||
const m = eval(i.service);
|
||||
if ((m || {}).init) await m.init();
|
||||
console.timeEnd(`Service init: ${i.id}`);
|
||||
} catch (e) {
|
||||
i.fail = true;
|
||||
@@ -200,12 +162,10 @@ async function service() {
|
||||
}
|
||||
|
||||
async function script() {
|
||||
for (const i of active) {
|
||||
for (const i of pending) {
|
||||
if (i.script && !i.fail) {
|
||||
try {
|
||||
console.time(`Script init: ${i.id}`);
|
||||
const exports = {};
|
||||
const module = { exports }; // eslint-disable-line no-unused-vars
|
||||
eval(i.script);
|
||||
console.timeEnd(`Script init: ${i.id}`);
|
||||
} catch (e) {
|
||||
@@ -215,6 +175,7 @@ async function script() {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
if (!i.fail) active.push(i.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +200,7 @@ async function load() {
|
||||
template: {},
|
||||
ui: {},
|
||||
};
|
||||
pending.push(...await require('./lib/hpm').getInstalled());
|
||||
await preload();
|
||||
require('./lib/i18n');
|
||||
require('./utils');
|
||||
@@ -303,7 +265,10 @@ async function load() {
|
||||
if (global.Hydro.service[i].postInit) await global.Hydro.service[i].postInit();
|
||||
}
|
||||
await script();
|
||||
pending = [];
|
||||
await server.start();
|
||||
}
|
||||
|
||||
module.exports = { load, active, fail };
|
||||
module.exports = {
|
||||
load, pending, active, fail,
|
||||
};
|
||||
@@ -123,11 +123,11 @@ function count(query) {
|
||||
return coll.find(query).count();
|
||||
}
|
||||
async function random(query) {
|
||||
const pdocs = coll.find(query);
|
||||
const pcount = await pdocs.count();
|
||||
const cursor = coll.find(query);
|
||||
const pcount = await cursor.count();
|
||||
if (pcount) {
|
||||
const pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
|
||||
return pdoc.pid;
|
||||
const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray();
|
||||
return pdoc[0].pid;
|
||||
} return null;
|
||||
}
|
||||
async function getList(pids) {
|
||||
|
||||
@@ -242,7 +242,7 @@ class Handler {
|
||||
}
|
||||
}
|
||||
|
||||
const check = ['tid', 'rid', 'did', 'drid', 'drrid', 'psid', 'psrid', 'docId'];
|
||||
const check = ['tid', 'rid', 'did', 'drid', 'drrid', 'psid', 'psrid', 'docId', 'mongoId'];
|
||||
|
||||
function Route(route, RouteHandler) {
|
||||
router.all(route, async (ctx) => {
|
||||
|
||||
+1
-5
@@ -315,6 +315,7 @@ manage_join_applications: 加域申请
|
||||
manage_role: 管理角色
|
||||
manage_user: 管理用户
|
||||
manage_permission: 管理权限
|
||||
manage_module: 管理模块
|
||||
domain_join: 加入域
|
||||
'Join {0}': '加入 {0}'
|
||||
You are not allowed to join the domain. The link is either invalid or expired.: 您无法加入该域,链接无效或已过期。
|
||||
@@ -772,10 +773,6 @@ home_domain_account: 当前域的设置
|
||||
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
||||
Display name {1} you want to set is used by others.: 您想要设置的显示名 {1} 已经被其他人使用了。
|
||||
Numeric PID: 数字题号
|
||||
Only {0} problems can be copied in one request, got {1}.: 一次请求只能复制 {0} 个题目,但是您输入了 {1} 个。
|
||||
Problem is successfully copied.: 题目复制完成。
|
||||
problem_copy: 复制题目
|
||||
Copy Problem: 复制题目
|
||||
Be Copied: 被复制
|
||||
Copy from: 复制自
|
||||
Problem ID List: 题目 ID 列表
|
||||
@@ -784,7 +781,6 @@ Destination Domain: 目标域
|
||||
Domain ID: 域 ID
|
||||
Test data comes from: 测试数据来自
|
||||
Current dataset comes from: 当前数据集来自
|
||||
Copy Problem To: 将题目复制到
|
||||
What's this?: 这是什么?
|
||||
About test data: 关于测试数据
|
||||
With this feature, you can copy problems that you can view from a domain to some other domain. Their title, content, tags and categories will be copied. However, their test data are not copied directly.: 您可以通过这个功能将某域下您能查看的题目复制到其它域中,题目的标题、描述、标签与分类将被复制过来。但是测试数据不会被直接复制。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "contest-rule-homework",
|
||||
"id": "contest-rule-homework",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "html2md",
|
||||
"id": "html2md",
|
||||
"version": "0.0.1",
|
||||
"description": "Convert html to markdown"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# 迁移须知
|
||||
|
||||
迁移前,您需要指定数据来源。
|
||||
**请不要**将 Hydro 正在使用的数据库设置为数据来源的数据库。
|
||||
迁移过程中,Hydro 的以下内容会被**清空**:
|
||||
|
||||
题目列表,提交记录,用户列表,比赛列表,比赛成绩表,训练列表,训练进度,站内消息,题解,讨论
|
||||
|
||||
以下内容将被迁移:
|
||||
|
||||
题目与测试数据、题解,讨论与回复,比赛与成绩单,训练与进度数据,提交记录,用户列表
|
||||
|
||||
迁移过程中请保持
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "migrate-vijos",
|
||||
"id": "migrate-vijos",
|
||||
"version": "0.0.1",
|
||||
"description": "Migrate From Vijos"
|
||||
}
|
||||
@@ -20,6 +20,7 @@ const tasks = {
|
||||
salt: doc.salt,
|
||||
hash: doc.hash.split('|')[1],
|
||||
hashType: doc.hash.split('|')[0] === 'vj4' ? 'hydro' : doc.hash.split('|')[0],
|
||||
role: doc.priv === -1 ? 'root' : 'default',
|
||||
gravatar: doc.gravatar,
|
||||
email: doc.mail,
|
||||
emailLower: doc.mail_lower,
|
||||
|
||||
@@ -54,8 +54,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="medium-3 columns">
|
||||
{% include 'components/md_hint.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "module-pastebin",
|
||||
"id": "module-pastebin",
|
||||
"version": "1.0.0",
|
||||
"description": "Provide pastebin service."
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "problem-import-syzoj",
|
||||
"id": "problem-import-syzoj",
|
||||
"version": "1.0.0",
|
||||
"description": "Import problem from syzoj."
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "wiki",
|
||||
"id": "wiki",
|
||||
"version": "1.0.0",
|
||||
"description": "Basic wiki module."
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
wiki_about: About
|
||||
wiki_help: Help
|
||||
wiki_development: Development
|
||||
@@ -1,2 +1,7 @@
|
||||
wiki_about: 关于
|
||||
wiki_help: 帮助
|
||||
wiki_development: 开发文档
|
||||
Basic: 基础
|
||||
Module: 模块
|
||||
Introduction: 介绍
|
||||
Deployment: 部署
|
||||
@@ -27,7 +27,7 @@
|
||||
{%- if pdoc['tag']|length > 0 %}
|
||||
<ul class="problem__tags">
|
||||
{%- for tag in pdoc['tag'] %}
|
||||
<li class="problem__tag"><a class="problem__tag-link" href="/p?category={{ tag }}">{{ tag }}</a></li>
|
||||
<li class="problem__tag"><a class="problem__tag-link" href="/p/category/{{ tag }}">{{ tag }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- else %}
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
{% set page_name = "manage_module" %}
|
||||
{% extends "manage_base.html" %}
|
||||
{% block manage_content %}
|
||||
<div style="display: none" class="dialog__body--add-module">
|
||||
<div>
|
||||
<div class="row"><div class="columns">
|
||||
<h1>{{ _('Add Module') }}</h1>
|
||||
</div></div>
|
||||
<div class="row">
|
||||
<div class="medium-6 columns">
|
||||
<label>
|
||||
{{ _('URL') }}
|
||||
<input name="url" type="text" class="textbox" autocomplete="off" data-autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div class="medium-6 columns">
|
||||
<label>
|
||||
{{ _('ID (Optional)') }}
|
||||
<input name="id" type="text" class="textbox" autocomplete="off">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Installed Modules') }}</h1>
|
||||
<div class="section__tools">
|
||||
<a class="primary rounded button" href="/manage/module/add" target="_blank"><span class="icon icon-add"></span> {{ _('Add module') }}</a>
|
||||
<button class="primary rounded button" name="add_module"><span class="icon icon-add"></span> {{ _('Add module') }}</button>
|
||||
<a class="tool-button" href="/wiki/help#module"><span class="icon icon-help"></span> {{ _("What's module?") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -30,12 +51,12 @@
|
||||
<tbody>
|
||||
{% for module in installed %}
|
||||
<tr>
|
||||
<td class="col--id">{{ module.id }}{{ _('(Active)') if active.includes(module.id) else '' }}</td>
|
||||
<td class="col--id">{{ module.id }}</td>
|
||||
<td class="col--desc">{{ _(module.description|default(_('None'))) }}</td>
|
||||
<td class="col--size">{{ size(module.size) if module.size else '?' }}</td>
|
||||
<td class="col--version">{{ module.version|default('?') }}</td>
|
||||
<td class="col--action">
|
||||
<a class="typo-a" href="/manage/module/download/{{ module.id }}">{{ _('Download') }}</a>
|
||||
<a class="typo-a" href="/manage/module/download/{{ module.filename }}">{{ _('Download') }}</a>
|
||||
<form method="post">
|
||||
<input type="hidden" name="operation" value="delete">
|
||||
<input type="hidden" name="id" value="{{ module.id }}">
|
||||
|
||||
@@ -64,12 +64,6 @@
|
||||
<span class="icon icon-comment--text"></span> {{ _('Solutions') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if page_name == 'problem_detail' and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
<li class="menu__seperator"></li>
|
||||
<li class="menu__item"><a class="menu__link" name="problem-sidebar__copy-to" href="javascript:;">
|
||||
<span class="icon icon-copy"></span> {{ _('Copy Problem') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if user._id == pdoc.owner or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %}
|
||||
<li class="menu__seperator"></li>
|
||||
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_edit' %} active{% endif %}" href="/p/{{ pdoc.pid }}/edit">
|
||||
@@ -113,13 +107,9 @@
|
||||
{% if pdoc['category'] %}
|
||||
<span class="hasjs--hide" name="problem-sidebar__categories">
|
||||
{% for category in pdoc['category'] %}
|
||||
<a href="/p?category={{ category }}">{{ category }}</a>
|
||||
<a href="/p/category/{{ category }}">{{ category }}</a>
|
||||
{% if loop.index < pdoc['category']|length %}
|
||||
{% if category in vj4.model.builtin.PROBLEM_CATEGORIES %}
|
||||
{{ _('problem-category-delim') }}
|
||||
{% else %}
|
||||
{{ _('problem-subcategory-delim') }}
|
||||
{% endif %}
|
||||
{{ _('problem-subcategory-delim') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</span>
|
||||
@@ -146,10 +136,6 @@
|
||||
<dd>{{ pdoc.nAccept }}</dd>
|
||||
<dt>{{ _('Accepted Ratio') }}</dt>
|
||||
<dd>{% if pdoc.nSubmit > 0 %}{{ (100 * pdoc.nAccept / pdoc.nSubmit)|round|int }}%{% else %}{{ _('?') }}{% endif %}</dd>
|
||||
{% if pdoc['num_be_copied'] %}
|
||||
<dt>{{ _('Be Copied') }}</dt>
|
||||
<dd>{{ pdoc['num_be_copied'] }}</dd>
|
||||
{% endif %}
|
||||
<dt>{{ _('Uploaded By') }}</dt>
|
||||
<dd>{{ user.render_inline(owner_udoc, badge=false) }}</dd>
|
||||
</dl>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% block content %}
|
||||
<script>
|
||||
var Context = {
|
||||
getProblemUrlWithCategory: "/p?category={{category}}",
|
||||
getProblemUrlWithCategory: "/p/category/{category}",
|
||||
getProblemUrlWithoutCategory: "/p",
|
||||
currentCategory: "{{category}}"
|
||||
};
|
||||
@@ -28,13 +28,13 @@
|
||||
{% for category, sub_categories in model.builtin.CATEGORIES %}
|
||||
<li class="group-list__item">
|
||||
<h2 class="section__title">
|
||||
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>
|
||||
<a href="/p/category/{{ category|urlencode }}">{{ category }}</a>
|
||||
</h2>
|
||||
{% if sub_categories | length > 0 %}
|
||||
<ol class="chip-list">
|
||||
{% for sub_category in sub_categories %}
|
||||
<li class="chip-list__item">
|
||||
<a class="typo-a" href="/p?category={{ category|urlencode + ',' + sub_category|urlencode }}">{{ sub_category }}</a>
|
||||
<a class="typo-a" href="/p/category/{{ category|urlencode + ',' + sub_category|urlencode }}">{{ sub_category }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
|
||||
+2
-1
@@ -44,7 +44,8 @@ module.exports = {
|
||||
"UiContext": true,
|
||||
"UserContext": true,
|
||||
"Context": true,
|
||||
"LOCALES": true
|
||||
"LOCALES": true,
|
||||
"window": true,
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": [
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ async function load() {
|
||||
...buildSequence(includedPages, 'after'),
|
||||
...buildSequence([currentPage], 'after'),
|
||||
];
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const { page, func, type } of loadSequence) {
|
||||
if (typeof func !== 'function') {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
|
||||
@@ -47,6 +47,10 @@ export class AutoloadPage extends Page {
|
||||
}
|
||||
}
|
||||
|
||||
window.Hydro = {
|
||||
NamedPage, AutoloadPage,
|
||||
};
|
||||
|
||||
export class PageLoader {
|
||||
constructor() {
|
||||
const pageReq = require.context('../', true, /\.page\.js$/i);
|
||||
@@ -57,6 +61,7 @@ export class PageLoader {
|
||||
}
|
||||
return page;
|
||||
});
|
||||
window.Hydro.pageInstances = this.pageInstances;
|
||||
}
|
||||
|
||||
getAutoloadPages() {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { NamedPage } from 'vj/misc/PageLoader';
|
||||
import Notification from 'vj/components/notification';
|
||||
import { ActionDialog } from 'vj/components/dialog';
|
||||
|
||||
import request from 'vj/utils/request';
|
||||
|
||||
const page = new NamedPage('manage_module', () => {
|
||||
const addModuleDialog = new ActionDialog({
|
||||
$body: $('.dialog__body--add-module > div'),
|
||||
onDispatch(action) {
|
||||
const $url = addModuleDialog.$dom.find('[name="url"]');
|
||||
if (action === 'ok' && $url.val() === '') {
|
||||
$url.focus();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
addModuleDialog.clear = function () {
|
||||
this.$dom.find('[name="url"]').val('');
|
||||
this.$dom.find('[name="id"]').val('');
|
||||
return this;
|
||||
};
|
||||
|
||||
async function handleClickAddModule() {
|
||||
const action = await addModuleDialog.clear().open();
|
||||
if (action !== 'ok') return;
|
||||
const url = addModuleDialog.$dom.find('[name="url"]').val();
|
||||
const id = addModuleDialog.$dom.find('[name="id"]').val();
|
||||
try {
|
||||
await request.post('', {
|
||||
operation: 'install',
|
||||
url,
|
||||
id,
|
||||
});
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
Notification.error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
$('[name="add_module"]').click(() => handleClickAddModule());
|
||||
});
|
||||
|
||||
export default page;
|
||||
Reference in new issue
Block a user