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/hydro/handler/ui.js

105 lines
3.6 KiB
JavaScript

const
path = require('path'),
md5 = require('blueimp-md5'),
static = require('koa-static'),
nunjucks = require('nunjucks'),
hljs = require('hljs'),
MarkdownIt = require('markdown-it'),
katex = require('markdown-it-katex'),
options = require('../options'),
perm = require('../permission'),
builtin = require('../model/builtin'),
{ MIDDLEWARE } = require('../service/server'),
{ NotFoundError } = require('../error'),
MD5_REGEX = /^[0-9a-f]{32}$/;
function getHash(email) {
email = (typeof email === 'string') ? email.trim().toLowerCase() : 'unspecified';
return email.match(MD5_REGEX) ? email : md5(email);
}
class Markdown extends MarkdownIt {
constructor() {
super({
linkify: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang))
try {
return hljs.highlight(lang, str).value;
} catch (__) { } // eslint-disable-line no-empty
return '';
}
});
this.linkify.tlds('.py', false);
this.use(katex);
}
}
const md = new Markdown();
class Nunjucks extends nunjucks.Environment {
constructor() {
super(
new nunjucks.FileSystemLoader(path.resolve(options.template.path)),
{ autoescape: true, trimBlocks: true }
);
this.addFilter('json', function (self) {
return JSON.stringify(self);
}, false);
this.addFilter('assign', function (self, data) {
return Object.assign(self, data);
});
this.addFilter('markdown', function (self) {
return md.render(self);
});
this.addFilter('gravatar_url', function (email, size) {
return `//gravatar.loli.net/avatar/${getHash(email)}?d=mm&s=${size}`;
});
this.addGlobal('static_url', str => `/${str}`);
this.addGlobal('reverse_url', str => str);
this.addGlobal('perm', perm);
this.addGlobal('builtin', builtin);
}
}
const env = new Nunjucks();
MIDDLEWARE(static(path.resolve(process.cwd(), '.uibuild')));
MIDDLEWARE(async (ctx, next) => {
ctx.render_title = str => str;
ctx.UIContext = {
cdn_prefix: '/',
url_prefix: '/'
};
ctx.render = async (name, context) => {
ctx.user = ctx.state.user;
ctx.translate = str => {
return str.translate(ctx.state.user.language);
};
ctx.has_perm = perm => ctx.state.user.hasPerm(perm);
ctx.body = await new Promise((resolve, reject) => {
env.render(name, Object.assign(ctx.state, context, {
handler: ctx,
_: ctx.translate
}), (error, res) => {
if (error) reject(error);
else resolve(res);
});
});
ctx.response.type = 'text/html';
};
try {
try {
await next();
if (!ctx.body) throw new NotFoundError();
if (ctx.query.template || ctx.templateName) {
Object.assign(ctx.body, JSON.parse(ctx.query.data || '{}'));
await ctx.render(ctx.query.template || ctx.templateName, ctx.body);
} else if (ctx.setRedirect) {
ctx.response.type = 'application/octet-stream';
ctx.redirect(ctx.setRedirect);
}
} catch (error) {
if (error.toString().startsWith('Template render error')) throw error;
await ctx.render('error.html', { error });
}
} catch (error) {
await ctx.render('bsod.html', { error });
}
});