template: problem_detail

This commit is contained in:
masnn committed 2020-04-03 22:37:30 +08:00
1 parent 2e23d05a59
commit ffa193b5dd
43 files changed
+265 -242

No files matched your search

+7 -1
View File
@@ -16,6 +16,11 @@ process.on('restart', async () => {
delete require.cache;
run();
});
const path = require('path');
const i18n = require('./lib/i18n');
i18n(path.resolve(__dirname, '..', 'locales', 'zh_CN.yaml'), 'zh_CN');
const EventEmitter = require('events');
global.bus = new EventEmitter();
async function run() {
@@ -27,13 +32,14 @@ async function run() {
});
});
let server = require('./service/server');
require('./lib/i18n');
require('./handler/ui');
require('./handler/base');
require('./handler/home');
require('./handler/problem');
require('./handler/record');
require('./handler/judge');
require('./handler/user');
server.start();
}
run().catch(e => {
+6 -8
View File
@@ -15,13 +15,9 @@ MIDDLEWARE(async (ctx, next) => {
let sid = ctx.cookies.get('sid');
let save = ctx.cookies.get('save');
let tokenType, expireSeconds;
if (save) {
tokenType = token.TYPE_SAVED_SESSION;
expireSeconds = options.session.saved_expire_seconds;
} else {
tokenType = token.TYPE_UNSAVED_SESSION;
expireSeconds = options.session.unsaved_expire_seconds;
}
tokenType = token.TYPE_SESSION;
if (save) expireSeconds = options.session.saved_expire_seconds;
else expireSeconds = options.session.unsaved_expire_seconds;
ctx.session = sid ?
await token.update(sid, tokenType, expireSeconds, Object.assign({
update_ip: ctx.request.ip,
@@ -48,6 +44,7 @@ MIDDLEWARE(async (ctx, next) => {
}
}
};
console.log(ctx.session.uid);
await next();
if (ctx.session.sid)
await token.update(ctx.session.sid, tokenType, expireSeconds, Object.assign({
@@ -61,7 +58,8 @@ MIDDLEWARE(async (ctx, next) => {
update_ip: ctx.request.ip,
update_ua: ctx.request.headers['user-agent'] || ''
}, ctx.session));
let cookie = { domain: options.session.domain, secure: options.session.secure, httponly: true };
console.log(ctx.session.sid, ctx.session.uid);
let cookie = { secure: options.session.secure, httponly: true };
if (save) {
cookie.expires = ctx.session.expireAt, cookie.maxAge = expireSeconds;
ctx.cookies.set('save', 'true', cookie);
+5 -7
View File
@@ -1,5 +1,5 @@
const
{ CONTEXT } = require('../service/server'),
{ GET, POST } = require('../service/server'),
queue = require('../service/queue'),
record = require('../model/record'),
{ requirePerm } = require('./tools'),
@@ -7,12 +7,10 @@ const
queue.assert('judge');
const { MIDDLEWARE, GET, POST } = CONTEXT();
MIDDLEWARE(requirePerm(PERM_JUDGE));
GET('/judge/noop', async ctx => {
GET('/judge/noop', requirePerm(PERM_JUDGE), async ctx => {
ctx.body = {};
});
GET('/judge/fetch', async ctx => {
GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
let rid = await queue.get('judge', false);
if (rid) {
let rdoc = await record.get(rid);
@@ -27,9 +25,9 @@ GET('/judge/fetch', async ctx => {
ctx.body = data;
}
});
POST('/judge/next', async ctx => {
POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => {
console.log(ctx.request.body);
});
POST('/judge/end', async ctx => {
POST('/judge/end', requirePerm(PERM_JUDGE), async ctx => {
console.log(ctx.request.body);
});
+1
View File
@@ -38,6 +38,7 @@ GET('/p/:pid', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
let pdoc = await problem.get({ pid, uid });
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
let udoc = await user.getById(pdoc.owner);
ctx.templateName = 'problem_detail.html';
ctx.body = { pdoc, udoc, title: pdoc.title };
});
POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
+48 -8
View File
@@ -1,25 +1,58 @@
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');
{ 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 (data) {
return JSON.stringify(data);
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);
@@ -30,8 +63,10 @@ const env = new Nunjucks();
MIDDLEWARE(static(path.resolve(process.cwd(), '.uibuild')));
MIDDLEWARE(async (ctx, next) => {
ctx.render_title = str => str;
ctx.ui_context = {};
ctx.user_context = {};
ctx.UIContext = {
cdn_prefix: '/',
url_prefix: '/'
};
ctx.render = async (name, context) => {
ctx.user = ctx.state.user;
ctx.translate = str => {
@@ -53,12 +88,17 @@ MIDDLEWARE(async (ctx, next) => {
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 });
}
Object.assign(ctx.body, JSON.parse(ctx.query.data || '{}'));
console.log(ctx.body);
if (ctx.query.template || ctx.templateName) await ctx.render(ctx.query.template || ctx.templateName, ctx.body);
} catch (error) {
await ctx.render('bsod.html', { error });
}
+13 -11
View File
@@ -12,12 +12,16 @@ const
GET('/user', async ctx => {
let udoc = await user.getById(ctx.session.uid);
ctx.templateName = 'user_detail.html';
ctx.body = { udoc };
});
GET('/login', async ctx => {
ctx.templateName = 'user_login.html';
});
POST('/login', async ctx => {
let { username, password, rememberme } = ctx.request.body;
let udoc = await user.getByUname(username);
if (!udoc) throw new LoginError(username);
let { uname, password, rememberme = false } = ctx.request.body;
let udoc = await user.getByUname(uname);
if (!udoc) throw new LoginError(uname);
if (udoc) udoc.checkPassword(password);
await user.setById(udoc._id, { loginat: new Date(), loginip: ctx.request.ip });
udoc.salt = '';
@@ -25,22 +29,20 @@ POST('/login', async ctx => {
console.log(udoc);
ctx.session.uid = udoc._id;
ctx.session.rememberme = rememberme;
ctx.body = { udoc };
ctx.body = {};
ctx.setRedirect = ctx.request.headers.referer || '/';
});
POST('/logout', requirePerm(PERM_LOGGEDIN), async ctx => {
ctx.session = { uid: 1 };
ctx.body = {};
});
let { GET: _GET, POST: _POST, MIDDLEWARE: _MIDDLEWARE } = CONTEXT();
_MIDDLEWARE(requirePerm(PERM_REGISTER_USER));
_GET('/register/:code', async ctx => {
GET('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => {
let code = ctx.request.body.code;
let { mail } = await token.get(code, token.TYPE_REGISTRATION);
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
ctx.body = { mail };
});
_GET('/register/:code', async ctx => {
GET('/register/:code', requirePerm(PERM_REGISTER_USER), async ctx => {
let { code, password, verify_password, uname } = ctx.request.body;
let { mail } = await token.get(code, token.TYPE_REGISTRATION);
if (!mail) throw new InvalidTokenError(token.TYPE_REGISTRATION, code);
@@ -53,7 +55,7 @@ _GET('/register/:code', async ctx => {
});
if (options.smtp.user) {
_POST('/register', limitRate('send_mail', 3600, 30), async ctx => {
POST('/register', requirePerm(PERM_REGISTER_USER), limitRate('send_mail', 3600, 30), async ctx => {
let email = ctx.request.body.email;
validator.check_mail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email);
@@ -95,7 +97,7 @@ if (options.smtp.user) {
ctx.redirect('/');
});
} else
_POST('/register', async ctx => {
POST('/register', requirePerm(PERM_REGISTER_USER), async ctx => {
let email = ctx.request.body.email;
validator.check_mail(email);
if (await user.get_by_mail(email)) throw new UserAlreadyExistError(email);
+2 -2
View File
@@ -25,8 +25,8 @@ String.prototype.rawformat = function (object) {
let res = this.split('{@}');
return [res[0], object, res[1]];
};
String.prototype.translate = function (language = 'zh-CN') {
if (locales[language]) return this;
String.prototype.translate = function (language = 'zh_CN') {
if (locales[language]) return locales[language][this] || this;
else return this;
};
-33
View File
@@ -13,37 +13,7 @@ let server = http.createServer(app.callback());
app.keys = options.session.keys;
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
app.use(Body());
let router = new Router();
let contexts = [];
function CONTEXT() {
this.router = new Router();
/**
* @param {import('koa').Middleware} middleware
*/
function MIDDLEWARE(middleware) {
router.use(middleware);
}
/**
* @param {string} route
* @param {...import('koa').Middleware} handler
*/
function GET(route, ...handler) {
router.get(route, ...handler);
}
/**
* @param {string} route
* @param {...import('koa').Middleware} handler
*/
function POST(route, ...handler) {
router.post(route, ...handler);
}
this.MIDDLEWARE = MIDDLEWARE;
this.GET = GET;
this.POST = POST;
contexts.push(this);
return this;
}
/**
* @param {import('koa').Middleware} middleware
@@ -78,15 +48,12 @@ function SOCKET(prefix, handler) {
sock.on('connection', handler);
sock.installHandlers(server);
}
exports.CONTEXT = CONTEXT;
exports.MIDDLEWARE = MIDDLEWARE;
exports.GET = GET;
exports.POST = POST;
exports.SOCKET = SOCKET;
exports.start = function start() {
app.use(router.routes()).use(router.allowedMethods());
for (let c of contexts)
app.use(c.router.routes()).use(c.router.allowedMethods());
app.listen(options.listen.port);
console.log('Server listening at: %s', options.listen.port);
};