markdown preview

This commit is contained in:
masnn committed 2020-06-12 23:14:19 +08:00
1 parent 86f67a0ec3
commit 5ca424da0b
15 files changed
+123 -104

No files matched your search

-1
View File
@@ -20,7 +20,6 @@ module.exports = () => {
} else {
content.content = `${content.content}${line}\n`;
}
res[pagename].push(content);
}
}
return res;
+1
View File
@@ -48,6 +48,7 @@ class DomainCreateHandler extends Handler {
async post({ id, name }) {
await domain.add(id, this.user._id, name);
this.response.body = { domainId: id };
this.response.redirect = this.url('homepage', { domainId: id });
}
}
+10
View File
@@ -2,6 +2,7 @@
const { PERM_JUDGE, PERM_LOGGEDIN } = require('../permission');
const file = require('../model/file');
const user = require('../model/user');
const markdown = require('../lib/markdown');
const db = require('../service/db');
const { Route, Handler } = require('../service/server');
@@ -54,12 +55,21 @@ class SwitchLanguageHandler extends Handler {
}
}
class MarkdownHandler extends Handler {
async post({ text, safe = true }) {
this.response.body = safe
? markdown.safe.render(text)
: markdown.unsafe.render(text);
}
}
async function apply() {
Route('file_download', '/fs/:id/:secret', FileDownloadHandler);
Route('file_download_with_name', '/fs/:id/:name/:secret', FileDownloadHandler);
Route('status', '/status', StatusHandler);
Route('status_update', '/status/update', StatusUpdateHandler);
Route('switch_language', '/language/:lang', SwitchLanguageHandler);
Route('markdown', '/markdown', MarkdownHandler);
}
apply.updateStatus = function updateStatus(args) {
+10 -2
View File
@@ -14,7 +14,7 @@ const RecordHandler = contest.ContestHandlerMixin(Handler);
class RecordListHandler extends RecordHandler {
async get({
domainId, page = 1, tid, uidOrName,
domainId, page = 1, pid, tid, uidOrName,
}) {
this.response.template = 'record_main.html';
const q = { tid };
@@ -24,6 +24,7 @@ class RecordListHandler extends RecordHandler {
{ _id: parseInt(uidOrName) },
];
}
if (pid) q.pid = pid;
const rdocs = await record.getMany(domainId, q, { _id: -1 }, page, await system.get('RECORD_PER_PAGE'));
const [udict, pdict] = await Promise.all([
user.getList(domainId, rdocs.map((rdoc) => rdoc.uid)),
@@ -34,7 +35,14 @@ class RecordListHandler extends RecordHandler {
['record_main', null],
];
this.response.body = {
path, page, rdocs, pdict, udict,
path,
page,
rdocs,
pdict,
udict,
fliterPid: pid,
fliterTid: tid,
fliterUidOrName: uidOrName,
};
}
}
+8 -4
View File
@@ -28,12 +28,16 @@ async function _parseDagJson(domainId, dag) {
for (const nid of node.requireNids) {
assert(ids.has(nid), `required nid ${nid} not found`);
}
const tasks = [];
for (const i in node.pids) {
// eslint-disable-next-line no-await-in-loop
const pdoc = await problem.get(domainId, node.pids[i]); // FIXME no-await-in-loop
assert(pdoc, `Problem not found: ${node.pids[i]}`);
node.pids[i] = pdoc.docId;
tasks.push(problem.get(domainId, node.pids[i]).then((pdoc) => {
if (!pdoc) throw new ProblemNotFoundError(domainId, node.pids[i]);
node.pids[i] = pdoc.docId;
}));
}
// FIXME no-await-in-loop
// eslint-disable-next-line no-await-in-loop
await Promise.all(tasks);
const newNode = {
_id: parseInt(node._id),
title: node.title,
+1 -1
View File
@@ -2,7 +2,7 @@ const { Route, Handler } = require('../service/server');
const { NotFoundError } = require('../error');
class WikiHandler extends Handler {
async get({ name: page }) {
async get({ page }) {
if (!global.Hydro.wiki[page]) throw new NotFoundError(page);
const contents = global.Hydro.wiki[page];
const path = [
+12 -12
View File
@@ -3,26 +3,26 @@ const permission = require('../permission');
global.Hydro.ui.nav = [];
const trueChecker = () => true;
const Item = (path, name, prefix, perm, checker) => {
const Item = (name, args, prefix, perm, checker) => {
if (perm && checker) {
checker = ((_chk) => (handler) => _chk(handler) && handler.user.hasPerm(perm))(checker);
} else if (perm) {
checker = (handler) => handler.user.hasPerm(perm);
} else if (!checker) checker = trueChecker;
global.Hydro.ui.nav.push({
path, name, prefix, checker,
name, args: args || {}, prefix, checker,
});
};
Item('/', 'homepage', 'homepage');
Item('/p', 'problem_main', 'problem', permission.PERM_VIEW_PROBLEM);
Item('/t', 'training_main', 'training', permission.PERM_VIEW_TRAINING);
Item('/homework', 'homework_main', 'homework', permission.PERM_VIEW_HOMEWORK);
Item('/discuss', 'discussion_main', 'discussion', permission.PERM_VIEW_DISCUSSION);
Item('/c', 'contest_main', 'contest', permission.PERM_VIEW_CONTEST);
Item('/record', 'record_main', 'record');
Item('/ranking', 'ranking', 'ranking');
Item('/domain/dashboard', 'domain_dashboard', 'domain', permission.PERM_MANAGE);
Item('/manage/dashboard', 'manage', 'manage', null, (handler) => handler.user.priv === 1);
Item('homepage', null, 'homepage');
Item('problem_main', null, 'problem', permission.PERM_VIEW_PROBLEM);
Item('training_main', null, 'training', permission.PERM_VIEW_TRAINING);
Item('homework_main', null, 'homework', permission.PERM_VIEW_HOMEWORK);
Item('discussion_main', null, 'discussion', permission.PERM_VIEW_DISCUSSION);
Item('contest_main', null, 'contest', permission.PERM_VIEW_CONTEST);
Item('record_main', null, 'record');
Item('ranking', null, 'ranking');
Item('domain_dashboard', null, 'domain', permission.PERM_MANAGE);
Item('manage_dashboard', null, 'manage', null, (handler) => handler.user.priv === 1);
global.Hydro.lib.nav = module.exports = Item;
+7 -15
View File
@@ -16,14 +16,6 @@ function ensureDir(dir) {
}
}
function superRequire(p) {
// eslint-disable-next-line camelcase
if (typeof __non_webpack_require__ === 'function') {
// eslint-disable-next-line no-undef
return __non_webpack_require__(p);
} return require(p);
}
let pending = [];
const active = [];
const fail = [];
@@ -35,7 +27,7 @@ async function handler() {
try {
console.log(`Handler init: ${i}`);
console.time(`Handler init: ${i}`);
superRequire(p);
eval('require')(p);
console.timeEnd(`Handler init: ${i}`);
} catch (e) {
fail.push(i);
@@ -50,7 +42,7 @@ async function locale() {
const p = `${os.tmpdir()}/hydro/tmp/${i}/locale.json`;
if (fs.existsSync(p) && !fail.includes(i)) {
try {
global.Hydro.lib.i18n(superRequire(p));
global.Hydro.lib.i18n(eval('require')(p));
console.log(`Locale init: ${i}`);
} catch (e) {
fail.push(i);
@@ -65,7 +57,7 @@ async function template() {
const p = `${os.tmpdir()}/hydro/tmp/${i}/template.json`;
if (fs.existsSync(p) && !fail.includes(i)) {
try {
Object.assign(global.Hydro.template, superRequire(p));
Object.assign(global.Hydro.template, eval('require')(p));
console.log(`Template init: ${i}`);
} catch (e) {
fail.push(i);
@@ -82,7 +74,7 @@ async function model() {
try {
console.log(`Model init: ${i}`);
console.time(`Model init: ${i}`);
superRequire(p);
eval('require')(p);
console.timeEnd(`Model init: ${i}`);
} catch (e) {
fail.push(i);
@@ -99,7 +91,7 @@ async function lib() {
try {
console.log(`Lib init: ${i}`);
console.time(`Lib init: ${i}`);
superRequire(p);
eval('require')(p);
console.timeEnd(`Lib init: ${i}`);
} catch (e) {
fail.push(i);
@@ -116,7 +108,7 @@ async function service() {
try {
console.log(`Service init: ${i}`);
console.time(`Service init: ${i}`);
superRequire(p);
eval('require')(p);
console.timeEnd(`Service init: ${i}`);
} catch (e) {
fail.push(i);
@@ -133,7 +125,7 @@ async function script() {
if (fs.existsSync(p) && !fail.includes(i)) {
try {
console.time(`Script init: ${i}`);
superRequire(p);
eval('require')(p);
console.timeEnd(`Script init: ${i}`);
} catch (e) {
fail.push(i);
+54 -57
View File
@@ -186,14 +186,16 @@ class Handler {
url(name, kwargs = {}) { // eslint-disable-line class-methods-use-this
delete kwargs.__keywords;
let res;
const { anchor } = kwargs;
if (kwargs.query) {
const { query } = kwargs;
delete kwargs.query;
res = router.url(name, kwargs, { query });
if (this.args.domainId !== 'system') {
name += '_with_domainId';
kwargs.domainId = kwargs.domainId || this.args.domainId;
}
res = router.url(name, kwargs);
let res;
const { anchor, query } = kwargs;
delete kwargs.anchor;
delete kwargs.query;
if (query) res = router.url(name, kwargs, { query });
else res = router.url(name, kwargs);
if (anchor) return `${res}#${anchor}`;
return res;
}
@@ -311,60 +313,55 @@ class Handler {
}
}
function Route(name, route, RouteHandler, permission = null) {
router.all(name, route, async (ctx) => {
const h = new RouteHandler(ctx);
async function handle(ctx, HandlerClass, permission) {
const h = new HandlerClass(ctx);
try {
const method = ctx.method.toLowerCase();
const args = {
domainId: 'system', ...ctx.params, ...ctx.query, ...ctx.request.body,
};
if (h.___prepare) await h.___prepare(args);
if (permission) h.checkPerm(permission);
try {
const method = ctx.method.toLowerCase();
// TODO(masnn) domainId
const args = {
domainId: 'system', ...ctx.params, ...ctx.query, ...ctx.request.body,
};
if (h.request.host !== await system.get('server.host')) {
try {
args.domainId = (h.request.url.split('//')[1] || '')
.split(`.${await system.get('server.host')}`)[0] || 'system';
} catch (e) {
// Ignore
for (const key in validate) {
if (args[key]) {
args[key] = validate[key](args[key], key);
}
}
if (h.___prepare) await h.___prepare(args);
if (permission) h.checkPerm(permission);
try {
for (const key in validate) {
if (args[key]) {
args[key] = validate[key](args[key], key);
}
}
} catch (e) {
if (e instanceof ValidationError) throw e;
throw new ValidationError('Argument check failed');
}
if (h.__prepare) await h.__prepare(args);
if (h._prepare) await h._prepare(args);
if (h.prepare) await h.prepare(args);
if (h[`___${method}`]) await h[`___${method}`](args);
if (h[`__${method}`]) await h[`__${method}`](args);
if (h[`_${method}`]) await h[`_${method}`](args);
if (h[method]) await h[method](args);
if (method === 'post' && ctx.request.body.operation) {
const operation = `_${ctx.request.body.operation}`
.replace(/_([a-z])/gm, (s) => s[1].toUpperCase());
if (h[`${method}${operation}`]) await h[`${method}${operation}`](args);
}
if (h.cleanup) await h.cleanup(args);
if (h._cleanup) await h._cleanup(args);
if (h.__cleanup) await h.__cleanup(args);
if (h.___cleanup) await h.___cleanup(args);
} catch (e) {
if (h.onerror) await h.onerror(e);
if (e instanceof ValidationError) throw e;
throw new ValidationError('Argument check failed');
}
});
h.args = args;
if (h.__prepare) await h.__prepare(args);
if (h._prepare) await h._prepare(args);
if (h.prepare) await h.prepare(args);
if (h[`___${method}`]) await h[`___${method}`](args);
if (h[`__${method}`]) await h[`__${method}`](args);
if (h[`_${method}`]) await h[`_${method}`](args);
if (h[method]) await h[method](args);
if (method === 'post' && ctx.request.body.operation) {
const operation = `_${ctx.request.body.operation}`
.replace(/_([a-z])/gm, (s) => s[1].toUpperCase());
if (h[`${method}${operation}`]) await h[`${method}${operation}`](args);
}
if (h.cleanup) await h.cleanup(args);
if (h._cleanup) await h._cleanup(args);
if (h.__cleanup) await h.__cleanup(args);
if (h.___cleanup) await h.___cleanup(args);
} catch (e) {
if (h.onerror) await h.onerror(e);
}
}
function Route(name, route, RouteHandler, permission = null) {
router.all(name, route, (ctx) => handle(ctx, RouteHandler, permission));
router.all(`${name}_with_domainId`, `/d/:domainId${route}`, (ctx) => handle(ctx, RouteHandler, permission));
}
class ConnectionHandler {
@@ -461,7 +458,7 @@ function Validate(key, func) {
async function start() {
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
app.use(router.routes()).use(router.allowedMethods());
Route('*', Handler);
Route('notfound_handler', '*', Handler);
if (cluster.worker.id === 1) {
const port = await system.get('server.port');
server.listen(port);
+8 -1
View File
@@ -3,6 +3,12 @@ const { PERM_LOGGEDIN } = global.Hydro.permission;
const { pastebin } = global.Hydro.model;
const { nav } = global.Hydro.lib;
class PasteMainHandler extends Handler {
async get() {
this.response.redirect = this.url('paste_creaate');
}
}
class PasteShowHandler extends Handler {
async get({ docId }) {
const doc = await pastebin.get(docId);
@@ -42,9 +48,10 @@ class PasteCreateHandler extends Handler {
}
async function apply() {
Route('paste', '/paste', PasteMainHandler);
Route('paste_create', '/paste/create', PasteCreateHandler);
Route('paste_show', '/paste/:docId', PasteShowHandler);
nav('/paste/create', 'pastebin', 'pastebin', PERM_LOGGEDIN);
nav('paste', null, 'paste', PERM_LOGGEDIN);
}
global.Hydro.handler.pastebin = module.exports = apply;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "2.1.1",
"version": "2.2.1",
"main": "hydro/loader.js",
"repository": "https://github.com/hydro-dev/Hydro.git",
"author": "masnn",
+3 -3
View File
@@ -1,6 +1,6 @@
{% macro nav_item(target_page_url, target_page_name, target_page_prefix) %}
{% macro nav_item(target_page_name, target_page_args, target_page_prefix) %}
<li class="nav__list-item">
<a href="{{ target_page_url }}" class="nav__item{% if page_name.startsWith(target_page_prefix) %} nav--active{% endif %}">
<a href="{{ url(target_page_name, target_page_args) }}" class="nav__item{% if page_name.startsWith(target_page_prefix) %} nav--active{% endif %}">
{{ _(target_page_name) }}
</a>
</li>
@@ -12,7 +12,7 @@
<li class="nav__list-item"><a href="/" class="nav__logo">&nbsp;</a></li>
{% for item in Context.nav %}
{% if item.checker(handler) %}
{{ nav_item(item.path, item.name, item.prefix) }}
{{ nav_item(item.name, item.args, item.prefix) }}
{% endif %}
{% endfor %}
</ol>
+3 -3
View File
@@ -7,7 +7,7 @@ Given two integers x and y, print the sum.
# Format
## Input
Two integers x and y, satisfying 0 <= x, y <= 32767.
Two integers x and y, satisfying $0\leq x,y\leq 32767$ .
## Output
One integer, the sum of x and y.
@@ -15,16 +15,16 @@ One integer, the sum of x and y.
# Sample 1
## Input
```
123 500
```
## Output
```
623
```
# Limitation
1s, 1024KiB for each test case.
```
+3 -3
View File
@@ -25,19 +25,19 @@
<div class="medium-4 columns">
<label class="filter-user">
{{ _('By Username / UID') }}
<input name="uidOrName" type="text" class="textbox" value="{{ filter_uidOrName }}" autocomplete="off" data-autofocus>
<input name="uidOrName" type="text" class="textbox" value="{{ filterUidOrName }}" autocomplete="off" data-autofocus>
</label>
</div>
<div class="medium-4 columns">
<label>
{{ _('By Problem') }}
<input name="pid" type="text" class="textbox" value="{{ filter_pid }}">
<input name="pid" type="text" class="textbox" value="{{ filterPid }}">
</label>
</div>
<div class="medium-4 columns">
<label>
{{ _('By Contest') }}
<input name="tid" type="text" class="textbox" value="{{ filter_tid }}">
<input name="tid" type="text" class="textbox" value="{{ filterTid }}">
</label>
</div>
</div>
+2 -1
View File
@@ -92,8 +92,9 @@ export default class VjCmEditor extends SimpleMDE {
}
async markdown(text) {
// FIXME preview wont update after first toggle
const data = await request.ajax({
url: '/preview',
url: '/markdown',
method: 'post',
data: $.param({ text }, true),
dataType: 'text',