You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
490 lines
14 KiB
490 lines
14 KiB
let Problem = syzoj.model('problem'); |
|
let JudgeState = syzoj.model('judge_state'); |
|
let Article = syzoj.model('article'); |
|
let Contest = syzoj.model('contest'); |
|
let User = syzoj.model('user'); |
|
let UserPrivilege = syzoj.model('user_privilege'); |
|
const RatingCalculation = syzoj.model('rating_calculation'); |
|
const RatingHistory = syzoj.model('rating_history'); |
|
let ContestPlayer = syzoj.model('contest_player'); |
|
const calcRating = require('../libs/rating'); |
|
|
|
app.get('/admin/info', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
let allSubmissionsCount = await JudgeState.count(); |
|
let todaySubmissionsCount = await JudgeState.count({ |
|
submit_time: TypeORM.MoreThanOrEqual(syzoj.utils.getCurrentDate(true)) |
|
}); |
|
let problemsCount = await Problem.count(); |
|
let articlesCount = await Article.count(); |
|
let contestsCount = await Contest.count(); |
|
let usersCount = await User.count(); |
|
|
|
res.render('admin_info', { |
|
allSubmissionsCount: allSubmissionsCount, |
|
todaySubmissionsCount: todaySubmissionsCount, |
|
problemsCount: problemsCount, |
|
articlesCount: articlesCount, |
|
contestsCount: contestsCount, |
|
usersCount: usersCount |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
let configItems = { |
|
'title': { name: '站点标题', type: String }, |
|
'google_analytics': { name: 'Google Analytics', type: String }, |
|
'默认参数': null, |
|
'default.problem.time_limit': { name: '时间限制(单位:ms)', type: Number }, |
|
'default.problem.memory_limit': { name: '空间限制(单位:MiB)', type: Number }, |
|
'限制': null, |
|
'limit.time_limit': { name: '最大时间限制(单位:ms)', type: Number }, |
|
'limit.memory_limit': { name: '最大空间限制(单位:MiB)', type: Number }, |
|
'limit.data_size': { name: '所有数据包大小(单位:byte)', type: Number }, |
|
'limit.testdata': { name: '测试数据大小(单位:byte)', type: Number }, |
|
'limit.submit_code': { name: '代码长度(单位:byte)', type: Number }, |
|
'limit.submit_answer': { name: '提交答案题目答案大小(单位:byte)', type: Number }, |
|
'limit.custom_test_input': { name: '自定义测试输入文件大小(单位:byte)', type: Number }, |
|
'limit.testdata_filecount': { name: '测试数据文件数量(单位:byte)', type: Number }, |
|
'每页显示数量': null, |
|
'page.problem': { name: '题库', type: Number }, |
|
'page.judge_state': { name: '提交记录', type: Number }, |
|
'page.problem_statistics': { name: '题目统计', type: Number }, |
|
'page.ranklist': { name: '排行榜', type: Number }, |
|
'page.discussion': { name: '讨论', type: Number }, |
|
'page.article_comment': { name: '评论', type: Number }, |
|
'page.contest': { name: '比赛', type: Number } |
|
}; |
|
|
|
app.get('/admin/config', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
for (let i in configItems) { |
|
if (!configItems[i]) continue; |
|
configItems[i].val = eval(`syzoj.config.${i}`); |
|
} |
|
|
|
res.render('admin_config', { |
|
items: configItems |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.post('/admin/config', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
for (let i in configItems) { |
|
if (!configItems[i]) continue; |
|
if (req.body[i]) { |
|
let val; |
|
if (configItems[i].type === Boolean) { |
|
val = req.body[i] === 'on'; |
|
} else if (configItems[i].type === Number) { |
|
val = Number(req.body[i]); |
|
} else { |
|
val = req.body[i]; |
|
} |
|
|
|
let f = new Function('val', `syzoj.config.${i} = val`); |
|
f(val); |
|
} |
|
} |
|
|
|
await syzoj.utils.saveConfig(); |
|
|
|
res.redirect(syzoj.utils.makeUrl(['admin', 'config'])); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.get('/admin/privilege', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
let a = await UserPrivilege.find(); |
|
let users = {}; |
|
for (let p of a) { |
|
if (!users[p.user_id]) { |
|
users[p.user_id] = { |
|
user: await User.findById(p.user_id), |
|
privileges: [] |
|
}; |
|
} |
|
|
|
users[p.user_id].privileges.push(p.privilege); |
|
} |
|
|
|
res.render('admin_privilege', { |
|
users: Object.values(users) |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.post('/admin/privilege', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
let data = JSON.parse(req.body.data); |
|
for (let id in data) { |
|
let user = await User.findById(id); |
|
if (!user) throw new ErrorMessage(`不存在 ID 为 ${id} 的用户。`); |
|
await user.setPrivileges(data[id]); |
|
} |
|
|
|
res.redirect(syzoj.utils.makeUrl(['admin', 'privilege'])); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.get('/admin/rating', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
const contests = await Contest.find({ |
|
order: { |
|
start_time: 'DESC' |
|
} |
|
}); |
|
const calcs = await RatingCalculation.find({ |
|
order: { |
|
id: 'DESC' |
|
} |
|
}); |
|
for (const calc of calcs) await calc.loadRelationships(); |
|
|
|
res.render('admin_rating', { |
|
contests: contests, |
|
calcs: calcs |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.post('/admin/rating/add', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
const contest = await Contest.findById(req.body.contest); |
|
if (!contest) throw new ErrorMessage('无此比赛'); |
|
|
|
await contest.loadRelationships(); |
|
const newcalc = await RatingCalculation.create({ contest_id: contest.id }); |
|
await newcalc.save(); |
|
|
|
if (!contest.ranklist || contest.ranklist.ranklist.player_num <= 1) { |
|
throw new ErrorMessage("比赛人数太少。"); |
|
} |
|
|
|
const players = []; |
|
for (let i = 1; i <= contest.ranklist.ranklist.player_num; i++) { |
|
const user = await User.findById((await ContestPlayer.findById(contest.ranklist.ranklist[i])).user_id); |
|
players.push({ |
|
user: user, |
|
rank: i, |
|
currentRating: user.rating |
|
}); |
|
} |
|
const newRating = calcRating(players); |
|
for (let i = 0; i < newRating.length; i++) { |
|
const user = newRating[i].user; |
|
user.rating = newRating[i].currentRating; |
|
await user.save(); |
|
const newHistory = await RatingHistory.create({ |
|
rating_calculation_id: newcalc.id, |
|
user_id: user.id, |
|
rating_after: newRating[i].currentRating, |
|
rank: newRating[i].rank |
|
}); |
|
await newHistory.save(); |
|
} |
|
|
|
res.redirect(syzoj.utils.makeUrl(['admin', 'rating'])); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}); |
|
} |
|
}); |
|
|
|
app.post('/admin/rating/delete', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
const calcList = await RatingCalculation.find({ |
|
where: { |
|
id: TypeORM.MoreThanOrEqual(req.body.calc_id) |
|
}, |
|
order: { |
|
id: 'DESC' |
|
} |
|
}); |
|
if (calcList.length === 0) throw new ErrorMessage('ID 不正确'); |
|
|
|
for (let i = 0; i < calcList.length; i++) { |
|
await calcList[i].delete(); |
|
} |
|
|
|
res.redirect(syzoj.utils.makeUrl(['admin', 'rating'])); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}); |
|
} |
|
}); |
|
|
|
app.get('/admin/other', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
res.render('admin_other'); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.get('/admin/rejudge', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
res.render('admin_rejudge', { |
|
form: {}, |
|
count: null |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.post('/admin/other', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
if (req.body.type === 'reset_count') { |
|
const problems = await Problem.find(); |
|
for (const p of problems) { |
|
await p.resetSubmissionCount(); |
|
} |
|
} else if (req.body.type === 'reset_discussion') { |
|
const articles = await Article.find(); |
|
for (const a of articles) { |
|
await a.resetReplyCountAndTime(); |
|
} |
|
} else if (req.body.type === 'reset_codelen') { |
|
const submissions = await JudgeState.find(); |
|
for (const s of submissions) { |
|
if (s.type !== 'submit-answer') { |
|
s.code_length = Buffer.from(s.code).length; |
|
await s.save(); |
|
} |
|
} |
|
} else { |
|
throw new ErrorMessage("操作类型不正确"); |
|
} |
|
|
|
res.redirect(syzoj.utils.makeUrl(['admin', 'other'])); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
app.post('/admin/rejudge', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
let query = JudgeState.createQueryBuilder(); |
|
|
|
let user = await User.fromName(req.body.submitter || ''); |
|
if (user) { |
|
query.andWhere('user_id = :user_id', { user_id: user.id }); |
|
} else if (req.body.submitter) { |
|
query.andWhere('user_id = :user_id', { user_id: 0 }); |
|
} |
|
|
|
let minID = parseInt(req.body.min_id); |
|
if (!isNaN(minID)) query.andWhere('id >= :minID', { minID }) |
|
let maxID = parseInt(req.body.max_id); |
|
if (!isNaN(maxID)) query.andWhere('id <= :maxID', { maxID }) |
|
|
|
let minScore = parseInt(req.body.min_score); |
|
if (!isNaN(minScore)) query.andWhere('score >= :minScore', { minScore }); |
|
let maxScore = parseInt(req.body.max_score); |
|
if (!isNaN(maxScore)) query.andWhere('score <= :maxScore', { maxScore }); |
|
|
|
let minTime = syzoj.utils.parseDate(req.body.min_time); |
|
if (!isNaN(minTime)) query.andWhere('submit_time >= :minTime', { minTime: parseInt(minTime) }); |
|
let maxTime = syzoj.utils.parseDate(req.body.max_time); |
|
if (!isNaN(maxTime)) query.andWhere('submit_time <= :maxTime', { maxTime: parseInt(maxTime) }); |
|
|
|
if (req.body.language) { |
|
if (req.body.language === 'submit-answer') { |
|
query.andWhere(new TypeORM.Brackets(qb => { |
|
qb.orWhere('language = :language', { language: '' }) |
|
.orWhere('language IS NULL'); |
|
})); |
|
} else if (req.body.language === 'non-submit-answer') { |
|
query.andWhere('language != :language', { language: '' }) |
|
.andWhere('language IS NOT NULL');; |
|
} else { |
|
query.andWhere('language = :language', { language: req.body.language }); |
|
} |
|
} |
|
|
|
if (req.body.status) { |
|
query.andWhere('status = :status', { status: req.body.status }); |
|
} |
|
|
|
if (req.body.problem_id) { |
|
query.andWhere('problem_id = :problem_id', { problem_id: parseInt(req.body.problem_id) || 0 }) |
|
} |
|
|
|
let count = await JudgeState.countQuery(query); |
|
if (req.body.type === 'rejudge') { |
|
let submissions = await JudgeState.queryAll(query); |
|
for (let submission of submissions) { |
|
await submission.rejudge(); |
|
} |
|
} |
|
|
|
res.render('admin_rejudge', { |
|
form: req.body, |
|
count: count |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.get('/admin/links', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
res.render('admin_links', { |
|
links: syzoj.config.links || [] |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.post('/admin/links', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
syzoj.config.links = JSON.parse(req.body.data); |
|
await syzoj.utils.saveConfig(); |
|
|
|
res.redirect(syzoj.utils.makeUrl(['admin', 'links'])); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.get('/admin/raw', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
res.render('admin_raw', { |
|
data: JSON.stringify(syzoj.config, null, 2) |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.post('/admin/raw', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
syzoj.config = JSON.parse(req.body.data); |
|
await syzoj.utils.saveConfig(); |
|
|
|
res.redirect(syzoj.utils.makeUrl(['admin', 'raw'])); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.post('/admin/restart', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
syzoj.restart(); |
|
|
|
res.render('admin_restart', { |
|
data: JSON.stringify(syzoj.config, null, 2) |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
}); |
|
|
|
app.get('/admin/serviceID', async (req, res) => { |
|
try { |
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
|
|
|
res.send({ |
|
serviceID: syzoj.serviceID |
|
}); |
|
} catch (e) { |
|
syzoj.log(e); |
|
res.render('error', { |
|
err: e |
|
}) |
|
} |
|
});
|
|
|