core: allow pin training

This commit is contained in:
undefined committed 2021-06-18 19:04:48 +08:00
1 parent 6607a36856
commit a7f364e651
10 files changed
+51 -25

No files matched your search

+17 -8
View File
@@ -35,12 +35,14 @@ if (!cluster.isMaster) {
return child.spawn('mongo', [url], { stdio: 'inherit' }); return child.spawn('mongo', [url], { stdio: 'inherit' });
} }
try { if (!addons.includes('@hydrooj/ui-default')) {
const ui = argv.options.ui || '@hydrooj/ui-default'; try {
require.resolve(ui); const ui = argv.options.ui || '@hydrooj/ui-default';
addons.push(ui); require.resolve(ui);
} catch (e) { addons.push(ui);
console.error('Please also install @hydrooj/ui-default'); } catch (e) {
console.error('Please also install @hydrooj/ui-default');
}
} }
if (argv.args[0] && argv.args[0] !== 'cli') { if (argv.args[0] && argv.args[0] !== 'cli') {
@@ -55,8 +57,15 @@ if (!cluster.isMaster) {
fs.mkdirSync('/root/addon/locales'); fs.mkdirSync('/root/addon/locales');
fs.mkdirSync('/root/addon/public'); fs.mkdirSync('/root/addon/public');
addons.push('/root/addon'); addons.push('/root/addon');
} else if (arg1 === 'add') addons.push(arg2); } else if (arg1 === 'add') {
else if (arg1 === 'remove') { for (let i = 0; i < addons.length; i++) {
if (addons[i] === arg2) {
addons.splice(i, 1);
break;
}
}
addons.push(arg2);
} else if (arg1 === 'remove') {
for (let i = 0; i < addons.length; i++) { for (let i = 0; i < addons.length; i++) {
if (addons[i] === arg2) { if (addons[i] === arg2) {
addons.splice(i, 1); addons.splice(i, 1);
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.28.37", "version": "2.28.38",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "dist/loader.js", "main": "dist/loader.js",
"typings": "dist/loader.d.ts", "typings": "dist/loader.d.ts",
+9 -4
View File
@@ -181,11 +181,13 @@ class TrainingCreateHandler extends Handler {
@param('title', Types.Title) @param('title', Types.Title)
@param('content', Types.Content) @param('content', Types.Content)
@param('dag', Types.String, isContent) @param('dag', Types.String, isContent)
@param('pin', Types.Boolean)
@param('description', Types.String, isDescription) @param('description', Types.String, isDescription)
async post( async post(
domainId: string, title: string, content: string, domainId: string, title: string, content: string,
_dag: string, description: string, _dag: string, pin = false, description: string,
) { ) {
if (pin) this.checkPerm(PERM.PERM_PIN_TRAINING);
const dag = await _parseDagJson(domainId, _dag); const dag = await _parseDagJson(domainId, _dag);
const pids = training.getPids(dag); const pids = training.getPids(dag);
assert(pids.length, new ValidationError('dag')); assert(pids.length, new ValidationError('dag'));
@@ -194,13 +196,14 @@ class TrainingCreateHandler extends Handler {
if (pdoc.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN); if (pdoc.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
} }
const tid = await training.add(domainId, title, content, this.user._id, dag, description); const tid = await training.add(domainId, title, content, this.user._id, dag, description);
if (pin) await training.edit(domainId, tid, { pin });
this.response.body = { tid }; this.response.body = { tid };
this.response.redirect = this.url('training_detail', { tid }); this.response.redirect = this.url('training_detail', { tid });
} }
} }
class TrainingEditHandler extends Handler { class TrainingEditHandler extends Handler {
tdoc: Tdoc; tdoc: TrainingDoc;
@param('tid', Types.ObjectID) @param('tid', Types.ObjectID)
async prepare(domainId: string, tid: ObjectID) { async prepare(domainId: string, tid: ObjectID) {
@@ -227,12 +230,14 @@ class TrainingEditHandler extends Handler {
@param('title', Types.Title) @param('title', Types.Title)
@param('content', Types.Content) @param('content', Types.Content)
@param('dag', Types.Content) @param('dag', Types.Content)
@param('pin', Types.Boolean)
@param('description', Types.String, isDescription) @param('description', Types.String, isDescription)
async post( async post(
domainId: string, tid: ObjectID, domainId: string, tid: ObjectID,
title: string, content: string, title: string, content: string,
_dag: string, description: string, _dag: string, pin = false, description: string,
) { ) {
if (this.tdoc.pin !== pin) this.checkPerm(PERM.PERM_PIN_TRAINING);
const dag = await _parseDagJson(domainId, _dag); const dag = await _parseDagJson(domainId, _dag);
const pids = training.getPids(dag); const pids = training.getPids(dag);
assert(pids.length, new ValidationError('dag')); assert(pids.length, new ValidationError('dag'));
@@ -257,7 +262,7 @@ class TrainingEditHandler extends Handler {
if (pdoc.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN); if (pdoc.hidden) this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
} }
await training.edit(domainId, tid, { await training.edit(domainId, tid, {
title, content, dag, description, title, content, dag, description, pin,
}); });
this.response.body = { tid }; this.response.body = { tid };
this.response.redirect = this.url('training_detail', { tid }); this.response.redirect = this.url('training_detail', { tid });
+3 -2
View File
@@ -326,8 +326,9 @@ export interface Tdoc<docType = document['TYPE_CONTEST'] | document['TYPE_HOMEWO
} }
export interface TrainingDoc extends Tdoc { export interface TrainingDoc extends Tdoc {
description: string, description: string;
dag: TrainingNode[], pin?: boolean;
dag: TrainingNode[];
} }
export interface DomainDoc extends Record<string, any> { export interface DomainDoc extends Record<string, any> {
+1
View File
@@ -85,6 +85,7 @@ export const PERM = {
PERM_VIEW_TRAINING: 1n << 46n, PERM_VIEW_TRAINING: 1n << 46n,
PERM_CREATE_TRAINING: 1n << 47n, PERM_CREATE_TRAINING: 1n << 47n,
PERM_EDIT_TRAINING: 1n << 48n, PERM_EDIT_TRAINING: 1n << 48n,
PERM_PIN_TRAINING: 1n << 63n,
PERM_EDIT_TRAINING_SELF: 1n << 49n, PERM_EDIT_TRAINING_SELF: 1n << 49n,
// Ranking // Ranking
+1 -1
View File
@@ -114,7 +114,7 @@ export async function getList(domainId: string, tids: ObjectID[]) {
} }
export const getMulti = (domainId: string, query: FilterQuery<TrainingDoc> = {}) => export const getMulti = (domainId: string, query: FilterQuery<TrainingDoc> = {}) =>
document.getMulti(domainId, document.TYPE_TRAINING, query).sort('_id', 1); document.getMulti(domainId, document.TYPE_TRAINING, query).sort({ pin: -1, _id: -1 });
global.Hydro.model.training = { global.Hydro.model.training = {
getPids, getPids,
+9 -7
View File
@@ -84,13 +84,15 @@ export async function updateJudge(args) {
if (cluster.isMaster) { if (cluster.isMaster) {
bus.on('app/started', async () => { bus.on('app/started', async () => {
sysinfo.get().then((info) => coll.updateOne( sysinfo.get().then((info) => {
{ mid: info.mid, type: 'server' }, coll.updateOne(
{ $set: { ...info, updateAt: new Date(), type: 'server' } }, { mid: info.mid, type: 'server' },
{ upsert: true }, { $set: { ...info, updateAt: new Date(), type: 'server' } },
)); { upsert: true },
feedback(); );
setInterval(update, 60 * 1000); feedback();
setInterval(update, 60 * 1000);
});
}); });
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/ui-default", "name": "@hydrooj/ui-default",
"version": "4.11.24", "version": "4.11.25",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "hydro.js", "main": "hydro.js",
@@ -45,7 +45,7 @@
{%- for role in roles -%} {%- for role in roles -%}
<td class="col--p"> <td class="col--p">
<label class="compact checkbox"> <label class="compact checkbox">
<input name="{{ role._id }}" data-checkbox-group="{{ role._id }}" data-checkbox-range value="100" type="checkbox" checked{% if role._id == 'admin' %} disabled{% endif %}> <input name="{{ role._id }}" data-checkbox-group="{{ role._id }}" data-checkbox-range value="1000" type="checkbox" checked{% if role._id == 'admin' %} disabled{% endif %}>
</label> </label>
</td> </td>
{%- endfor -%} {%- endfor -%}
@@ -12,6 +12,14 @@
<input name="title" placeholder="{{ _('title') }}" value="{{ tdoc.title|default('') }}" class="textbox" autofocus> <input name="title" placeholder="{{ _('title') }}" value="{{ tdoc.title|default('') }}" class="textbox" autofocus>
</label> </label>
</div> </div>
<div class="medium-2 columns">
<label>
<br>
<label class="checkbox">
<input type="checkbox" name="pin" value="on"{% if tdoc['hidden'] %} checked{% endif %}>{{ _('Pin') }}
</label>
</label>
</div>
</div> </div>
<div class="row"><div class="columns"> <div class="row"><div class="columns">
<label> <label>