mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 09:46:28 +08:00
题目搜索,收藏功能
This commit is contained in:
1 parent
a248425db6
commit
441eeadaf3
23 files changed
+158
-97
No files matched your search
@@ -34,19 +34,4 @@ module.exports = {
|
|||||||
console.error('Cannot hack yargs');
|
console.error('Cannot hack yargs');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'markdown-it-katex': () => {
|
|
||||||
const p = require.resolve('markdown-it-katex');
|
|
||||||
const file = fs.readFileSync(p).toString().split('\n');
|
|
||||||
if (file[27].includes('if (prevChar === 0x20/* " " */ || prevChar === 0x09/* \\t */ ||')) {
|
|
||||||
file[27] = 'if (prevChar === 0x09/* \\t */ ||';
|
|
||||||
} else if (!file[27].includes('if (prevChar === 0x09/* \\t */ ||')) {
|
|
||||||
console.error('Cannot hack markdown-it-katex');
|
|
||||||
}
|
|
||||||
if (file[31].includes('if (nextChar === 0x20/* " " */ || nextChar === 0x09/* \\t */) {')) {
|
|
||||||
file[31] = 'if (nextChar === 0x09/* \\t */) {';
|
|
||||||
} else if (!file[27].includes('if (nextChar === 0x09/* \\t */) {')) {
|
|
||||||
console.error('Cannot hack markdown-it-katex');
|
|
||||||
}
|
|
||||||
fs.writeFileSync(p, file.join('\n'));
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
+17
-2
@@ -14,6 +14,7 @@ const document = require('../model/document');
|
|||||||
const system = require('../model/system');
|
const system = require('../model/system');
|
||||||
const user = require('../model/user');
|
const user = require('../model/user');
|
||||||
const setting = require('../model/setting');
|
const setting = require('../model/setting');
|
||||||
|
const domain = require('../model/domain');
|
||||||
const discussion = require('../model/discussion');
|
const discussion = require('../model/discussion');
|
||||||
const token = require('../model/token');
|
const token = require('../model/token');
|
||||||
const training = require('../model/training');
|
const training = require('../model/training');
|
||||||
@@ -87,10 +88,24 @@ class HomeHandler extends Handler {
|
|||||||
this.homework(domainId), this.contest(domainId),
|
this.homework(domainId), this.contest(domainId),
|
||||||
this.training(domainId), this.discussion(domainId),
|
this.training(domainId), this.discussion(domainId),
|
||||||
]);
|
]);
|
||||||
const udict = await user.getList(domainId, ddocs.map((ddoc) => ddoc.owner));
|
const [udict, dodoc, vnodes] = await Promise.all([
|
||||||
|
user.getList(domainId, ddocs.map((ddoc) => ddoc.owner)),
|
||||||
|
domain.get(domainId),
|
||||||
|
discussion.getNodes(domainId),
|
||||||
|
]);
|
||||||
this.response.template = 'main.html';
|
this.response.template = 'main.html';
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
htdocs, htsdict, tdocs, tsdict, trdocs, trsdict, ddocs, vndict, udict,
|
htdocs,
|
||||||
|
htsdict,
|
||||||
|
tdocs,
|
||||||
|
tsdict,
|
||||||
|
trdocs,
|
||||||
|
trsdict,
|
||||||
|
ddocs,
|
||||||
|
vndict,
|
||||||
|
udict,
|
||||||
|
domain: dodoc,
|
||||||
|
vnodes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-9
@@ -32,16 +32,18 @@ class ProblemImportHandler extends Handler {
|
|||||||
if (pid) pdoc.pid = pid;
|
if (pid) pdoc.pid = pid;
|
||||||
if (hidden) pdoc.hidden = true;
|
if (hidden) pdoc.hidden = true;
|
||||||
const docId = await problem.add(domainId, pdoc.title, pdoc.content, this.user._id, pdoc);
|
const docId = await problem.add(domainId, pdoc.title, pdoc.content, this.user._id, pdoc);
|
||||||
const file = path.resolve(os.tmpdir(), 'hydro', `import_${domainId}_${pid}.zip`);
|
if (testdata) {
|
||||||
const w = fs.createWriteStream(file);
|
const file = path.resolve(os.tmpdir(), 'hydro', `import_${domainId}_${pid || pdoc.pid || docId}.zip`);
|
||||||
await new Promise((resolve, reject) => {
|
const w = fs.createWriteStream(file);
|
||||||
w.on('finish', resolve);
|
await new Promise((resolve, reject) => {
|
||||||
w.on('error', reject);
|
w.on('finish', resolve);
|
||||||
testdata.pipe(w);
|
w.on('error', reject);
|
||||||
});
|
testdata.pipe(w);
|
||||||
await problem.setTestdata(domainId, docId, file);
|
});
|
||||||
|
await problem.setTestdata(domainId, docId, file);
|
||||||
|
}
|
||||||
this.response.body = { pid: pid || docId };
|
this.response.body = { pid: pid || docId };
|
||||||
this.response.redirect = `/p/${pid || docId}/settings`;
|
this.response.redirect = this.url('problem_settings', { pid: pid || docId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-10
@@ -27,14 +27,23 @@ class ProblemHandler extends Handler {
|
|||||||
this.checkPerm(PERM_VIEW_PROBLEM);
|
this.checkPerm(PERM_VIEW_PROBLEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get({ domainId, page = 1, category = '' }) {
|
async get({ domainId, page = 1, q = '' }) {
|
||||||
this.response.template = 'problem_main.html';
|
this.response.template = 'problem_main.html';
|
||||||
const q = {};
|
const query = {};
|
||||||
let psdict = {};
|
let psdict = {};
|
||||||
if (category) q.$or = [{ category }, { tag: category }];
|
const path = [
|
||||||
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
['Hydro', 'homepage'],
|
||||||
|
['problem_main', null],
|
||||||
|
];
|
||||||
|
if (q) {
|
||||||
|
q = q.toLowerCase();
|
||||||
|
const $regex = new RegExp(`\\A\\Q${q.replace(/\\E/gmi, /\\E\\E\\Q/gmi)}\\E`, 'gmi');
|
||||||
|
query.title = { $regex };
|
||||||
|
path.push([q, null, null, true]);
|
||||||
|
}
|
||||||
|
if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) query.hidden = false;
|
||||||
const [pdocs, ppcount, pcount] = await paginate(
|
const [pdocs, ppcount, pcount] = await paginate(
|
||||||
problem.getMulti(domainId, q).sort({ pid: 1 }),
|
problem.getMulti(domainId, query).sort({ pid: 1 }),
|
||||||
page,
|
page,
|
||||||
await system.get('PROBLEM_PER_PAGE'),
|
await system.get('PROBLEM_PER_PAGE'),
|
||||||
);
|
);
|
||||||
@@ -43,15 +52,21 @@ class ProblemHandler extends Handler {
|
|||||||
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
domainId, this.user._id, pdocs.map((pdoc) => pdoc.docId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const path = [
|
|
||||||
['Hydro', 'homepage'],
|
|
||||||
['problem_main', null],
|
|
||||||
];
|
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
path, page, pcount, ppcount, pdocs, psdict, category,
|
path, page, pcount, ppcount, pdocs, psdict, category: q,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async postStar({ domainId, pid }) {
|
||||||
|
await problem.setStar(domainId, pid, this.user._id, true);
|
||||||
|
this.back({ star: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async postUnstar({ domainId, pid }) {
|
||||||
|
await problem.setStar(domainId, pid, this.user._id, false);
|
||||||
|
this.back({ star: false });
|
||||||
|
}
|
||||||
|
|
||||||
async cleanup() {
|
async cleanup() {
|
||||||
if (this.response.template === 'problem_main.html' && this.preferJson) {
|
if (this.response.template === 'problem_main.html' && this.preferJson) {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class TrainingDetailHandler extends TrainingHandler {
|
|||||||
const tdoc = await training.get(domainId, tid);
|
const tdoc = await training.get(domainId, tid);
|
||||||
const pids = training.getPids(tdoc);
|
const pids = training.getPids(tdoc);
|
||||||
const f = this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN) ? {} : { hidden: false };
|
const f = this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN) ? {} : { hidden: false };
|
||||||
const [udoc, pdict] = await Promise.all([
|
const [owner, pdict] = await Promise.all([
|
||||||
user.getById(domainId, tdoc.owner),
|
user.getById(domainId, tdoc.owner),
|
||||||
problem.getList(domainId, pids, f),
|
problem.getList(domainId, pids, f),
|
||||||
]);
|
]);
|
||||||
@@ -146,7 +146,7 @@ class TrainingDetailHandler extends TrainingHandler {
|
|||||||
];
|
];
|
||||||
this.response.template = 'training_detail.html';
|
this.response.template = 'training_detail.html';
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
path, tdoc, tsdoc, pids, pdict, psdict, ndict, nsdict, udoc,
|
path, tdoc, tsdoc, pids, pdict, psdict, ndict, nsdict, owner,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ class TrainingCreateHandler extends TrainingHandler {
|
|||||||
}
|
}
|
||||||
const tid = await training.add(domainId, title, content, this.user._id, dag, description);
|
const tid = await training.add(domainId, title, content, this.user._id, dag, description);
|
||||||
this.response.body = { tid };
|
this.response.body = { tid };
|
||||||
this.response.redirect = `/t/${tid}`;
|
this.response.redirect = this.url('training_detail', { tid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ class TrainingEditHandler extends TrainingHandler {
|
|||||||
title, content, dag, description,
|
title, content, dag, description,
|
||||||
});
|
});
|
||||||
this.response.body = { tid };
|
this.response.body = { tid };
|
||||||
this.response.redirect = `/t/${tid}`;
|
this.response.redirect = this.url('training_detail', { tid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-11
@@ -1,17 +1,19 @@
|
|||||||
const superagent = require('superagent');
|
const superagent = require('superagent');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
async function download(url, path, retry = 3) {
|
async function _download(url, path, retry) {
|
||||||
if (path) {
|
const w = fs.createWriteStream(path);
|
||||||
const w = await fs.createWriteStream(path);
|
await superagent.get(url).retry(retry).pipe(w);
|
||||||
await superagent.get(url).retry(retry).pipe(w);
|
await new Promise((resolve, reject) => {
|
||||||
await new Promise((resolve, reject) => {
|
w.on('finish', resolve);
|
||||||
w.on('finish', resolve);
|
w.on('error', reject);
|
||||||
w.on('error', reject);
|
});
|
||||||
});
|
return path;
|
||||||
return path;
|
}
|
||||||
}
|
|
||||||
return superagent.get(url);
|
function download(url, path, retry = 3) {
|
||||||
|
if (path) return _download(url, path, retry);
|
||||||
|
return superagent.get(url).retry(retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.download = module.exports = download;
|
global.Hydro.lib.download = module.exports = download;
|
||||||
+2
-2
@@ -60,8 +60,8 @@ async function del(id) {
|
|||||||
fs.unlinkSync(root(`${moduleRoot}/${id}.hydro`));
|
fs.unlinkSync(root(`${moduleRoot}/${id}.hydro`));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function install(url) {
|
function install(url) {
|
||||||
await download(url, root(`${moduleRoot}/${String.random(16)}.hydro`));
|
return download(url, root(`${moduleRoot}/${String.random(16)}.hydro`));
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib.hpm = module.exports = {
|
global.Hydro.lib.hpm = module.exports = {
|
||||||
|
|||||||
@@ -123,8 +123,11 @@ function getStatus(domainId, did, uid) {
|
|||||||
return document.getStatus(domainId, document.TYPE_DISCUSSION, did, uid);
|
return document.getStatus(domainId, document.TYPE_DISCUSSION, did, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNode(domainId, _id, category) {
|
function addNode(domainId, _id, category, args) {
|
||||||
return document.add(domainId, category, 1, document.TYPE_DISCUSSION_NODE, _id);
|
return document.add(
|
||||||
|
domainId, category, 1, document.TYPE_DISCUSSION_NODE,
|
||||||
|
_id, null, null, args,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNode(domainId, _id) {
|
function getNode(domainId, _id) {
|
||||||
@@ -166,6 +169,10 @@ async function getVnode(domainId, ddoc, handler) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNodes(domainId) {
|
||||||
|
return document.getMulti(domainId, document.TYPE_DISCUSSION_NODE).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
async function getListVnodes(domainId, ddocs, handler) {
|
async function getListVnodes(domainId, ddocs, handler) {
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
const res = {};
|
const res = {};
|
||||||
@@ -201,6 +208,7 @@ global.Hydro.model.discussion = module.exports = {
|
|||||||
setStar,
|
setStar,
|
||||||
getStatus,
|
getStatus,
|
||||||
addNode,
|
addNode,
|
||||||
|
getNodes,
|
||||||
getVnode,
|
getVnode,
|
||||||
getListVnodes,
|
getListVnodes,
|
||||||
};
|
};
|
||||||
+1
-1
@@ -15,7 +15,7 @@ function _timestamp() {
|
|||||||
async function add(streamOrPath, filename, meta = {}) {
|
async function add(streamOrPath, filename, meta = {}) {
|
||||||
if (typeof streamOrPath === 'string') streamOrPath = fs.createReadStream(streamOrPath);
|
if (typeof streamOrPath === 'string') streamOrPath = fs.createReadStream(streamOrPath);
|
||||||
const w = gridfs.openUploadStream(filename);
|
const w = gridfs.openUploadStream(filename);
|
||||||
await coll.insertOne({ ...meta, _id: w.id });
|
await coll.insertOne({ ...meta, _id: w.id, secret: String.random(32) });
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
w.on('error', reject);
|
w.on('error', reject);
|
||||||
w.on('finish', resolve);
|
w.on('finish', resolve);
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ async function add(domainId, title, content, owner, {
|
|||||||
}) {
|
}) {
|
||||||
const d = await domain.inc(domainId, 'pidCounter', 1);
|
const d = await domain.inc(domainId, 'pidCounter', 1);
|
||||||
if (!pid) pid = d.pidCounter.toString();
|
if (!pid) pid = d.pidCounter.toString();
|
||||||
// eslint-disable-next-line no-return-await
|
|
||||||
return await document.add(
|
return await document.add(
|
||||||
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
|
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
|
||||||
{
|
{
|
||||||
@@ -47,6 +46,7 @@ async function add(domainId, title, content, owner, {
|
|||||||
* @returns {Promise<Pdoc>}
|
* @returns {Promise<Pdoc>}
|
||||||
*/
|
*/
|
||||||
async function get(domainId, pid, uid = null, doThrow = true) {
|
async function get(domainId, pid, uid = null, doThrow = true) {
|
||||||
|
if (!Number.isNaN(parseInt(pid, 10))) pid = parseInt(pid, 10);
|
||||||
const pdoc = Number.isInteger(pid)
|
const pdoc = Number.isInteger(pid)
|
||||||
? await document.get(domainId, document.TYPE_PROBLEM, pid)
|
? await document.get(domainId, document.TYPE_PROBLEM, pid)
|
||||||
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { pid }).toArray())[0];
|
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { pid }).toArray())[0];
|
||||||
@@ -95,7 +95,7 @@ function getMultiStatus(domainId, query) {
|
|||||||
* @param {string} domainId
|
* @param {string} domainId
|
||||||
* @param {ObjectID} _id
|
* @param {ObjectID} _id
|
||||||
* @param {object} query
|
* @param {object} query
|
||||||
* @returns {Pdoc}
|
* @returns {Promise<Pdoc>}
|
||||||
*/
|
*/
|
||||||
function edit(domainId, _id, $set) {
|
function edit(domainId, _id, $set) {
|
||||||
return document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
return document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
||||||
@@ -156,6 +156,10 @@ async function updateStatus(domainId, pid, uid, rid, status) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setStar(domainId, pid, uid, star) {
|
||||||
|
return document.setStatus(domainId, document.TYPE_PROBLEM, pid, uid, { star });
|
||||||
|
}
|
||||||
|
|
||||||
async function setTestdata(domainId, _id, filePath) {
|
async function setTestdata(domainId, _id, filePath) {
|
||||||
const pdoc = await get(domainId, _id);
|
const pdoc = await get(domainId, _id);
|
||||||
const config = await readConfig(filePath);
|
const config = await readConfig(filePath);
|
||||||
@@ -176,6 +180,7 @@ global.Hydro.model.problem = module.exports = {
|
|||||||
getList,
|
getList,
|
||||||
getListStatus,
|
getListStatus,
|
||||||
getMultiStatus,
|
getMultiStatus,
|
||||||
|
setStar,
|
||||||
setTestdata,
|
setTestdata,
|
||||||
updateStatus,
|
updateStatus,
|
||||||
};
|
};
|
||||||
@@ -102,6 +102,7 @@ SystemSetting(
|
|||||||
Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'),
|
Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'),
|
||||||
Setting('setting_server', 'server.url', null, null, 'text', 'Server BaseURL'),
|
Setting('setting_server', 'server.url', null, null, 'text', 'Server BaseURL'),
|
||||||
Setting('setting_server', 'server.port', null, 8888, 'number', 'Server Port'),
|
Setting('setting_server', 'server.port', null, 8888, 'number', 'Server Port'),
|
||||||
|
Setting('setting_server', 'server.log', null, false, 'checkbox', 'Disable Access Log'),
|
||||||
Setting('setting_oauth', 'oauth.githubappid', null, null, 'text', 'Github Oauth AppID'),
|
Setting('setting_oauth', 'oauth.githubappid', null, null, 'text', 'Github Oauth AppID'),
|
||||||
Setting('setting_oauth', 'oauth.githubsecret', null, null, 'text', 'Github Oauth Secret'),
|
Setting('setting_oauth', 'oauth.githubsecret', null, null, 'text', 'Github Oauth Secret'),
|
||||||
Setting('setting_oauth', 'oauth.googleappid', null, null, 'text', 'Google Oauth ClientID'),
|
Setting('setting_oauth', 'oauth.googleappid', null, null, 'text', 'Google Oauth ClientID'),
|
||||||
|
|||||||
+10
-4
@@ -23,6 +23,8 @@ const {
|
|||||||
UserFacingError, ValidationError,
|
UserFacingError, ValidationError,
|
||||||
} = require('../error');
|
} = require('../error');
|
||||||
|
|
||||||
|
let enableLog = true;
|
||||||
|
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
let server;
|
let server;
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
@@ -153,7 +155,7 @@ class Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async renderHTML(name, context) {
|
async renderHTML(name, context) {
|
||||||
console.time(name);
|
if (enableLog) console.time(name);
|
||||||
this.hasPerm = (perm) => this.user.hasPerm(perm);
|
this.hasPerm = (perm) => this.user.hasPerm(perm);
|
||||||
const res = await template.render(name, Object.assign(context, {
|
const res = await template.render(name, Object.assign(context, {
|
||||||
handler: this,
|
handler: this,
|
||||||
@@ -161,7 +163,7 @@ class Handler {
|
|||||||
_: (str) => (str ? str.toString().translate(this.user.viewLang || this.session.viewLang) : ''),
|
_: (str) => (str ? str.toString().translate(this.user.viewLang || this.session.viewLang) : ''),
|
||||||
user: this.user,
|
user: this.user,
|
||||||
}));
|
}));
|
||||||
console.timeEnd(name);
|
if (enableLog) console.timeEnd(name);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +268,7 @@ class Handler {
|
|||||||
try {
|
try {
|
||||||
await this.renderBody();
|
await this.renderBody();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.response.status = error instanceof UserFacingError ? error.code : 500;
|
||||||
if (this.preferJson) this.response.body = { error };
|
if (this.preferJson) this.response.body = { error };
|
||||||
else await this.render(error instanceof UserFacingError ? 'error.html' : 'bsod.html', { error });
|
else await this.render(error instanceof UserFacingError ? 'error.html' : 'bsod.html', { error });
|
||||||
}
|
}
|
||||||
@@ -342,6 +345,7 @@ class Handler {
|
|||||||
async onerror(error) {
|
async onerror(error) {
|
||||||
console.error(error.message, error.params);
|
console.error(error.message, error.params);
|
||||||
console.error(error.stack);
|
console.error(error.stack);
|
||||||
|
this.response.status = error instanceof UserFacingError ? error.code : 500;
|
||||||
this.response.template = error instanceof UserFacingError ? 'error.html' : 'bsod.html';
|
this.response.template = error instanceof UserFacingError ? 'error.html' : 'bsod.html';
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
error: { message: error.message, params: error.params, stack: error.stack },
|
error: { message: error.message, params: error.params, stack: error.stack },
|
||||||
@@ -526,10 +530,12 @@ function Validate(key, func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
const [disableLog, port] = await system.getMany(['server.log', 'server.port']);
|
||||||
|
if (!disableLog) {
|
||||||
|
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
||||||
|
} else enableLog = false;
|
||||||
app.use(router.routes()).use(router.allowedMethods());
|
app.use(router.routes()).use(router.allowedMethods());
|
||||||
Route('notfound_handler', '*', Handler);
|
Route('notfound_handler', '*', Handler);
|
||||||
const port = await system.get('server.port');
|
|
||||||
server.listen(port);
|
server.listen(port);
|
||||||
console.log('Server listening at: %s', port);
|
console.log('Server listening at: %s', port);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"id": "migrate-vijos",
|
"id": "migrate-vijos",
|
||||||
"version": "0.0.1",
|
"version": "1.0.0",
|
||||||
"description": "Migrate From Vijos"
|
"description": "Migrate From Vijos"
|
||||||
}
|
}
|
||||||
@@ -236,7 +236,13 @@ async function discussionNode(src, report) {
|
|||||||
const category = item[0];
|
const category = item[0];
|
||||||
const nodes = item[1];
|
const nodes = item[1];
|
||||||
for (const node of nodes || []) {
|
for (const node of nodes || []) {
|
||||||
t.push(discussion.addNode(doc.domain_id, node.name, category));
|
if (node.pic) {
|
||||||
|
t.push(discussion.addNode(
|
||||||
|
doc.domain_id, node.name, category, { pic: node.pic },
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
t.push(discussion.addNode(doc.domain_id, node.name, category));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await Promise.all(t).catch((e) => e);
|
await Promise.all(t).catch((e) => e);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"id": "problem-import-syzoj",
|
"id": "problem-import-syzoj",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "Import problem from syzoj."
|
"description": "Import problem from syzoj."
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,8 @@ async function syzoj(url, handler) {
|
|||||||
const [, host, pid] = RE_SYZOJ.exec(url);
|
const [, host, pid] = RE_SYZOJ.exec(url);
|
||||||
const res = await superagent.get(`${url}export`);
|
const res = await superagent.get(`${url}export`);
|
||||||
assert(res.status === 200, new RemoteOnlineJudgeError('Cannot connect to target server'));
|
assert(res.status === 200, new RemoteOnlineJudgeError('Cannot connect to target server'));
|
||||||
assert(res.data.success, new RemoteOnlineJudgeError((res.data.error || {}).message));
|
assert(res.body.success, new RemoteOnlineJudgeError((res.body.error || {}).message));
|
||||||
const p = res.data.obj;
|
const p = res.body.obj;
|
||||||
const content = [];
|
const content = [];
|
||||||
if (p.description) {
|
if (p.description) {
|
||||||
content.push(
|
content.push(
|
||||||
@@ -64,8 +64,8 @@ async function syzoj(url, handler) {
|
|||||||
type: p.type,
|
type: p.type,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const r = await download(`${url}testdata/download`);
|
const r = download(`${url}testdata/download`);
|
||||||
return [pdoc, r];
|
return [pdoc, r];
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.lib['import.syzoj'] = syzoj;
|
global.Hydro.lib['import.syzoj'] = module.exports = syzoj;
|
||||||
+4
-4
@@ -6,14 +6,14 @@
|
|||||||
"author": "masnn",
|
"author": "masnn",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hydrooj/markdown-it-katex": "^2.1.0",
|
"@hydrooj/markdown-it-katex": "^2.1.1",
|
||||||
"adm-zip": "^0.4.14",
|
"adm-zip": "^0.4.16",
|
||||||
"ansi_up": "^4.0.4",
|
"ansi_up": "^4.0.4",
|
||||||
"bson": "^4.0.2",
|
"bson": "^4.0.2",
|
||||||
"detect-browser": "^5.1.1",
|
"detect-browser": "^5.1.1",
|
||||||
"eslint": "7.2.0",
|
"eslint": "7.2.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"koa": "^2.12.1",
|
"koa": "^2.13.0",
|
||||||
"koa-body": "^4.2.0",
|
"koa-body": "^4.2.0",
|
||||||
"koa-morgan": "^1.0.1",
|
"koa-morgan": "^1.0.1",
|
||||||
"koa-router": "^8.0.8",
|
"koa-router": "^8.0.8",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"nodemailer": "^6.4.10",
|
"nodemailer": "^6.4.10",
|
||||||
"nunjucks": "^3.2.1",
|
"nunjucks": "^3.2.1",
|
||||||
"prismjs": "^1.20.0",
|
"prismjs": "^1.20.0",
|
||||||
"snyk": "^1.346.0",
|
"snyk": "^1.348.1",
|
||||||
"sockjs": "^0.3.20",
|
"sockjs": "^0.3.20",
|
||||||
"superagent": "^5.2.2",
|
"superagent": "^5.2.2",
|
||||||
"superagent-proxy": "^2.0.0",
|
"superagent-proxy": "^2.0.0",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% include 'partials/discussion_nodes_widget.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+3
-3
@@ -3,12 +3,12 @@
|
|||||||
{% extends "layout/basic.html" %}
|
{% extends "layout/basic.html" %}
|
||||||
{% import "components/contest.html" as contest with context %}
|
{% import "components/contest.html" as contest with context %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if system['bulletin'] %}
|
{% if domain['bulletin'] %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section__body typo">
|
<div class="section__body typo">
|
||||||
{{ system['bulletin']|markdown }}
|
{{ domain['bulletin']|markdown|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="large-3 columns">
|
<div class="large-3 columns">
|
||||||
{% if handler.hasPerm(perm.PERM_VIEW_DISCUSSION) %}
|
{% if handler.hasPerm(perm.PERM_VIEW_DISCUSSION) %}
|
||||||
{#{% include 'discussion_nodes_widget.html' %} // TODO(masnn) #}
|
{% include 'partials/discussion_nodes_widget.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="section side">
|
<div class="section side">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div class="section side">
|
||||||
|
<div class="section__header">
|
||||||
|
<h1 class="section__title">{{ _('Discussion Nodes') }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="section__body"><ul class="group-list">
|
||||||
|
{%- for category, nodes in vnodes|groupby('content') -%}
|
||||||
|
<li class="group-list__item">
|
||||||
|
<h2 class="section__title">{{ category }}</h2>
|
||||||
|
<ol class="chip-list">
|
||||||
|
{%- for node in nodes -%}
|
||||||
|
<li class="chip-list__item"><a class="typo-a" href="{{ url('discussion_node', type=model.discussion.typeDisplay[model.document.TYPE_DISCUSSION_NODE], name=node.docId) }}">{{ node.docId }}</a></li>
|
||||||
|
{%- endfor -%}
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
{%- endfor -%}
|
||||||
|
</ul></div>
|
||||||
|
</div>
|
||||||
@@ -50,10 +50,10 @@
|
|||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<h1 class="section__title">{{ _('Search') }}</h1>
|
<h1 class="section__title">{{ _('Search') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<form method="get" action="{{ url('problem_search') }}">
|
<form method="get" action="{{ url('problem_main') }}">
|
||||||
<div class="section__body">
|
<div class="section__body">
|
||||||
<label>
|
<label>
|
||||||
<input name="q" type="text" class="textbox" value="" placeholder="1001">
|
<input name="q" type="text" class="textbox" value="{{ category }}" placeholder="1001">
|
||||||
</label>
|
</label>
|
||||||
<button type="submit" class="primary button">{{ _('Search') }}</button>
|
<button type="submit" class="primary button">{{ _('Search') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -153,9 +153,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<div class="section__body"> <!-- space placeholder --></div>
|
<div class="section__body"> <!-- space placeholder --></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
@@ -194,7 +192,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<dt>{{ _('Enrollees') }}</dt><dd>{{ tdoc['enroll']|default(0) }}</dd>
|
<dt>{{ _('Enrollees') }}</dt><dd>{{ tdoc['enroll']|default(0) }}</dd>
|
||||||
<dt>{{ _('Created By') }}</dt>
|
<dt>{{ _('Created By') }}</dt>
|
||||||
<dd>{{ user.render_inline(owner_udoc, badge=false) }}</dd>
|
<dd>{{ user.render_inline(owner, badge=false) }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
chalk "^2.0.0"
|
chalk "^2.0.0"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
"@hydrooj/markdown-it-katex@^2.1.0":
|
"@hydrooj/markdown-it-katex@^2.1.1":
|
||||||
version "2.1.0"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@hydrooj/markdown-it-katex/-/markdown-it-katex-2.1.0.tgz#6f7bddafff876327359bd41069767793ff7bd964"
|
resolved "https://registry.yarnpkg.com/@hydrooj/markdown-it-katex/-/markdown-it-katex-2.1.1.tgz#0b656483b912593e2a927360592462770e329f25"
|
||||||
integrity sha512-17b6Vd6ZNzRFkz3BKf+je2xJBfDhFYV46TbBwrpS6CGflIV2X41zv+a9n2/Sidn9E9JSYeEMtmVkM8jQx1HpKA==
|
integrity sha512-uOinvM64Y8XPxbsLLPHcOYt0A6eBqnZQUkN6ynWu/pLwD+a3pPnKvBhBi+BHETMGPMJpxXiAWGgs5qqnVN8Hvg==
|
||||||
dependencies:
|
dependencies:
|
||||||
katex "^0.11.1"
|
katex "^0.11.1"
|
||||||
|
|
||||||
@@ -479,10 +479,10 @@ acorn@^7.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd"
|
||||||
integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==
|
integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==
|
||||||
|
|
||||||
adm-zip@^0.4.14:
|
adm-zip@^0.4.16:
|
||||||
version "0.4.14"
|
version "0.4.16"
|
||||||
resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.14.tgz#2cf312bcc9f8875df835b0f6040bd89be0a727a9"
|
resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365"
|
||||||
integrity sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==
|
integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==
|
||||||
|
|
||||||
agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0:
|
agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
@@ -3195,10 +3195,10 @@ koa-static-cache@^5.1.3:
|
|||||||
mime-types "^2.1.8"
|
mime-types "^2.1.8"
|
||||||
mz "^2.7.0"
|
mz "^2.7.0"
|
||||||
|
|
||||||
koa@^2.12.1:
|
koa@^2.13.0:
|
||||||
version "2.12.1"
|
version "2.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/koa/-/koa-2.12.1.tgz#a0ab4dcfebe3cc1b6f0875cbfd686edd7e926643"
|
resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.0.tgz#25217e05efd3358a7e5ddec00f0a380c9b71b501"
|
||||||
integrity sha512-NuYVKjnBxeEe19VljPO9yNcaKKVrMagcax3jjzZtOlxRY2nThWKQqgnI3Pr1OG7mFtvySoDRixoUWZIt6R9C3A==
|
integrity sha512-i/XJVOfPw7npbMv67+bOeXr3gPqOAw6uh5wFyNs3QvJ47tUx3M3V9rIE0//WytY42MKz4l/MXKyGkQ2LQTfLUQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts "^1.3.5"
|
accepts "^1.3.5"
|
||||||
cache-content-type "^1.0.0"
|
cache-content-type "^1.0.0"
|
||||||
@@ -5120,10 +5120,10 @@ snyk-try-require@1.3.1, snyk-try-require@^1.1.1, snyk-try-require@^1.3.1:
|
|||||||
lru-cache "^4.0.0"
|
lru-cache "^4.0.0"
|
||||||
then-fs "^2.0.0"
|
then-fs "^2.0.0"
|
||||||
|
|
||||||
snyk@^1.346.0:
|
snyk@^1.348.1:
|
||||||
version "1.346.0"
|
version "1.348.1"
|
||||||
resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.346.0.tgz#c94646a7ca109a329095419542df92d10df9709f"
|
resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.348.1.tgz#c2eeaa1fd37a0ed8a6ae8a614780d5a006083402"
|
||||||
integrity sha512-uwJDC0CRefppngm1J1M+Sja5CVfdmzLYkxGYp3ZYXrzvd912pgzrdYm8ciUL5a8x1/C+2L0iZ09W68daSuObhA==
|
integrity sha512-jDOUUoJzemIrhIQuUerS1drMY+Ijq1WtE+K2yLhKUgYo6tJnCpqSwIuuJREjeiQ2Q7zRmjlT+iD5owKdEaJR1w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@snyk/cli-interface" "2.8.0"
|
"@snyk/cli-interface" "2.8.0"
|
||||||
"@snyk/dep-graph" "1.18.3"
|
"@snyk/dep-graph" "1.18.3"
|
||||||
|
|||||||
Reference in new issue
Block a user