You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/packages/ui-default/handler.js

194 lines
6.1 KiB
JavaScript

/* eslint-disable no-return-await */
/* eslint-disable camelcase */
const { readdirSync, readFileSync } = require('fs');
const { join } = require('path');
const crypto = require('crypto');
const { tmpdir } = require('os');
const { ObjectID } = require('mongodb');
const bus = require('hydrooj/dist/service/bus');
const { PERM } = require('hydrooj/dist/model/builtin');
const markdown = require('./backendlib/markdown');
const {
system, domain, user, setting, problem, contest,
} = global.Hydro.model;
const { Route, Handler, UiContextBase } = global.Hydro.service.server;
class WikiHelpHandler extends Handler {
constructor(args) {
super(args);
this.noCheckPermView = true;
}
async get() {
const LANGS = setting.langs;
const languages = {};
// eslint-disable-next-line guard-for-in
for (const key in LANGS) {
languages[`${LANGS[key].display}(${key})`] = LANGS[key].compile || LANGS[key].execute;
}
this.response.body = { languages };
this.response.template = 'wiki_help.html';
}
}
class WikiAboutHandler extends Handler {
constructor(args) {
super(args);
this.noCheckPermView = true;
}
async get() {
this.response.template = 'about.html';
}
}
class UiConstantsHandler extends Handler {
constructor(args) {
super(args);
this.noCheckPermView = true;
}
async get() {
this.response.body = `window.LANGS=${JSON.stringify(setting.langs)}`;
this.response.type = 'text/javascript';
this.ctx.set('nolog', '1');
}
}
class UiSettingsHandler extends Handler {
constructor(args) {
super(args);
this.noCheckPermView = true;
}
async get({ domainId }) {
const [nav_logo_dark, nav_logo_dark_2x] = system.getMany([
'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x',
]);
const ddoc = await domain.get(domainId);
this.response.body = await this.renderHTML('extra.css', {
nav_logo_dark,
nav_logo_dark_2x,
...ddoc,
});
this.response.type = 'text/css';
this.ctx.set('nolog', '1');
}
}
class LocaleHandler extends Handler {
constructor(args) {
super(args);
this.noCheckPermView = true;
}
async get({ id }) {
// eslint-disable-next-line prefer-destructuring
id = id.split('.')[0];
// TODO use language_default setting
if (!global.Hydro.locales[id]) id = system.get('server.language');
this.response.body = `window.LOCALES=${JSON.stringify(global.Hydro.locales[id])}`;
this.response.type = 'text/javascript';
this.ctx.set('nolog', '1');
}
}
class SetThemeHandler extends Handler {
constructor(args) {
super(args);
this.noCheckPermView = true;
}
async get({ theme }) {
await user.setById(this.user._id, { theme });
this.back();
}
}
class MarkdownHandler extends Handler {
constructor(args) {
super(args);
this.noCheckPermView = true;
}
async post({ text, html = false, inline = false }) {
this.response.body = inline
? markdown.renderInline(text, html)
: markdown.render(text, html);
this.response.type = 'text/html';
this.response.status = 200;
}
}
class RichMediaHandler extends Handler {
async renderUser(domainId, payload) {
let d = payload.domainId || domainId;
const cur = payload.domainId ? await user.getById(payload.domainId, this.user._id) : this.user;
if (!cur.hasPerm(PERM.PERM_VIEW)) d = domainId;
const udoc = Number.isNaN(+payload.id) ? await user.getByUname(d, payload.id) : await user.getById(d, +payload.id);
return await this.renderHTML('partials/user.html', { udoc });
}
async renderProblem(domainId, payload) {
const cur = payload.domainId ? await user.getById(payload.domainId, this.user._id) : this.user;
let pdoc = cur.hasPerm(PERM.PERM_VIEW | PERM.PERM_VIEW_PROBLEM)
? await problem.get(payload.domainId || domainId, payload.id) || problem.default
: problem.default;
if (pdoc.hidden && !cur.own(pdoc) && !cur.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) pdoc = problem.default;
return await this.renderHTML('partials/problem.html', { pdoc });
}
async renderContest(domainId, payload) {
const cur = payload.domainId ? await user.getById(payload.domainId, this.user._id) : this.user;
const tdoc = cur.hasPerm(PERM.PERM_VIEW | PERM.PERM_VIEW_CONTEST)
? await contest.get(payload.domainId || domainId, new ObjectID(payload.id))
: null;
if (tdoc) return await this.renderHTML('partials/contest.html', { tdoc });
return '';
}
async post({ domainId, items }) {
const res = [];
for (const item of items) {
if (item.domainId && item.domainId === domainId) delete item.domainId;
if (item.type === 'user') res.push(this.renderUser(domainId, item).catch(() => ''));
else if (item.type === 'problem') res.push(this.renderProblem(domainId, item).catch(() => ''));
else if (item.type === 'contest') res.push(this.renderContest(domainId, item).catch(() => ''));
else res.push('');
}
this.response.body = await Promise.all(res);
}
}
4 years ago
const getHash = (i) => {
const shasum = crypto.createHash('sha1');
const file = readFileSync(join(tmpdir(), 'hydro', 'public', i));
shasum.update(file);
4 years ago
return shasum.digest('hex').substr(0, 10);
};
const getUrl = (files) => files.map((i) => `/${i}?${getHash(i)}`);
bus.on('app/started', () => {
const files = readdirSync(join(tmpdir(), 'hydro', 'public'));
const pages = files.filter((file) => file.endsWith('.page.js'));
const themes = files.filter((file) => file.endsWith('.theme.js'));
UiContextBase.extraPages = getUrl(pages);
4 years ago
UiContextBase.themes = {};
for (const theme of themes) {
UiContextBase.themes[theme] = `/${theme}?${getHash(theme)}`;
}
});
global.Hydro.handler.ui = async () => {
Route('wiki_help', '/wiki/help', WikiHelpHandler);
Route('wiki_about', '/wiki/about', WikiAboutHandler);
Route('ui_constants', '/ui-constants.js', UiConstantsHandler);
Route('locale', '/locale/:id', LocaleHandler);
Route('set_theme', '/set_theme/:id', SetThemeHandler);
Route('ui_extracss', '/extra.css', UiSettingsHandler);
Route('markdown', '/markdown', MarkdownHandler);
Route('media', '/media', RichMediaHandler);
};