mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
Hello Typescript!
This commit is contained in:
1 parent
c9b6412527
commit
ba03d7a7bf
98 files changed
+3186
-2616
No files matched your search
@@ -1,24 +1,24 @@
|
||||
const db = require('../service/db');
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('blacklist');
|
||||
|
||||
async function add(ip) {
|
||||
export async function add(ip: string) {
|
||||
const expireAt = new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000);
|
||||
return coll.findOneAndUpdate({ _id: ip }, { $set: { expireAt } }, { upsert: true });
|
||||
}
|
||||
|
||||
function get(ip) {
|
||||
export function get(ip: string) {
|
||||
return coll.findOne({ _id: ip });
|
||||
}
|
||||
|
||||
function del(ip) {
|
||||
export function del(ip: string) {
|
||||
return coll.deleteOne({ _id: ip });
|
||||
}
|
||||
|
||||
function ensureIndexes() {
|
||||
export function ensureIndexes() {
|
||||
return coll.createIndex('expireAt', { expireAfterSeconds: 0 });
|
||||
}
|
||||
|
||||
global.Hydro.model.blacklist = module.exports = {
|
||||
global.Hydro.model.blacklist = {
|
||||
add, get, del, ensureIndexes,
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
const PERM = {
|
||||
export const PERM = {
|
||||
PERM_NONE: '',
|
||||
|
||||
// Domain Settings
|
||||
@@ -30,8 +30,10 @@ const PERM = {
|
||||
PERM_REPLY_PROBLEM_SOLUTION: 'L',
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY: 'M',
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF: 'N',
|
||||
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF_SOLUTION: 'x',
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY: 'O',
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF: 'P',
|
||||
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF_SOLUTION: 'y',
|
||||
|
||||
// Discussion
|
||||
PERM_VIEW_DISCUSSION: 'Q',
|
||||
@@ -75,11 +77,17 @@ const PERM = {
|
||||
|
||||
// Ranking
|
||||
PERM_VIEW_RANKING: 'w',
|
||||
|
||||
// Placeholder
|
||||
PERM_ALL: '',
|
||||
PERM_BASIC: '',
|
||||
PERM_DEFAULT: '',
|
||||
PERM_ADMIN: '',
|
||||
};
|
||||
|
||||
const Permission = (family, key, desc) => ({ family, key, desc });
|
||||
export const Permission = (family, key, desc) => ({ family, key, desc });
|
||||
|
||||
const PERMS = [
|
||||
export const PERMS = [
|
||||
Permission('perm_general', PERM.PERM_VIEW, 'View this domain'),
|
||||
Permission('perm_general', PERM.PERM_EDIT_DOMAIN, 'Edit domain settings'),
|
||||
Permission('perm_general', PERM.PERM_MOD_BADGE, 'Show MOD badge'),
|
||||
@@ -142,10 +150,10 @@ const PERMS = [
|
||||
];
|
||||
|
||||
let PERM_ALL = '';
|
||||
for (const p in PERM) PERM_ALL += PERM[p];
|
||||
for (const p in PERM) if (PERM[p]) PERM_ALL += PERM[p];
|
||||
PERM.PERM_ALL = PERM_ALL;
|
||||
|
||||
const PERMS_BY_FAMILY = {};
|
||||
export const PERMS_BY_FAMILY = {};
|
||||
for (const p of PERMS) {
|
||||
if (!PERMS_BY_FAMILY[p.family]) PERMS_BY_FAMILY[p.family] = [p];
|
||||
else PERMS_BY_FAMILY[p.family].push(p);
|
||||
@@ -208,7 +216,7 @@ PERM.PERM_DEFAULT = PERM.PERM_VIEW
|
||||
|
||||
PERM.PERM_ADMIN = PERM.PERM_ALL;
|
||||
|
||||
const PRIV = {
|
||||
export const PRIV = {
|
||||
PRIV_NONE: 0,
|
||||
PRIV_EDIT_SYSTEM: 1 << 0, // renamed from PRIV_SET_PRIV
|
||||
PRIV_SET_PERM: 1 << 1,
|
||||
@@ -231,18 +239,19 @@ const PRIV = {
|
||||
PRIV_DELETE_FILE: 1 << 18,
|
||||
PRIV_DELETE_FILE_SELF: 1 << 19,
|
||||
PRIV_ALL: -1,
|
||||
|
||||
PRIV_DEFAULT: 0,
|
||||
};
|
||||
|
||||
PRIV.PRIV_DETAULT = PRIV.PRIV_USER_PROFILE
|
||||
PRIV.PRIV_DEFAULT = PRIV.PRIV_USER_PROFILE
|
||||
+ PRIV.PRIV_REGISTER_USER
|
||||
+ PRIV.PRIV_READ_PRETEST_DATA_SELF
|
||||
+ PRIV.PRIV_CREATE_DOMAIN
|
||||
+ PRIV.PRIV_CREATE_FILE
|
||||
+ PRIV.PRIV_DELETE_FILE_SELF
|
||||
+ PRIV.PRIV_USERFILE;
|
||||
+ PRIV.PRIV_DELETE_FILE_SELF;
|
||||
|
||||
// [10, 1] means that people whose rank is less than 1% will get Level 10
|
||||
const LEVELS = [
|
||||
export const LEVELS = [
|
||||
[10, 1],
|
||||
[9, 2],
|
||||
[8, 10],
|
||||
@@ -255,7 +264,7 @@ const LEVELS = [
|
||||
[1, 100],
|
||||
];
|
||||
|
||||
const BUILTIN_USERS = [
|
||||
export const BUILTIN_USERS = [
|
||||
{
|
||||
_id: 0,
|
||||
mail: 'Guest@hydro.local',
|
||||
@@ -288,13 +297,13 @@ const BUILTIN_USERS = [
|
||||
},
|
||||
];
|
||||
|
||||
const BUILTIN_ROLES = {
|
||||
export const BUILTIN_ROLES = {
|
||||
guest: { perm: PERM.PERM_BASIC },
|
||||
default: { perm: PERM.PERM_DEFAULT },
|
||||
admin: { perm: PERM.PERM_ADMIN },
|
||||
};
|
||||
|
||||
const DEFAULT_NODES = {
|
||||
export const DEFAULT_NODES = {
|
||||
探索: [
|
||||
{ pic: 'qa', name: '问答' },
|
||||
{ pic: 'share', name: '分享' },
|
||||
@@ -353,7 +362,7 @@ const DEFAULT_NODES = {
|
||||
],
|
||||
};
|
||||
|
||||
const CATEGORIES = {
|
||||
export const CATEGORIES = {
|
||||
动态规划: [
|
||||
'LCS',
|
||||
'LIS',
|
||||
@@ -490,15 +499,15 @@ const CATEGORIES = {
|
||||
],
|
||||
};
|
||||
|
||||
const FOOTER_EXTRA_HTMLS = [];
|
||||
export const FOOTER_EXTRA_HTMLS = [];
|
||||
|
||||
const VIEW_LANGS = [
|
||||
export const VIEW_LANGS = [
|
||||
{ code: 'zh_CN', name: '简体中文' },
|
||||
{ code: 'zh_TW', name: '正體中文' },
|
||||
{ code: 'en', name: 'English' },
|
||||
];
|
||||
|
||||
const LANG_TEXTS = {
|
||||
export const LANG_TEXTS = {
|
||||
c: 'C',
|
||||
cc: 'C++',
|
||||
cs: 'C#',
|
||||
@@ -514,7 +523,7 @@ const LANG_TEXTS = {
|
||||
rb: 'Ruby',
|
||||
};
|
||||
|
||||
const LANG_HIGHLIGHT_ID = {
|
||||
export const LANG_HIGHLIGHT_ID = {
|
||||
c: 'c',
|
||||
cc: 'cpp',
|
||||
cs: 'csharp',
|
||||
@@ -530,7 +539,7 @@ const LANG_HIGHLIGHT_ID = {
|
||||
rb: 'ruby',
|
||||
};
|
||||
|
||||
const STATUS = {
|
||||
export const STATUS = {
|
||||
STATUS_WAITING: 0,
|
||||
STATUS_ACCEPTED: 1,
|
||||
STATUS_WRONG_ANSWER: 2,
|
||||
@@ -548,7 +557,7 @@ const STATUS = {
|
||||
STATUS_IGNORED: 30,
|
||||
};
|
||||
|
||||
const STATUS_TEXTS = {
|
||||
export const STATUS_TEXTS = {
|
||||
[STATUS.STATUS_WAITING]: 'Waiting',
|
||||
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
||||
[STATUS.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
||||
@@ -566,7 +575,7 @@ const STATUS_TEXTS = {
|
||||
[STATUS.STATUS_IGNORED]: 'Ignored',
|
||||
};
|
||||
|
||||
const STATUS_CODES = {
|
||||
export const STATUS_CODES = {
|
||||
0: 'pending',
|
||||
1: 'pass',
|
||||
2: 'fail',
|
||||
@@ -584,7 +593,22 @@ const STATUS_CODES = {
|
||||
30: 'ignored',
|
||||
};
|
||||
|
||||
global.Hydro.model.builtin = module.exports = {
|
||||
export const USER_GENDER_MALE = 0;
|
||||
export const USER_GENDER_FEMALE = 1;
|
||||
export const USER_GENDER_OTHER = 2;
|
||||
export const USER_GENDERS = [USER_GENDER_MALE, USER_GENDER_FEMALE, USER_GENDER_OTHER];
|
||||
export const USER_GENDER_RANGE = {
|
||||
[USER_GENDER_MALE]: 'Boy ♂',
|
||||
[USER_GENDER_FEMALE]: 'Girl ♀',
|
||||
[USER_GENDER_OTHER]: 'Other',
|
||||
};
|
||||
export const USER_GENDER_ICONS = {
|
||||
[USER_GENDER_MALE]: '♂',
|
||||
[USER_GENDER_FEMALE]: '♀',
|
||||
[USER_GENDER_OTHER]: '?',
|
||||
};
|
||||
|
||||
global.Hydro.model.builtin = {
|
||||
PERM,
|
||||
PERMS,
|
||||
PERMS_BY_FAMILY,
|
||||
@@ -601,4 +625,10 @@ global.Hydro.model.builtin = module.exports = {
|
||||
STATUS,
|
||||
STATUS_TEXTS,
|
||||
STATUS_CODES,
|
||||
USER_GENDER_MALE,
|
||||
USER_GENDER_FEMALE,
|
||||
USER_GENDER_OTHER,
|
||||
USER_GENDERS,
|
||||
USER_GENDER_RANGE,
|
||||
USER_GENDER_ICONS,
|
||||
};
|
||||
@@ -1,28 +1,51 @@
|
||||
const user = require('./user');
|
||||
const problem = require('./problem');
|
||||
const {
|
||||
import { ObjectID } from 'mongodb';
|
||||
import * as user from './user';
|
||||
import * as problem from './problem';
|
||||
import {
|
||||
ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
|
||||
ContestNotAttendedError, ContestScoreboardHiddenError,
|
||||
} = require('../error');
|
||||
const { PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('./builtin').PERM;
|
||||
const validator = require('../lib/validator');
|
||||
const ranked = require('../lib/rank');
|
||||
const document = require('./document');
|
||||
} from '../error';
|
||||
import * as document from './document';
|
||||
import { PERM } from './builtin';
|
||||
import * as validator from '../lib/validator';
|
||||
import * as misc from '../lib/misc';
|
||||
import ranked from '../lib/rank';
|
||||
|
||||
interface Column {
|
||||
type: string,
|
||||
value: string,
|
||||
raw?: any
|
||||
}
|
||||
|
||||
export interface Tdoc {
|
||||
_id: ObjectID,
|
||||
domainId: string,
|
||||
docId: ObjectID,
|
||||
docType: number,
|
||||
beginAt: Date,
|
||||
endAt: Date,
|
||||
penaltySince?: Date,
|
||||
attend: number,
|
||||
title: string,
|
||||
content: string,
|
||||
rule: string,
|
||||
pids: number[]
|
||||
}
|
||||
|
||||
const acm = {
|
||||
TEXT: 'ACM/ICPC',
|
||||
check: () => { },
|
||||
showScoreboard: () => true,
|
||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||
stat: (tdoc, journal) => {
|
||||
stat: (tdoc, journal: any[]) => {
|
||||
const naccept = {};
|
||||
const effective = {};
|
||||
const detail = [];
|
||||
let accept = 0;
|
||||
let time = 0;
|
||||
for (const j in journal) {
|
||||
for (const j of journal) {
|
||||
if (tdoc.pids.includes(j.pid)
|
||||
&& !(effective.includes(j.pid) && effective[j.pid].accept)) {
|
||||
&& !(effective[j.pid] && effective[j.pid].accept)) {
|
||||
effective[j.pid] = j;
|
||||
}
|
||||
if (!j.accept) naccept[j.pid]++;
|
||||
@@ -32,15 +55,19 @@ const acm = {
|
||||
const penalty = 20 * 60 * naccept[jdoc.pid];
|
||||
return real + penalty;
|
||||
}
|
||||
for (const j of effective) detail.push({ ...j, naccept: naccept[j.pid], time: _time(j) });
|
||||
for (const j in effective) {
|
||||
detail.push({
|
||||
...effective[j], naccept: naccept[effective[j].pid], time: _time(effective[j]),
|
||||
});
|
||||
}
|
||||
for (const d of detail) {
|
||||
accept += d.accept;
|
||||
if (d.accept) time += d.time;
|
||||
}
|
||||
return { accept, time, detail };
|
||||
},
|
||||
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||
const columns = [
|
||||
scoreboard(isExport: boolean, _: (s: string) => string, tdoc, rankedTsdocs, udict, pdict) {
|
||||
const columns: Column[] = [
|
||||
{ type: 'rank', value: _('Rank') },
|
||||
{ type: 'user', value: _('User') },
|
||||
{ type: 'solved_problems', value: _('Solved Problems') },
|
||||
@@ -130,7 +157,7 @@ const oi = {
|
||||
stat: (tdoc, journal) => {
|
||||
const detail = {};
|
||||
let score = 0;
|
||||
for (const j in journal) {
|
||||
for (const j of journal) {
|
||||
if (tdoc.pids.includes(j.pid)) {
|
||||
detail[j.pid] = j;
|
||||
score += j.score;
|
||||
@@ -145,7 +172,7 @@ const oi = {
|
||||
return now > tdoc.endAt;
|
||||
},
|
||||
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||
const columns = [
|
||||
const columns: Column[] = [
|
||||
{ type: 'rank', value: _('Rank') },
|
||||
{ type: 'user', value: _('User') },
|
||||
{ type: 'total_score', value: _('Total Score') },
|
||||
@@ -186,30 +213,156 @@ const oi = {
|
||||
rank: (tdocs) => ranked(tdocs, (a, b) => a.score === b.score),
|
||||
};
|
||||
|
||||
const RULES = {
|
||||
acm, oi,
|
||||
const homework = {
|
||||
TEXT: 'Assignment',
|
||||
check: () => { },
|
||||
stat: (tdoc, journal) => {
|
||||
const effective = {};
|
||||
for (const j of journal) {
|
||||
if (tdoc.pids.includes(j.pid)
|
||||
&& !effective[j.pid]
|
||||
&& journal[j.pid].accept) {
|
||||
effective[j.pid] = j;
|
||||
}
|
||||
}
|
||||
function time(jdoc) {
|
||||
const real = jdoc.rid.generationTime - tdoc.beginAt.getTime() / 1000;
|
||||
return Math.floor(real);
|
||||
}
|
||||
function penaltyScore(jdoc) {
|
||||
const { score } = jdoc;
|
||||
const exceedSeconds = Math.floor(
|
||||
jdoc.rid.generationTime - tdoc.penaltySince.getTime() / 1000,
|
||||
);
|
||||
if (exceedSeconds < 0) return score;
|
||||
let coefficient = 1;
|
||||
const keys = Object.keys(tdoc.penaltyRules)
|
||||
.map((k) => parseFloat(k)).sort((a, b) => a - b);
|
||||
for (const i of keys) {
|
||||
if (i * 3600 <= exceedSeconds) coefficient = tdoc.penaltyRules[i];
|
||||
else break;
|
||||
}
|
||||
return score * coefficient;
|
||||
}
|
||||
const detail = [];
|
||||
for (const j in effective) {
|
||||
detail.push({
|
||||
...effective[j],
|
||||
penaltyScore: penaltyScore(effective[j]),
|
||||
time: time(j),
|
||||
});
|
||||
}
|
||||
return {
|
||||
score: Math.sum(detail.map((d) => d.score)),
|
||||
penaltyScore: Math.sum(detail.map((d) => d.penaltySince)),
|
||||
time: Math.sum(detail.map((d) => d.time)),
|
||||
detail,
|
||||
};
|
||||
},
|
||||
showScoreboard: () => true,
|
||||
showRecord: () => true,
|
||||
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||
const columns: Column[] = [
|
||||
{ type: 'rank', value: _('Rank') },
|
||||
{ type: 'user', value: _('User') },
|
||||
{ type: 'total_score', value: _('Score') },
|
||||
];
|
||||
if (isExport) {
|
||||
columns.push(
|
||||
{ type: 'total_original_score', value: _('Original Score') },
|
||||
{ type: 'total_time', value: _('Total Time (Seconds)') },
|
||||
);
|
||||
}
|
||||
columns.push({ type: 'total_time_str', value: _('Total Time') });
|
||||
for (const index in tdoc.pids) {
|
||||
const pid = tdoc.pids[index];
|
||||
if (isExport) {
|
||||
columns.push(
|
||||
{
|
||||
type: 'problem_score',
|
||||
value: '#{0} {1}'.format(index + 1, pdict[pid].title),
|
||||
},
|
||||
{
|
||||
type: 'problem_original_score',
|
||||
value: '#{0} {1}'.format(index + 1, _('Original Score')),
|
||||
},
|
||||
{
|
||||
type: 'problem_time',
|
||||
value: '#{0} {1}'.format(index + 1, _('Time (Seconds)')),
|
||||
},
|
||||
{
|
||||
type: 'problem_time_str',
|
||||
value: '#{0} {1}'.format(index + 1, _('Time')),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
columns.push({
|
||||
type: 'problem_detail',
|
||||
value: '#{0}'.format(index + 1),
|
||||
raw: pdict[pid],
|
||||
});
|
||||
}
|
||||
}
|
||||
const rows = [columns];
|
||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||
const tsddict = {};
|
||||
for (const item of tsdoc.journal || []) {
|
||||
tsddict[item.pid] = item;
|
||||
}
|
||||
const row = [
|
||||
{ type: 'string', value: rank },
|
||||
{
|
||||
type: 'user',
|
||||
value: udict[tsdoc.uid].uname,
|
||||
raw: udict[tsdoc.uid],
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
value: tsdoc.penaltyScore || 0,
|
||||
},
|
||||
];
|
||||
if (isExport) {
|
||||
row.push({ type: 'string', value: tsdoc.score || 0 });
|
||||
row.push({ type: 'string', value: tsdoc.time || 0.0 });
|
||||
}
|
||||
row.push({ type: 'string', value: misc.formatSeconds(tsdoc.time || 0) });
|
||||
for (const pid of tdoc.pids) {
|
||||
const rdoc = (tsddict[pid] || {}).rid;
|
||||
const colScore = (tsddict[pid] || {}).penaltyScore || '-';
|
||||
const colOriginalScore = (tsddict[pid] || {}).score || '-';
|
||||
const colTime = (tsddict[pid] || {}).time || '-';
|
||||
const colTimeStr = colTime !== '-' ? misc.formatSeconds(colTime || 0) : '-';
|
||||
if (isExport) {
|
||||
row.push(
|
||||
{ type: 'string', value: colScore },
|
||||
{ type: 'string', value: colOriginalScore },
|
||||
{ type: 'string', value: colTime },
|
||||
{ type: 'string', value: colTimeStr },
|
||||
);
|
||||
} else {
|
||||
row.push({
|
||||
type: 'record',
|
||||
value: '{0} / {1}\n{2}'.format(colScore, colOriginalScore, colTimeStr),
|
||||
raw: rdoc,
|
||||
});
|
||||
}
|
||||
}
|
||||
rows.push(row);
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
rank: (tsdocs) => ranked(tsdocs, (a, b) => a.score === b.score),
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {import('mongodb').ObjectID} ObjectID
|
||||
* @typedef {import('../interface').Tdoc} Tdoc
|
||||
*/
|
||||
export const RULES = {
|
||||
acm, oi, homework,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {string} title
|
||||
* @param {string} content
|
||||
* @param {number} owner
|
||||
* @param {string} rule
|
||||
* @param {Date} beginAt
|
||||
* @param {Date} endAt
|
||||
* @param {ObjectID[]} pids
|
||||
* @param {object} data
|
||||
* @returns {ObjectID} tid
|
||||
*/
|
||||
function add(domainId, title, content, owner, rule,
|
||||
beginAt = new Date(), endAt = new Date(), pids = [], rated = false,
|
||||
data = {}, type = document.TYPE_CONTEST) {
|
||||
export function add(
|
||||
domainId: string, title: string, content: string, owner: number,
|
||||
rule: string, beginAt = new Date(), endAt = new Date(), pids = [],
|
||||
rated = false, data = {}, type = document.TYPE_CONTEST,
|
||||
) {
|
||||
validator.checkTitle(title);
|
||||
validator.checkContent(content);
|
||||
if (!this.RULES[rule]) throw new ValidationError('rule');
|
||||
@@ -223,14 +376,10 @@ function add(domainId, title, content, owner, rule,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {ObjectID} tid
|
||||
* @param {object} $set
|
||||
* @param {number} type
|
||||
* @returns {Promise<Tdoc>} tdoc after modification
|
||||
*/
|
||||
async function edit(domainId, tid, $set, type = document.TYPE_CONTEST) {
|
||||
export async function edit(
|
||||
domainId: string, tid: ObjectID,
|
||||
$set: any, type = document.TYPE_CONTEST,
|
||||
) {
|
||||
if ($set.rule) {
|
||||
if (!this.RULES[$set.rule]) throw new ValidationError('rule');
|
||||
}
|
||||
@@ -240,13 +389,7 @@ async function edit(domainId, tid, $set, type = document.TYPE_CONTEST) {
|
||||
return await document.set(domainId, type, tid, $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {ObjectID} tid
|
||||
* @param {number} type
|
||||
* @returns {Promise<Tdoc>}
|
||||
*/
|
||||
async function get(domainId, tid, type = document.TYPE_CONTEST) {
|
||||
export async function get(domainId: string, tid: ObjectID, type = document.TYPE_CONTEST) {
|
||||
let tdoc;
|
||||
if (type === -1) {
|
||||
tdoc = await document.get(domainId, document.TYPE_CONTEST, tid);
|
||||
@@ -256,8 +399,8 @@ async function get(domainId, tid, type = document.TYPE_CONTEST) {
|
||||
return tdoc;
|
||||
}
|
||||
|
||||
async function updateStatus(
|
||||
domainId, tid, uid, rid, pid,
|
||||
export async function updateStatus(
|
||||
domainId: string, tid: ObjectID, uid: number, rid: ObjectID, pid: number,
|
||||
accept = false, score = 0, type = document.TYPE_CONTEST,
|
||||
) {
|
||||
await get(domainId, tid, type);
|
||||
@@ -267,18 +410,27 @@ async function updateStatus(
|
||||
if (!tsdoc.attend) throw new ContestNotAttendedError(tid, uid);
|
||||
}
|
||||
|
||||
function getStatus(domainId, tid, uid, type = document.TYPE_CONTEST) {
|
||||
export function getStatus(
|
||||
domainId: string, tid: ObjectID, uid: number,
|
||||
type = document.TYPE_CONTEST,
|
||||
) {
|
||||
return document.getStatus(domainId, type, tid, uid);
|
||||
}
|
||||
|
||||
async function getListStatus(domainId, uid, tids, type = document.TYPE_CONTEST) {
|
||||
export async function getListStatus(
|
||||
domainId: string, uid: number, tids: ObjectID[],
|
||||
type = document.TYPE_CONTEST,
|
||||
) {
|
||||
const r = {};
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
for (const tid of tids) r[tid] = await getStatus(domainId, tid, uid, type);
|
||||
for (const tid of tids) r[tid.toHexString()] = await getStatus(domainId, tid, uid, type);
|
||||
return r;
|
||||
}
|
||||
|
||||
async function attend(domainId, tid, uid, type = document.TYPE_CONTEST) {
|
||||
export async function attend(
|
||||
domainId: string, tid: ObjectID, uid: number,
|
||||
type = document.TYPE_CONTEST,
|
||||
) {
|
||||
try {
|
||||
await document.cappedIncStatus(domainId, type, tid, uid, 'attend', 1, 0, 1);
|
||||
} catch (e) {
|
||||
@@ -288,49 +440,61 @@ async function attend(domainId, tid, uid, type = document.TYPE_CONTEST) {
|
||||
return {};
|
||||
}
|
||||
|
||||
function getMultiStatus(domainId, query, docType = document.TYPE_CONTEST) {
|
||||
export function getMultiStatus(domainId: string, query: any, docType = document.TYPE_CONTEST) {
|
||||
return document.getMultiStatus(domainId, docType, query);
|
||||
}
|
||||
|
||||
function isNew(tdoc, days = 1) {
|
||||
export function isNew(tdoc: Tdoc, days = 1) {
|
||||
const now = new Date().getTime();
|
||||
const readyAt = tdoc.beginAt.getTime();
|
||||
return (now < readyAt - days * 24 * 3600 * 1000);
|
||||
}
|
||||
function isUpcoming(tdoc, days = 1) {
|
||||
|
||||
export function isUpcoming(tdoc: Tdoc, days = 1) {
|
||||
const now = new Date().getTime();
|
||||
const readyAt = tdoc.beginAt.getTime();
|
||||
return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt);
|
||||
return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt.getTime());
|
||||
}
|
||||
function isNotStarted(tdoc) {
|
||||
|
||||
export function isNotStarted(tdoc: Tdoc) {
|
||||
return (new Date()) < tdoc.beginAt;
|
||||
}
|
||||
function isOngoing(tdoc) {
|
||||
|
||||
export function isOngoing(tdoc: Tdoc) {
|
||||
const now = new Date();
|
||||
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
||||
}
|
||||
function isDone(tdoc) {
|
||||
|
||||
export function isDone(tdoc: Tdoc) {
|
||||
return tdoc.endAt <= new Date();
|
||||
}
|
||||
|
||||
const ContestHandlerMixin = (c) => class extends c {
|
||||
export function isExtended(tdoc: Tdoc) {
|
||||
const now = new Date().getTime();
|
||||
return tdoc.penaltySince.getTime() <= now && now < tdoc.endAt.getTime();
|
||||
}
|
||||
|
||||
export const ContestHandlerMixin = (c) => class extends c {
|
||||
canViewHiddenScoreboard() {
|
||||
return this.user.hasPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||
return this.user.hasPerm(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||
}
|
||||
|
||||
canShowRecord(tdoc, allowPermOverride = true) {
|
||||
canShowRecord(tdoc: Tdoc, allowPermOverride = true) {
|
||||
if (RULES[tdoc.rule].showRecord(tdoc, new Date())) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
canShowScoreboard(tdoc, allowPermOverride = true) {
|
||||
canShowScoreboard(tdoc: Tdoc, allowPermOverride = true) {
|
||||
if (RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
async getScoreboard(domainId, tid, isExport = false, docType = document.TYPE_CONTEST) {
|
||||
async getScoreboard(
|
||||
domainId: string, tid: ObjectID,
|
||||
isExport = false, docType = document.TYPE_CONTEST,
|
||||
) {
|
||||
const tdoc = await get(domainId, tid, docType);
|
||||
if (!this.canShowScoreboard(tdoc)) throw new ContestScoreboardHiddenError(tid);
|
||||
const tsdocs = await getMultiStatus(domainId, { docId: tid }, docType)
|
||||
@@ -346,29 +510,32 @@ const ContestHandlerMixin = (c) => class extends c {
|
||||
return [tdoc, rows, udict];
|
||||
}
|
||||
|
||||
async verifyProblems(domainId, pids) { // eslint-disable-line class-methods-use-this
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async verifyProblems(domainId: string, pids: number[]) {
|
||||
await problem.getList(domainId, pids, true);
|
||||
return pids;
|
||||
}
|
||||
};
|
||||
|
||||
function setStatus(domainId, tid, uid, $set) {
|
||||
export function setStatus(domainId: string, tid: ObjectID, uid: number, $set: any) {
|
||||
return document.setStatus(domainId, document.TYPE_CONTEST, tid, uid, $set);
|
||||
}
|
||||
|
||||
function count(domainId, query, type = document.TYPE_CONTEST) {
|
||||
export function count(domainId: string, query: any, type = document.TYPE_CONTEST) {
|
||||
return document.count(domainId, type, query);
|
||||
}
|
||||
|
||||
function getMulti(domainId, query = {}, type = document.TYPE_CONTEST) {
|
||||
export function getMulti(domainId: string, query = {}, type = document.TYPE_CONTEST) {
|
||||
return document.getMulti(domainId, type, query);
|
||||
}
|
||||
|
||||
function _getStatusJournal(tsdoc) {
|
||||
export function _getStatusJournal(tsdoc) {
|
||||
return tsdoc.journal.sort((a, b) => (a.rid.generationTime - b.rid.generationTime));
|
||||
}
|
||||
|
||||
async function getAndListStatus(domainId, tid, docType = document.TYPE_CONTEST) {
|
||||
export async function getAndListStatus(
|
||||
domainId: string, tid: ObjectID, docType = document.TYPE_CONTEST,
|
||||
) {
|
||||
// TODO(iceboy): projection, pagination.
|
||||
const tdoc = await get(domainId, tid, docType);
|
||||
const tsdocs = await document.getMultiStatus(domainId, docType, { docId: tid })
|
||||
@@ -376,7 +543,7 @@ async function getAndListStatus(domainId, tid, docType = document.TYPE_CONTEST)
|
||||
return [tdoc, tsdocs];
|
||||
}
|
||||
|
||||
async function recalcStatus(domainId, tid, type) {
|
||||
export async function recalcStatus(domainId: string, tid: ObjectID, type = document.TYPE_CONTEST) {
|
||||
const [tdoc, tsdocs] = await Promise.all([
|
||||
document.get(domainId, type, tid),
|
||||
document.getMultiStatus(domainId, type, { docId: tid }).toArray(),
|
||||
@@ -397,7 +564,23 @@ async function recalcStatus(domainId, tid, type) {
|
||||
return await Promise.all(tasks);
|
||||
}
|
||||
|
||||
global.Hydro.model.contest = module.exports = {
|
||||
export const statusText = (tdoc: Tdoc) => (
|
||||
isNew(tdoc)
|
||||
? 'New'
|
||||
: isUpcoming(tdoc)
|
||||
? 'Ready (☆▽☆)'
|
||||
: isOngoing(tdoc)
|
||||
? 'Live...'
|
||||
: 'Done');
|
||||
|
||||
export const getStatusText = (tdoc: Tdoc) => (
|
||||
isNotStarted(tdoc)
|
||||
? 'not_started'
|
||||
: isOngoing(tdoc)
|
||||
? 'ongoing'
|
||||
: 'finished');
|
||||
|
||||
global.Hydro.model.contest = {
|
||||
RULES,
|
||||
ContestHandlerMixin,
|
||||
add,
|
||||
@@ -418,18 +601,7 @@ global.Hydro.model.contest = module.exports = {
|
||||
isNotStarted,
|
||||
isOngoing,
|
||||
isDone,
|
||||
statusText: (tdoc) => (
|
||||
isNew(tdoc)
|
||||
? 'New'
|
||||
: isUpcoming(tdoc)
|
||||
? 'Ready (☆▽☆)'
|
||||
: isOngoing(tdoc)
|
||||
? 'Live...'
|
||||
: 'Done'),
|
||||
getStatusText: (tdoc) => (
|
||||
isNotStarted(tdoc)
|
||||
? 'not_started'
|
||||
: isOngoing(tdoc)
|
||||
? 'ongoing'
|
||||
: 'finished'),
|
||||
isExtended,
|
||||
statusText,
|
||||
getStatusText,
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
const { ObjectID } = require('mongodb');
|
||||
const problem = require('./problem');
|
||||
const document = require('./document');
|
||||
const contest = require('./contest');
|
||||
const training = require('./training');
|
||||
const { PERM_VIEW_PROBLEM_HIDDEN } = require('./builtin').PERM;
|
||||
const { DocumentNotFoundError } = require('../error');
|
||||
import { ObjectID } from 'mongodb';
|
||||
import * as problem from './problem';
|
||||
import * as contest from './contest';
|
||||
import * as training from './training';
|
||||
import * as document from './document';
|
||||
import { PERM } from './builtin';
|
||||
import { DocumentNotFoundError } from '../error';
|
||||
|
||||
const typeDisplay = {
|
||||
export const typeDisplay = {
|
||||
[document.TYPE_PROBLEM]: 'problem',
|
||||
[document.TYPE_CONTEST]: 'contest',
|
||||
[document.TYPE_DISCUSSION_NODE]: 'node',
|
||||
@@ -14,7 +14,11 @@ const typeDisplay = {
|
||||
[document.TYPE_HOMEWORK]: 'homework',
|
||||
};
|
||||
|
||||
function add(domainId, parentType, parentId, owner, title, content, ip = null, highlight = false) {
|
||||
export function add(
|
||||
domainId: string, parentType: number, parentId: ObjectID,
|
||||
owner: number, title: string, content: string,
|
||||
ip: string | null = null, highlight: boolean = false,
|
||||
) {
|
||||
return document.add(
|
||||
domainId, content, owner, document.TYPE_DISCUSSION,
|
||||
null, parentType, parentId,
|
||||
@@ -29,15 +33,18 @@ function add(domainId, parentType, parentId, owner, title, content, ip = null, h
|
||||
);
|
||||
}
|
||||
|
||||
function get(domainId, did) {
|
||||
export function get(domainId: string, did: ObjectID) {
|
||||
return document.get(domainId, document.TYPE_DISCUSSION, did);
|
||||
}
|
||||
|
||||
function edit(domainId, did, title, content, highlight) {
|
||||
export function edit(
|
||||
domainId: string, did: ObjectID,
|
||||
title: string, content: string, highlight: boolean,
|
||||
) {
|
||||
return document.set(domainId, document.TYPE_DISCUSSION, did, { title, content, highlight });
|
||||
}
|
||||
|
||||
function del(domainId, did) {
|
||||
export function del(domainId: string, did: ObjectID) {
|
||||
return Promise.all([
|
||||
document.deleteOne(domainId, document.TYPE_DISCUSSION, did),
|
||||
document.deleteMulti(domainId, document.TYPE_DISCUSSION_REPLY, {
|
||||
@@ -47,15 +54,18 @@ function del(domainId, did) {
|
||||
]);
|
||||
}
|
||||
|
||||
function count(domainId, query) {
|
||||
export function count(domainId: string, query: any) {
|
||||
return document.count(domainId, document.TYPE_DISCUSSION, query);
|
||||
}
|
||||
|
||||
function getMulti(domainId, query) {
|
||||
export function getMulti(domainId: string, query: any = {}) {
|
||||
return document.getMulti(domainId, document.TYPE_DISCUSSION, query).sort('updateAt', -1);
|
||||
}
|
||||
|
||||
async function addReply(domainId, did, owner, content, ip) {
|
||||
export async function addReply(
|
||||
domainId: string, did: ObjectID, owner: number,
|
||||
content: string, ip: string,
|
||||
) {
|
||||
const [drdoc] = await Promise.all([
|
||||
document.add(
|
||||
domainId, content, owner, document.TYPE_DISCUSSION_REPLY,
|
||||
@@ -66,15 +76,15 @@ async function addReply(domainId, did, owner, content, ip) {
|
||||
return drdoc;
|
||||
}
|
||||
|
||||
function getReply(domainId, drid) {
|
||||
export function getReply(domainId: string, drid: ObjectID) {
|
||||
return document.get(domainId, document.TYPE_DISCUSSION_REPLY, drid);
|
||||
}
|
||||
|
||||
function editReply(domainId, drid, content) {
|
||||
export async function editReply(domainId: string, drid: ObjectID, content: string) {
|
||||
return document.set(domainId, document.TYPE_DISCUSSION_REPLY, drid, { content });
|
||||
}
|
||||
|
||||
async function delReply(domainId, drid) {
|
||||
export async function delReply(domainId: string, drid: ObjectID) {
|
||||
const drdoc = await getReply(domainId, drid);
|
||||
if (!drdoc) throw new DocumentNotFoundError(drid);
|
||||
return await Promise.all([
|
||||
@@ -83,18 +93,21 @@ async function delReply(domainId, drid) {
|
||||
]);
|
||||
}
|
||||
|
||||
function getMultiReply(domainId, did) {
|
||||
export function getMultiReply(domainId: string, did: ObjectID) {
|
||||
return document.getMulti(
|
||||
domainId, document.TYPE_DISCUSSION_REPLY,
|
||||
{ parentType: document.TYPE_DISCUSSION, parentId: did },
|
||||
).sort('_id', -1);
|
||||
}
|
||||
|
||||
function getListReply(domainId, did) {
|
||||
export function getListReply(domainId: string, did: ObjectID) {
|
||||
return getMultiReply(domainId, did).toArray();
|
||||
}
|
||||
|
||||
async function addTailReply(domainId, drid, owner, content, ip) {
|
||||
export async function addTailReply(
|
||||
domainId: string, drid: ObjectID,
|
||||
owner: number, content: string, ip: string,
|
||||
) {
|
||||
let drdoc = await document.get(domainId, document.TYPE_DISCUSSION_REPLY, drid);
|
||||
const sid = new ObjectID();
|
||||
[drdoc] = await Promise.all([
|
||||
@@ -104,42 +117,42 @@ async function addTailReply(domainId, drid, owner, content, ip) {
|
||||
return [drdoc, sid];
|
||||
}
|
||||
|
||||
function getTailReply(domainId, drid, drrid) {
|
||||
export function getTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) {
|
||||
return document.getSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid);
|
||||
}
|
||||
|
||||
function editTailReply(domainId, drid, drrid, content) {
|
||||
export function editTailReply(domainId: string, drid: ObjectID, drrid: ObjectID, content: string) {
|
||||
return document.setSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid, { content });
|
||||
}
|
||||
|
||||
function delTailReply(domainId, drid, drrid) {
|
||||
export async function delTailReply(domainId: string, drid: ObjectID, drrid: ObjectID) {
|
||||
return document.deleteSub(domainId, document.TYPE_DISCUSSION_REPLY, drid, 'reply', drrid);
|
||||
}
|
||||
|
||||
function setStar(domainId, did, uid, star) {
|
||||
export function setStar(domainId: string, did: ObjectID, uid: number, star: boolean) {
|
||||
return document.setStatus(domainId, document.TYPE_DISCUSSION, did, uid, { star });
|
||||
}
|
||||
|
||||
function getStatus(domainId, did, uid) {
|
||||
export function getStatus(domainId: string, did: ObjectID, uid: number) {
|
||||
return document.getStatus(domainId, document.TYPE_DISCUSSION, did, uid);
|
||||
}
|
||||
|
||||
function addNode(domainId, _id, category, args) {
|
||||
export function addNode(domainId: string, _id: string, category: string, args: any) {
|
||||
return document.add(
|
||||
domainId, category, 1, document.TYPE_DISCUSSION_NODE,
|
||||
_id, null, null, args,
|
||||
);
|
||||
}
|
||||
|
||||
function getNode(domainId, _id) {
|
||||
export function getNode(domainId: string, _id: string) {
|
||||
return document.get(domainId, document.TYPE_DISCUSSION_NODE, _id);
|
||||
}
|
||||
|
||||
async function getVnode(domainId, ddoc, handler) {
|
||||
export async function getVnode(domainId: string, ddoc: any, handler: any) {
|
||||
if (ddoc.parentType === document.TYPE_PROBLEM) {
|
||||
const pdoc = await problem.getById(domainId, ddoc.parentId);
|
||||
const pdoc = await problem.get(domainId, ddoc.parentId);
|
||||
if (!pdoc) return null;
|
||||
if (pdoc.hidden && handler) handler.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
||||
if (pdoc.hidden && handler) handler.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||
return { ...pdoc, type: ddoc.parentType, id: ddoc.parentId };
|
||||
}
|
||||
if (ddoc.parentType === document.TYPE_CONTEST) {
|
||||
@@ -170,11 +183,11 @@ async function getVnode(domainId, ddoc, handler) {
|
||||
};
|
||||
}
|
||||
|
||||
function getNodes(domainId) {
|
||||
export function getNodes(domainId: string) {
|
||||
return document.getMulti(domainId, document.TYPE_DISCUSSION_NODE).toArray();
|
||||
}
|
||||
|
||||
async function getListVnodes(domainId, ddocs, handler) {
|
||||
export async function getListVnodes(domainId: string, ddocs: any, handler: any) {
|
||||
const tasks = [];
|
||||
const res = {};
|
||||
function task(ddoc) {
|
||||
@@ -188,7 +201,7 @@ async function getListVnodes(domainId, ddocs, handler) {
|
||||
return res;
|
||||
}
|
||||
|
||||
global.Hydro.model.discussion = module.exports = {
|
||||
global.Hydro.model.discussion = {
|
||||
typeDisplay,
|
||||
add,
|
||||
get,
|
||||
@@ -1,28 +1,32 @@
|
||||
const assert = require('assert');
|
||||
const { ObjectID } = require('mongodb');
|
||||
const db = require('../service/db');
|
||||
import assert from 'assert';
|
||||
import { ObjectID } from 'mongodb';
|
||||
import * as db from '../service/db';
|
||||
|
||||
type DocID = ObjectID | string | number;
|
||||
|
||||
const coll = db.collection('document');
|
||||
const collStatus = db.collection('document.status');
|
||||
|
||||
const TYPE_DOMAIN_USER = 0;
|
||||
const TYPE_PROBLEM = 10;
|
||||
const TYPE_PROBLEM_SOLUTION = 11;
|
||||
const TYPE_PROBLEM_LIST = 12;
|
||||
const TYPE_DISCUSSION_NODE = 20;
|
||||
const TYPE_DISCUSSION = 21;
|
||||
const TYPE_DISCUSSION_REPLY = 22;
|
||||
const TYPE_CONTEST = 30;
|
||||
const TYPE_TRAINING = 40;
|
||||
const TYPE_FILE = 50;
|
||||
const TYPE_HOMEWORK = 60;
|
||||
export const TYPE_DOMAIN_USER = 0;
|
||||
export const TYPE_PROBLEM = 10;
|
||||
export const TYPE_PROBLEM_SOLUTION = 11;
|
||||
export const TYPE_PROBLEM_LIST = 12;
|
||||
export const TYPE_DISCUSSION_NODE = 20;
|
||||
export const TYPE_DISCUSSION = 21;
|
||||
export const TYPE_DISCUSSION_REPLY = 22;
|
||||
export const TYPE_CONTEST = 30;
|
||||
export const TYPE_TRAINING = 40;
|
||||
export const TYPE_FILE = 50;
|
||||
export const TYPE_HOMEWORK = 60;
|
||||
|
||||
async function add(
|
||||
domainId, content, owner, docType, docId = null,
|
||||
parentType = null, parentId = null, args = {},
|
||||
export async function add(
|
||||
domainId: string, content: string, owner: number,
|
||||
docType: number, docId: DocID = null,
|
||||
parentType: number | null = null, parentId: DocID = null,
|
||||
args: any = {},
|
||||
) {
|
||||
const _id = new ObjectID();
|
||||
const doc = {
|
||||
const doc: any = {
|
||||
_id,
|
||||
content,
|
||||
owner,
|
||||
@@ -40,11 +44,11 @@ async function add(
|
||||
return docId || res.insertedId;
|
||||
}
|
||||
|
||||
function get(domainId, docType, docId) {
|
||||
export function get(domainId: string, docType: number, docId: DocID) {
|
||||
return coll.findOne({ domainId, docType, docId });
|
||||
}
|
||||
|
||||
async function set(domainId, docType, docId, args) {
|
||||
export async function set(domainId: string, docType: number, docId: DocID, args: any) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ domainId, docType, docId },
|
||||
{ $set: args },
|
||||
@@ -53,26 +57,29 @@ async function set(domainId, docType, docId, args) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
function deleteOne(domainId, docType, docId) {
|
||||
export function deleteOne(domainId: string, docType: number, docId: DocID) {
|
||||
return Promise.all([
|
||||
coll.deleteMany({ domainId, docType, docId }),
|
||||
coll.deleteOne({ domainId, docType, docId }),
|
||||
]);
|
||||
}
|
||||
|
||||
function deleteMulti(domainId, docType, args = {}) {
|
||||
export function deleteMulti(domainId: string, docType: number, args: any = {}) {
|
||||
return coll.deleteMany({ ...args, domainId, docType });
|
||||
}
|
||||
|
||||
function deleteMultiStatus(domainId, docType, args = {}) {
|
||||
export function deleteMultiStatus(domainId: string, docType: number, args: any = {}) {
|
||||
return collStatus.deleteMany({ ...args, domainId, docType });
|
||||
}
|
||||
|
||||
function getMulti(domainId, docType, args = {}) {
|
||||
export function getMulti(domainId: string, docType: number, args: any = {}) {
|
||||
return coll.find({ ...args, docType, domainId });
|
||||
}
|
||||
|
||||
async function inc(domainId, docType, docId, key, value) {
|
||||
export async function inc(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
key: string, value: number,
|
||||
) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ domainId, docType, docId },
|
||||
{ $inc: { [key]: value } },
|
||||
@@ -81,7 +88,10 @@ async function inc(domainId, docType, docId, key, value) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function incAndSet(domainId, docType, docId, key, value, args) {
|
||||
export async function incAndSet(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
key: string, value: number, args: any,
|
||||
) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ domainId, docType, docId },
|
||||
{ $inc: { [key]: value }, $set: args },
|
||||
@@ -90,11 +100,14 @@ async function incAndSet(domainId, docType, docId, key, value, args) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
function count(domainId, docType, query) {
|
||||
export function count(domainId: string, docType: number, query: any) {
|
||||
return coll.find({ ...query, docType, domainId }).count();
|
||||
}
|
||||
|
||||
async function push(domainId, docType, docId, key, content, owner, args) {
|
||||
export async function push(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
key: string, content: string, owner: number, args: any = {},
|
||||
) {
|
||||
const _id = new ObjectID();
|
||||
const doc = await coll.findOneAndUpdate(
|
||||
{ domainId, docType, docId },
|
||||
@@ -110,7 +123,10 @@ async function push(domainId, docType, docId, key, content, owner, args) {
|
||||
return [doc.value, _id];
|
||||
}
|
||||
|
||||
async function pull(domainId, docType, docId, setKey, contents) {
|
||||
export async function pull(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
setKey: string, contents: string[],
|
||||
) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ domainId, docType, docId },
|
||||
{ $pull: { [setKey]: { $in: contents } } },
|
||||
@@ -119,7 +135,10 @@ async function pull(domainId, docType, docId, setKey, contents) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function deleteSub(domainId, docType, docId, key, subId) {
|
||||
export async function deleteSub(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
key: string, subId: ObjectID,
|
||||
) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ domainId, docType, docId },
|
||||
{ $pull: { [key]: { _id: subId } } },
|
||||
@@ -128,7 +147,10 @@ async function deleteSub(domainId, docType, docId, key, subId) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function getSub(domainId, docType, docId, key, subId) {
|
||||
export async function getSub(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
key: string, subId: ObjectID,
|
||||
) {
|
||||
const doc = await coll.findOne({
|
||||
domainId,
|
||||
docType,
|
||||
@@ -142,7 +164,10 @@ async function getSub(domainId, docType, docId, key, subId) {
|
||||
return [doc, null];
|
||||
}
|
||||
|
||||
async function setSub(domainId, docType, docId, key, subId, args) {
|
||||
export async function setSub(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
key: string, subId: ObjectID, args: any,
|
||||
) {
|
||||
const $set = {};
|
||||
for (const k in args) {
|
||||
$set[`${key}.$.${k}`] = args[k];
|
||||
@@ -160,7 +185,10 @@ async function setSub(domainId, docType, docId, key, subId, args) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function addToSet(domainId, docType, docId, setKey, content) {
|
||||
export async function addToSet(
|
||||
domainId: string, docType: number, docId: DocID,
|
||||
setKey: string, content: string,
|
||||
) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ domainId, docType, docId },
|
||||
{ $addToSet: { [setKey]: content } },
|
||||
@@ -169,17 +197,19 @@ async function addToSet(domainId, docType, docId, setKey, content) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
function getStatus(domainId, docType, docId, uid) {
|
||||
export function getStatus(domainId: string, docType: number, docId: DocID, uid: number) {
|
||||
return collStatus.findOne({
|
||||
domainId, docType, docId, uid,
|
||||
});
|
||||
}
|
||||
|
||||
function getMultiStatus(domainId, docType, args) {
|
||||
export function getMultiStatus(domainId: string, docType: number, args: any) {
|
||||
return collStatus.find({ ...args, docType, domainId });
|
||||
}
|
||||
|
||||
async function setStatus(domainId, docType, docId, uid, args) {
|
||||
export async function setStatus(
|
||||
domainId: string, docType: number, docId: DocID, uid: number, args: any,
|
||||
) {
|
||||
const res = await collStatus.findOneAndUpdate(
|
||||
{
|
||||
domainId, docType, docId, uid,
|
||||
@@ -190,14 +220,17 @@ async function setStatus(domainId, docType, docId, uid, args) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
function setMultiStatus(domainId, docType, query, args) {
|
||||
export function setMultiStatus(domainId: string, docType: number, query: any, args: any) {
|
||||
return collStatus.updateMany(
|
||||
{ domainId, docType, ...query },
|
||||
{ $set: args },
|
||||
);
|
||||
}
|
||||
|
||||
async function setIfNotStatus(domainId, docType, docId, uid, key, value, ifNot, args) {
|
||||
export async function setIfNotStatus(
|
||||
domainId: string, docType: number, docId: DocID, uid: number,
|
||||
key: string, value: number, ifNot: any, args: any,
|
||||
) {
|
||||
const res = await collStatus.findOneAndUpdate(
|
||||
{
|
||||
domainId, docType, docId, uid, key: { $not: { $eq: ifNot } },
|
||||
@@ -208,9 +241,9 @@ async function setIfNotStatus(domainId, docType, docId, uid, key, value, ifNot,
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function cappedIncStatus(
|
||||
domainId, docType, docId, uid,
|
||||
key, value, minValue = -1, maxValue = 1,
|
||||
export async function cappedIncStatus(
|
||||
domainId: string, docType: number, docId: DocID, uid: number,
|
||||
key: string, value: number, minValue = -1, maxValue = 1,
|
||||
) {
|
||||
assert(value !== 0);
|
||||
const $not = value > 0 ? { $gte: maxValue } : { $lte: minValue };
|
||||
@@ -224,7 +257,10 @@ async function cappedIncStatus(
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function incStatus(domainId, docType, docId, uid, key, value) {
|
||||
export async function incStatus(
|
||||
domainId: string, docType: number, docId: DocID, uid: number,
|
||||
key: string, value: number,
|
||||
) {
|
||||
const res = await collStatus.findOneAndUpdate(
|
||||
{
|
||||
domainId, docType, docId, uid,
|
||||
@@ -235,7 +271,10 @@ async function incStatus(domainId, docType, docId, uid, key, value) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function revPushStatus(domainId, docType, docId, uid, key, value) {
|
||||
export async function revPushStatus(
|
||||
domainId: string, docType: number, docId: DocID, uid: number,
|
||||
key: string, value: any,
|
||||
) {
|
||||
const res = await collStatus.findOneAndUpdate(
|
||||
{
|
||||
domainId, docType, docId, uid,
|
||||
@@ -246,7 +285,9 @@ async function revPushStatus(domainId, docType, docId, uid, key, value) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function revInitStatus(domainId, docType, docId, uid) {
|
||||
export async function revInitStatus(
|
||||
domainId: string, docType: number, docId: DocID, uid: number,
|
||||
) {
|
||||
const res = await collStatus.findOneAndUpdate(
|
||||
{
|
||||
domainId, docType, docId, uid,
|
||||
@@ -257,7 +298,10 @@ async function revInitStatus(domainId, docType, docId, uid) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function revSetStatus(domainId, docType, docId, uid, rev, args, returnDoc = true) {
|
||||
export async function revSetStatus(
|
||||
domainId: string, docType: number, docId: DocID, uid: number,
|
||||
rev: number, args: any, returnDoc = true,
|
||||
) {
|
||||
const filter = {
|
||||
domainId, docType, docId, uid, rev,
|
||||
};
|
||||
@@ -270,7 +314,7 @@ async function revSetStatus(domainId, docType, docId, uid, rev, args, returnDoc
|
||||
return res;
|
||||
}
|
||||
|
||||
async function ensureIndexes() {
|
||||
export async function ensureIndexes() {
|
||||
await coll.createIndex({ domainId: 1, docType: 1, docId: 1 }, { unique: true });
|
||||
await coll.createIndex({
|
||||
domainId: 1, docType: 1, owner: 1, docId: -1,
|
||||
@@ -332,7 +376,7 @@ async function ensureIndexes() {
|
||||
}, { sparse: true });
|
||||
}
|
||||
|
||||
global.Hydro.model.document = module.exports = {
|
||||
global.Hydro.model.document = {
|
||||
add,
|
||||
addToSet,
|
||||
cappedIncStatus,
|
||||
@@ -373,3 +417,5 @@ global.Hydro.model.document = module.exports = {
|
||||
TYPE_FILE,
|
||||
TYPE_TRAINING,
|
||||
};
|
||||
|
||||
export default global.Hydro.model.document;
|
||||
@@ -1,21 +1,11 @@
|
||||
const builtin = require('./builtin');
|
||||
const document = require('./document');
|
||||
const db = require('../service/db');
|
||||
const validator = require('../lib/validator');
|
||||
import * as builtin from './builtin';
|
||||
import * as document from './document';
|
||||
import * as db from '../service/db';
|
||||
import * as validator from '../lib/validator';
|
||||
|
||||
const coll = db.collection('domain');
|
||||
|
||||
/**
|
||||
* @typedef {import('../interface').Pdoc} Pdoc
|
||||
* @typedef {import('mongodb').ObjectID} ObjectID
|
||||
* @typedef {import('mongodb').Cursor} Cursor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {number} owner
|
||||
*/
|
||||
function add(domainId, owner, name, isEnsure = false) {
|
||||
export function add(domainId: string, owner: number, name: string, isEnsure = false) {
|
||||
const tasks = [];
|
||||
if (isEnsure) {
|
||||
tasks.push(
|
||||
@@ -43,46 +33,26 @@ function add(domainId, owner, name, isEnsure = false) {
|
||||
return Promise.all(tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @returns {Ddoc}
|
||||
*/
|
||||
async function get(domainId) {
|
||||
export async function get(domainId: string) {
|
||||
return coll.findOne({ _id: domainId });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} query
|
||||
* @param {object} sort
|
||||
* @param {number} page
|
||||
* @param {number} limit
|
||||
* @returns {Ddoc[]}
|
||||
*/
|
||||
function getMany(query, sort, page, limit) {
|
||||
export function getMany(query: any, sort: any, page: number, limit: number) {
|
||||
return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} query
|
||||
* @returns {Cursor}
|
||||
*/
|
||||
function getMulti(query) {
|
||||
export function getMulti(query: any) {
|
||||
return coll.find(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {object} $set
|
||||
* @returns {Ddoc}
|
||||
*/
|
||||
function edit(domainId, $set) {
|
||||
export function edit(domainId: string, $set: any) {
|
||||
if ($set.title) validator.checkTitle($set.title);
|
||||
if ($set.content) validator.checkContent($set.content);
|
||||
return coll.updateOne({ _id: domainId }, { $set });
|
||||
}
|
||||
|
||||
async function inc(domainId, field, n) {
|
||||
export async function inc(domainId: string, field: string, n: number) {
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ _id: domainId },
|
||||
{ $inc: { [field]: n } },
|
||||
@@ -91,14 +61,14 @@ async function inc(domainId, field, n) {
|
||||
return res.value;
|
||||
}
|
||||
|
||||
async function getList(domainIds) {
|
||||
export async function getList(domainIds: string[]) {
|
||||
const r = {};
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
for (const domainId of domainIds) r[domainId] = await get(domainId);
|
||||
return r;
|
||||
}
|
||||
|
||||
global.Hydro.model.domain = module.exports = {
|
||||
global.Hydro.model.domain = {
|
||||
add,
|
||||
inc,
|
||||
get,
|
||||
@@ -1,83 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { ForbiddenError, NotFoundError } = require('../error');
|
||||
const db = require('../service/db');
|
||||
const gridfs = require('../service/gridfs');
|
||||
const hash = require('../lib/hash.hydro');
|
||||
|
||||
const coll = db.collection('file');
|
||||
const collFile = db.collection('fs.files');
|
||||
const collChunk = db.collection('fs.chunks');
|
||||
|
||||
function _timestamp() {
|
||||
return Math.floor(Number(new Date()) / 1000000);
|
||||
}
|
||||
|
||||
async function add(streamOrPath, filename, meta = {}) {
|
||||
if (typeof streamOrPath === 'string') streamOrPath = fs.createReadStream(streamOrPath);
|
||||
const w = gridfs.openUploadStream(filename);
|
||||
await coll.insertOne({ ...meta, _id: w.id, secret: String.random(32) });
|
||||
await new Promise((resolve, reject) => {
|
||||
w.on('error', reject);
|
||||
w.on('finish', resolve);
|
||||
streamOrPath.pipe(w);
|
||||
});
|
||||
const c = await gridfs.find({ _id: w.id }).toArray();
|
||||
await coll.updateOne({ _id: w.id }, { $set: { md5: c[0].md5, size: c[0].size } });
|
||||
return w.id;
|
||||
}
|
||||
|
||||
function del(_id) {
|
||||
return Promise.all([
|
||||
coll.deleteOne({ _id }),
|
||||
collFile.deleteOne({ _id }),
|
||||
collChunk.deleteMany({ files_id: _id }),
|
||||
]);
|
||||
}
|
||||
|
||||
async function inc(_id) {
|
||||
const doc = await coll.findOneAndUpdate(
|
||||
{ _id },
|
||||
{ $inc: { count: 1 } },
|
||||
{ returnOriginal: false },
|
||||
);
|
||||
return (doc.value || {}).count;
|
||||
}
|
||||
|
||||
async function dec(_id) {
|
||||
const file = await coll.findOneAndUpdate(
|
||||
{ _id },
|
||||
{ $inc: { count: -1 } },
|
||||
{ returnOriginal: false },
|
||||
);
|
||||
if (!file.count) await del(_id);
|
||||
return file.count;
|
||||
}
|
||||
|
||||
async function get(_id, secret) {
|
||||
const file = await coll.findOne({ _id });
|
||||
if (!file) throw new NotFoundError(_id);
|
||||
if (typeof secret !== 'undefined') {
|
||||
const timestamp = _timestamp();
|
||||
if (!(hash(file.secret, timestamp.toString()) === secret)) {
|
||||
if (!(hash(file.secret, timestamp.toString() - 1) === secret)) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
}
|
||||
}
|
||||
return gridfs.openDownloadStream(_id);
|
||||
}
|
||||
|
||||
function getMeta(_id) {
|
||||
return coll.findOne({ _id });
|
||||
}
|
||||
|
||||
async function url(_id, name) {
|
||||
const file = await coll.findOne({ _id });
|
||||
const secret = hash(file.secret, _timestamp().toString());
|
||||
if (name) return `/fs/${_id}/${Buffer.from(name).toString('base64')}/${secret}`;
|
||||
return `/fs/${_id}/${secret}`;
|
||||
}
|
||||
|
||||
global.Hydro.model.file = module.exports = {
|
||||
add, get, del, inc, dec, getMeta, url,
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
import { ObjectID } from 'mongodb';
|
||||
import * as fs from 'fs';
|
||||
import { ForbiddenError, NotFoundError } from '../error';
|
||||
import * as db from '../service/db';
|
||||
import gridfs from '../service/gridfs';
|
||||
import hash from '../lib/hash.hydro';
|
||||
|
||||
const coll = db.collection('file');
|
||||
const collFile = db.collection('fs.files');
|
||||
const collChunk = db.collection('fs.chunks');
|
||||
|
||||
function _timestamp() {
|
||||
return Math.floor(Number(new Date()) / 1000000);
|
||||
}
|
||||
|
||||
export async function add(streamOrPath: fs.ReadStream | string, filename: string, meta = {}) {
|
||||
let stream: fs.ReadStream;
|
||||
if (typeof streamOrPath === 'string') stream = fs.createReadStream(streamOrPath);
|
||||
else stream = streamOrPath;
|
||||
const w = gridfs.openUploadStream(filename);
|
||||
await coll.insertOne({ ...meta, _id: w.id, secret: String.random(32) });
|
||||
await new Promise((resolve, reject) => {
|
||||
w.on('error', reject);
|
||||
w.on('finish', resolve);
|
||||
stream.pipe(w);
|
||||
});
|
||||
const c = await gridfs.find({ _id: w.id }).toArray();
|
||||
await coll.updateOne({ _id: w.id }, { $set: { md5: c[0].md5, size: c[0].size } });
|
||||
return w.id;
|
||||
}
|
||||
|
||||
export function del(_id: ObjectID) {
|
||||
return Promise.all([
|
||||
coll.deleteOne({ _id }),
|
||||
collFile.deleteOne({ _id }),
|
||||
collChunk.deleteMany({ files_id: _id }),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function inc(_id: ObjectID) {
|
||||
const doc = await coll.findOneAndUpdate(
|
||||
{ _id },
|
||||
{ $inc: { count: 1 } },
|
||||
{ returnOriginal: false },
|
||||
);
|
||||
return (doc.value || {}).count;
|
||||
}
|
||||
|
||||
export async function dec(_id: ObjectID) {
|
||||
const file = await coll.findOneAndUpdate(
|
||||
{ _id },
|
||||
{ $inc: { count: -1 } },
|
||||
{ returnOriginal: false },
|
||||
);
|
||||
if (!file.value.count) await del(_id);
|
||||
return file.value.count;
|
||||
}
|
||||
|
||||
export async function getWithSecret(_id: ObjectID, secret: string) {
|
||||
const file = await coll.findOne({ _id });
|
||||
if (!file) throw new NotFoundError(_id);
|
||||
const timestamp = _timestamp();
|
||||
if (!(hash(file.secret, timestamp.toString()) === secret)) {
|
||||
if (!(hash(file.secret, (timestamp - 1).toString()) === secret)) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
}
|
||||
return gridfs.openDownloadStream(_id);
|
||||
}
|
||||
|
||||
export async function get(_id: ObjectID) {
|
||||
const file = await coll.findOne({ _id });
|
||||
if (!file) throw new NotFoundError(_id);
|
||||
return gridfs.openDownloadStream(_id);
|
||||
}
|
||||
|
||||
export function getMeta(_id: ObjectID) {
|
||||
return coll.findOne({ _id });
|
||||
}
|
||||
|
||||
export async function getMetaDict(ufids: ObjectID[]) {
|
||||
const r = {};
|
||||
ufids = Array.from(new Set(ufids));
|
||||
const ufdocs = await coll.find({ _id: { $in: ufids } }).toArray();
|
||||
for (const ufdoc of ufdocs) {
|
||||
r[ufdoc._id.toHexString()] = ufdoc;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
export function getMulti(query: any) {
|
||||
return coll.find(query);
|
||||
}
|
||||
|
||||
export async function url(_id: ObjectID, name: string | undefined) {
|
||||
const file = await coll.findOne({ _id });
|
||||
const secret = hash(file.secret, _timestamp().toString());
|
||||
if (name) return `/fs/${_id}/${Buffer.from(name).toString('base64')}/${secret}`;
|
||||
return `/fs/${_id}/${secret}`;
|
||||
}
|
||||
|
||||
global.Hydro.model.file = {
|
||||
add, get, del, inc, dec, getMeta, getWithSecret, getMulti, getMetaDict, url,
|
||||
};
|
||||
@@ -1,9 +1,10 @@
|
||||
const { MessageNotFoundError } = require('../error');
|
||||
const db = require('../service/db.js');
|
||||
import { ObjectID, QuerySelector } from 'mongodb';
|
||||
import { MessageNotFoundError } from '../error';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('message');
|
||||
|
||||
async function send(from, to, content) {
|
||||
export async function send(from: number, to: number, content: string) {
|
||||
const res = await coll.insertOne({
|
||||
from, to, content, unread: true,
|
||||
});
|
||||
@@ -12,42 +13,42 @@ async function send(from, to, content) {
|
||||
};
|
||||
}
|
||||
|
||||
async function get(_id) {
|
||||
export async function get(_id: ObjectID) {
|
||||
const doc = await coll.findOne({ _id });
|
||||
if (!doc) throw new MessageNotFoundError(_id);
|
||||
return doc;
|
||||
}
|
||||
|
||||
function getByUser(uid) {
|
||||
export function getByUser(uid: number) {
|
||||
return coll.find({ $or: [{ from: uid }, { to: uid }] }).sort('_id', 1).toArray();
|
||||
}
|
||||
|
||||
function getMany(query, sort, page, limit) {
|
||||
export function getMany<T>(query: QuerySelector<T>, sort, page: number, limit: number) {
|
||||
return coll.find(query).sort(sort)
|
||||
.skip((page - 1) * limit).limit(limit)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
function del(_id) {
|
||||
export function del(_id: ObjectID) {
|
||||
return coll.deleteOne({ _id });
|
||||
}
|
||||
|
||||
function count(query) {
|
||||
export function count<T>(query: QuerySelector<T>) {
|
||||
return coll.find(query).count();
|
||||
}
|
||||
|
||||
function getMulti(uid) {
|
||||
export function getMulti(uid: number) {
|
||||
return coll.find({ $or: [{ from: uid }, { to: uid }] });
|
||||
}
|
||||
|
||||
function ensureIndexes() {
|
||||
export function ensureIndexes() {
|
||||
return Promise.all([
|
||||
coll.createIndex({ to: 1, _id: -1 }),
|
||||
coll.createIndex({ from: 1, _id: -1 }),
|
||||
]);
|
||||
}
|
||||
|
||||
global.Hydro.model.message = module.exports = {
|
||||
global.Hydro.model.message = {
|
||||
count,
|
||||
get,
|
||||
getByUser,
|
||||
@@ -1,15 +1,9 @@
|
||||
const { OpcountExceededError } = require('../error');
|
||||
const db = require('../service/db');
|
||||
import { OpcountExceededError } from '../error';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('opcount');
|
||||
|
||||
/**
|
||||
* @param {string} op
|
||||
* @param {string} ident
|
||||
* @param {number} period_secs
|
||||
* @param {number} max_operations
|
||||
*/
|
||||
async function inc(op, ident, periodSecs, maxOperations) {
|
||||
export async function inc(op: string, ident: string, periodSecs: number, maxOperations: number) {
|
||||
const curTime = new Date().getTime();
|
||||
const beginAt = new Date(curTime - (curTime % (periodSecs * 1000)));
|
||||
const expireAt = new Date(beginAt.getTime() + periodSecs * 1000);
|
||||
@@ -25,8 +19,8 @@ async function inc(op, ident, periodSecs, maxOperations) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureIndexes() {
|
||||
export function ensureIndexes() {
|
||||
return coll.createIndex('expireAt', { expireAfterSeconds: 0 });
|
||||
}
|
||||
|
||||
global.Hydro.model.opcount = module.exports = { inc, ensureIndexes };
|
||||
global.Hydro.model.opcount = { inc, ensureIndexes };
|
||||
@@ -1,230 +0,0 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
const { STATUS_ACCEPTED } = require('./builtin').STATUS;
|
||||
const file = require('./file');
|
||||
const document = require('./document');
|
||||
const domain = require('./domain');
|
||||
const { ProblemNotFoundError } = require('../error');
|
||||
const readConfig = require('../lib/readConfig');
|
||||
|
||||
/**
|
||||
* @typedef {import('../interface').Pdoc} Pdoc
|
||||
* @typedef {import('mongodb').ObjectID} ObjectID
|
||||
* @typedef {import('mongodb').Cursor} Cursor
|
||||
*/
|
||||
|
||||
class Problem {
|
||||
constructor(pdoc) {
|
||||
this.docType = document.TYPE_PROBLEM;
|
||||
if (pdoc) {
|
||||
this._id = pdoc._id;
|
||||
this.docId = pdoc.docId;
|
||||
this.pid = pdoc.pid;
|
||||
this.title = pdoc.title;
|
||||
this.content = pdoc.content;
|
||||
this.owner = pdoc.owner;
|
||||
this.config = pdoc.config;
|
||||
this.data = pdoc.data;
|
||||
this.nSubmit = pdoc.nSubmit;
|
||||
this.nAccept = pdoc.nAccept;
|
||||
this.difficulty = pdoc.difficulty || 0;
|
||||
this.tag = pdoc.tag || [];
|
||||
this.category = pdoc.category || [];
|
||||
this.hidden = pdoc.hidden || false;
|
||||
} else {
|
||||
this._id = new ObjectId();
|
||||
this.docId = this._id;
|
||||
this.pid = String.random(8);
|
||||
this.title = '*';
|
||||
this.content = '';
|
||||
this.owner = 1;
|
||||
this.config = '';
|
||||
this.data = null;
|
||||
this.nSubmit = 0;
|
||||
this.nAccept = 0;
|
||||
this.difficulty = 0;
|
||||
this.tag = [];
|
||||
this.category = [];
|
||||
this.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {string} title
|
||||
* @param {string} content
|
||||
* @param {number} owner
|
||||
* @param {object} args
|
||||
* @returns {Promise<ObjectID>} docId
|
||||
*/
|
||||
async function add(domainId, title, content, owner, {
|
||||
pid = null,
|
||||
data = null,
|
||||
category = [],
|
||||
tag = [],
|
||||
hidden = false,
|
||||
}) {
|
||||
const d = await domain.inc(domainId, 'pidCounter', 1);
|
||||
if (!pid) pid = d.pidCounter.toString();
|
||||
return await document.add(
|
||||
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
|
||||
{
|
||||
pid, title, data, category, tag, hidden, nSubmit: 0, nAccept: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {string|number} pid
|
||||
* @param {number} uid
|
||||
* @returns {Promise<Problem>}
|
||||
*/
|
||||
async function get(domainId, pid, uid = null, doThrow = true) {
|
||||
if (!Number.isNaN(parseInt(pid, 10))) pid = parseInt(pid, 10);
|
||||
const pdoc = Number.isInteger(pid)
|
||||
? await document.get(domainId, document.TYPE_PROBLEM, pid)
|
||||
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { pid }).toArray())[0];
|
||||
if (!pdoc) {
|
||||
if (doThrow) throw new ProblemNotFoundError(domainId, pid);
|
||||
return null;
|
||||
}
|
||||
if (uid) {
|
||||
pdoc.psdoc = await document.getStatus(domainId, document.TYPE_PROBLEM, pdoc.docId, uid);
|
||||
}
|
||||
return new Problem(pdoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {object} query
|
||||
* @param {object} sort
|
||||
* @param {number} page
|
||||
* @param {number} limit
|
||||
* @returns {Promise<Problem[]>}
|
||||
*/
|
||||
async function getMany(domainId, query, sort, page, limit) {
|
||||
const pdocs = await document.getMulti(domainId, query)
|
||||
.sort(sort).skip((page - 1) * limit).limit(limit)
|
||||
.toArray();
|
||||
for (let i = 0; i < pdocs.length; i++) {
|
||||
pdocs[i] = new Problem(pdocs[i]);
|
||||
}
|
||||
return pdocs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {object} query
|
||||
* @returns {Cursor}
|
||||
*/
|
||||
function getMulti(domainId, query) {
|
||||
return document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {object} query
|
||||
* @returns {Cursor}
|
||||
*/
|
||||
function getMultiStatus(domainId, query) {
|
||||
return document.getMultiStatus(domainId, document.TYPE_PROBLEM, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {ObjectID} _id
|
||||
* @param {object} query
|
||||
* @returns {Promise<Problem>}
|
||||
*/
|
||||
async function edit(domainId, _id, $set) {
|
||||
const pdoc = await document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
||||
return new Problem(pdoc);
|
||||
}
|
||||
|
||||
function inc(domainId, _id, field, n) {
|
||||
return document.inc(domainId, document.TYPE_PROBLEM, _id, field, n);
|
||||
}
|
||||
|
||||
function count(domainId, query) {
|
||||
return document.count(domainId, query);
|
||||
}
|
||||
|
||||
async function random(domainId, query) {
|
||||
const cursor = document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
||||
const pcount = await cursor.count();
|
||||
if (pcount) {
|
||||
const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray();
|
||||
return pdoc[0].pid;
|
||||
} return null;
|
||||
}
|
||||
|
||||
async function getList(domainId, pids, getHidden = false, doThrow = true) {
|
||||
pids = Array.from(new Set(pids));
|
||||
const r = {};
|
||||
const q = { $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] };
|
||||
if (!getHidden) q.hidden = false;
|
||||
const pdocs = await document.getMulti(domainId, document.TYPE_PROBLEM, q).toArray();
|
||||
for (const pdoc of pdocs) {
|
||||
r[pdoc.docId] = r[pdoc.pid] = pdoc;
|
||||
}
|
||||
if (pdocs.length !== pids.length) {
|
||||
for (const pid of pids) {
|
||||
if (!r[pid]) {
|
||||
if (doThrow) {
|
||||
throw new ProblemNotFoundError(domainId, pid);
|
||||
} else {
|
||||
r[pid] = new Problem();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
async function getListStatus(domainId, uid, pids) {
|
||||
const psdocs = await getMultiStatus(
|
||||
domainId, { uid, docId: { $in: Array.from(new Set(pids)) } },
|
||||
).toArray();
|
||||
const r = {};
|
||||
for (const psdoc of psdocs) r[psdoc.docId] = psdoc;
|
||||
return r;
|
||||
}
|
||||
|
||||
async function updateStatus(domainId, pid, uid, rid, status) {
|
||||
try {
|
||||
await document.setIfNotStatus(domainId, document.TYPE_PROBLEM, pid, uid, 'status', status, STATUS_ACCEPTED, { rid });
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function setStar(domainId, pid, uid, star) {
|
||||
return document.setStatus(domainId, document.TYPE_PROBLEM, pid, uid, { star });
|
||||
}
|
||||
|
||||
async function setTestdata(domainId, _id, filePath) {
|
||||
const pdoc = await get(domainId, _id);
|
||||
const config = await readConfig(filePath);
|
||||
const id = await file.add(filePath, 'data.zip');
|
||||
if (pdoc.data && typeof pdoc.data === 'object') file.dec(pdoc.data);
|
||||
return await edit(domainId, _id, { data: id, config });
|
||||
}
|
||||
|
||||
global.Hydro.model.problem = module.exports = {
|
||||
add,
|
||||
inc,
|
||||
get,
|
||||
getMany,
|
||||
edit,
|
||||
count,
|
||||
random,
|
||||
getMulti,
|
||||
getList,
|
||||
getListStatus,
|
||||
getMultiStatus,
|
||||
setStar,
|
||||
setTestdata,
|
||||
updateStatus,
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
import { ObjectID, FilterQuery } from 'mongodb';
|
||||
import { STATUS } from './builtin';
|
||||
import * as file from './file';
|
||||
import * as document from './document';
|
||||
import * as domain from './domain';
|
||||
import { ProblemNotFoundError } from '../error';
|
||||
import readConfig from '../lib/readConfig';
|
||||
|
||||
export interface Pdoc {
|
||||
_id: ObjectID
|
||||
domainId: string
|
||||
docId: number
|
||||
pid: string
|
||||
owner: number
|
||||
title: string
|
||||
content: string
|
||||
nSubmit: number
|
||||
nAccept: number
|
||||
tag: string[]
|
||||
category: string[],
|
||||
data: ObjectID | null
|
||||
hidden: boolean
|
||||
config: string
|
||||
}
|
||||
|
||||
export const pdocHidden: Pdoc = {
|
||||
_id: new ObjectID(),
|
||||
domainId: 'system',
|
||||
docId: -1,
|
||||
pid: '',
|
||||
owner: 1,
|
||||
title: '*',
|
||||
content: '',
|
||||
nSubmit: 0,
|
||||
nAccept: 0,
|
||||
tag: [],
|
||||
category: [],
|
||||
data: null,
|
||||
hidden: true,
|
||||
config: '',
|
||||
};
|
||||
|
||||
export async function add(domainId: string, title: string, content: string, owner: number, {
|
||||
pid = null,
|
||||
data = null,
|
||||
category = [],
|
||||
tag = [],
|
||||
hidden = false,
|
||||
}) {
|
||||
const d = await domain.inc(domainId, 'pidCounter', 1);
|
||||
if (!pid) pid = d.pidCounter.toString();
|
||||
return await document.add(
|
||||
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
|
||||
{
|
||||
pid, title, data, category, tag, hidden, nSubmit: 0, nAccept: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function get(
|
||||
domainId: string, pid: string | number,
|
||||
uid: number = null, doThrow = true,
|
||||
): Promise<Pdoc> {
|
||||
if (typeof pid !== 'number') {
|
||||
if (!Number.isNaN(parseInt(pid, 10))) pid = parseInt(pid, 10);
|
||||
else if (doThrow) throw new ProblemNotFoundError(domainId, pid);
|
||||
else return null;
|
||||
}
|
||||
const pdoc = Number.isInteger(pid)
|
||||
? await document.get(domainId, document.TYPE_PROBLEM, pid)
|
||||
: (await document.getMulti(domainId, document.TYPE_PROBLEM, { pid }).toArray())[0];
|
||||
if (!pdoc) {
|
||||
if (doThrow) throw new ProblemNotFoundError(domainId, pid);
|
||||
return null;
|
||||
}
|
||||
if (uid) {
|
||||
pdoc.psdoc = await document.getStatus(domainId, document.TYPE_PROBLEM, pdoc.docId, uid);
|
||||
}
|
||||
return pdoc;
|
||||
}
|
||||
|
||||
export function getMany(
|
||||
domainId: string, query: any, sort: any, page: number, limit: number,
|
||||
): Promise<Pdoc[]> {
|
||||
return document.getMulti(domainId, query)
|
||||
.sort(sort).skip((page - 1) * limit).limit(limit)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
export function getMulti(domainId: string, query: object) {
|
||||
return document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
||||
}
|
||||
|
||||
export function getMultiStatus(domainId: string, query: object) {
|
||||
return document.getMultiStatus(domainId, document.TYPE_PROBLEM, query);
|
||||
}
|
||||
|
||||
export function edit(domainId: string, _id: number, $set: any): Promise<Pdoc> {
|
||||
return document.set(domainId, document.TYPE_PROBLEM, _id, $set);
|
||||
}
|
||||
|
||||
export function inc(domainId: string, _id: number, field: string, n: number) {
|
||||
return document.inc(domainId, document.TYPE_PROBLEM, _id, field, n);
|
||||
}
|
||||
|
||||
export function count<T>(domainId: string, query: FilterQuery<T>) {
|
||||
return document.count(domainId, document.TYPE_PROBLEM, query);
|
||||
}
|
||||
|
||||
export async function random<T>(domainId: string, query: FilterQuery<T>) {
|
||||
const cursor = document.getMulti(domainId, document.TYPE_PROBLEM, query);
|
||||
const pcount = await cursor.count();
|
||||
if (pcount) {
|
||||
const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray();
|
||||
return pdoc[0].pid;
|
||||
} return null;
|
||||
}
|
||||
|
||||
export async function getList(domainId: string, pids: number[], getHidden = false, doThrow = true) {
|
||||
pids = Array.from(new Set(pids));
|
||||
const r = {};
|
||||
const q: any = { $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }] };
|
||||
if (!getHidden) q.hidden = false;
|
||||
const pdocs = await document.getMulti(domainId, document.TYPE_PROBLEM, q).toArray();
|
||||
for (const pdoc of pdocs) {
|
||||
r[pdoc.docId] = pdoc;
|
||||
r[pdoc.pid] = pdoc;
|
||||
}
|
||||
if (pdocs.length !== pids.length) {
|
||||
for (const pid of pids) {
|
||||
if (!r[pid]) {
|
||||
if (doThrow) {
|
||||
throw new ProblemNotFoundError(domainId, pid);
|
||||
} else {
|
||||
r[pid] = pdocHidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
export async function getListStatus(domainId: string, uid: number, pids: number[]) {
|
||||
const psdocs = await getMultiStatus(
|
||||
domainId, { uid, docId: { $in: Array.from(new Set(pids)) } },
|
||||
).toArray();
|
||||
const r = {};
|
||||
for (const psdoc of psdocs) r[psdoc.docId] = psdoc;
|
||||
return r;
|
||||
}
|
||||
|
||||
export async function updateStatus(
|
||||
domainId: string, pid: number, uid: number,
|
||||
rid: ObjectID, status: number,
|
||||
) {
|
||||
try {
|
||||
await document.setIfNotStatus(
|
||||
domainId, document.TYPE_PROBLEM, pid, uid,
|
||||
'status', status, STATUS.STATUS_ACCEPTED, { rid },
|
||||
);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function setStar(domainId: string, pid: number, uid: number, star: boolean) {
|
||||
return document.setStatus(domainId, document.TYPE_PROBLEM, pid, uid, { star });
|
||||
}
|
||||
|
||||
export async function setTestdata(domainId: string, _id: number, filePath: string) {
|
||||
const pdoc = await get(domainId, _id);
|
||||
const config = await readConfig(filePath);
|
||||
const id = await file.add(filePath, 'data.zip');
|
||||
if (pdoc.data && typeof pdoc.data === 'object') file.dec(pdoc.data);
|
||||
return await edit(domainId, _id, { data: id, config });
|
||||
}
|
||||
|
||||
global.Hydro.model.problem = {
|
||||
add,
|
||||
inc,
|
||||
get,
|
||||
getMany,
|
||||
edit,
|
||||
count,
|
||||
random,
|
||||
getMulti,
|
||||
getList,
|
||||
getListStatus,
|
||||
getMultiStatus,
|
||||
setStar,
|
||||
setTestdata,
|
||||
updateStatus,
|
||||
};
|
||||
@@ -1,161 +0,0 @@
|
||||
const _ = require('lodash');
|
||||
const { ObjectID } = require('mongodb');
|
||||
const { STATUS_WAITING } = require('./builtin').STATUS;
|
||||
const task = require('./task');
|
||||
const problem = require('./problem');
|
||||
const { RecordNotFoundError } = require('../error');
|
||||
const db = require('../service/db');
|
||||
|
||||
const coll = db.collection('record');
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {import('../interface').Record} data
|
||||
*/
|
||||
async function add(domainId, data, addTask = true) {
|
||||
_.defaults(data, {
|
||||
status: STATUS_WAITING,
|
||||
domainId,
|
||||
score: 0,
|
||||
time: 0,
|
||||
memory: 0,
|
||||
hidden: false,
|
||||
judgeTexts: [],
|
||||
compilerTexts: [],
|
||||
testCases: [],
|
||||
judger: null,
|
||||
judgeAt: null,
|
||||
});
|
||||
const [pdoc, res] = await Promise.all([
|
||||
problem.get(domainId, data.pid, null, false),
|
||||
coll.insertOne(data),
|
||||
]);
|
||||
if (addTask) {
|
||||
const t = {
|
||||
type: data.type || 'judge',
|
||||
event: data.type || 'judge',
|
||||
rid: res.insertedId,
|
||||
domainId,
|
||||
pid: data.pid,
|
||||
lang: data.lang,
|
||||
code: data.code,
|
||||
};
|
||||
if (t.type === 'judge') {
|
||||
t.data = pdoc.data;
|
||||
t.config = pdoc.config;
|
||||
} else {
|
||||
t.config = {
|
||||
time: data.time,
|
||||
memory: data.memory,
|
||||
input: data.input,
|
||||
};
|
||||
}
|
||||
await task.add(t);
|
||||
}
|
||||
return res.insertedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} domainId
|
||||
* @param {ObjectID} rid
|
||||
* @returns {import('../interface').Record}
|
||||
*/
|
||||
async function get(domainId, rid) {
|
||||
const _id = new ObjectID(rid);
|
||||
const rdoc = await coll.findOne({ domainId, _id });
|
||||
if (!rdoc) throw new RecordNotFoundError(rid);
|
||||
return rdoc;
|
||||
}
|
||||
|
||||
function getMulti(domainId, query) {
|
||||
return coll.find({ ...query, domainId });
|
||||
}
|
||||
|
||||
async function update(domainId, rid, $set, $push, $unset) {
|
||||
const _id = new ObjectID(rid);
|
||||
const $update = {};
|
||||
if ($set && Object.keys($set).length) $update.$set = $set;
|
||||
if ($push && Object.keys($push).length) $update.$push = $push;
|
||||
if ($unset && Object.keys($unset).length) $update.$unset = $unset;
|
||||
const res = await coll.findOneAndUpdate({ domainId, _id }, $update, { returnOriginal: false });
|
||||
if (!res.value) throw new RecordNotFoundError(rid);
|
||||
return res.value;
|
||||
}
|
||||
|
||||
function reset(domainId, rid) {
|
||||
return update(domainId, rid, {
|
||||
score: 0,
|
||||
status: STATUS_WAITING,
|
||||
time: 0,
|
||||
memory: 0,
|
||||
testCases: [],
|
||||
judgeTexts: [],
|
||||
compilerTexts: [],
|
||||
judgeAt: null,
|
||||
judger: null,
|
||||
rejudged: true,
|
||||
});
|
||||
}
|
||||
|
||||
function count(domainId, query) {
|
||||
return coll.find({ domainId, ...query }).count();
|
||||
}
|
||||
|
||||
async function getList(domainId, rids, showHidden = false) {
|
||||
const r = {};
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
for (const rid of rids) r[rid] = await get(domainId, rid, showHidden);
|
||||
return r;
|
||||
}
|
||||
|
||||
function getUserInProblemMulti(domainId, uid, pid) {
|
||||
return coll.find({ domainId, uid, pid });
|
||||
}
|
||||
|
||||
function getByUid(domainId, uid) {
|
||||
return coll.find({ domainId, uid }).toArray();
|
||||
}
|
||||
|
||||
async function judge(domainId, rid) {
|
||||
const rdoc = await get(domainId, rid);
|
||||
const pdoc = await problem.get(domainId, rdoc.pid);
|
||||
await task.add({
|
||||
type: 'judge',
|
||||
rid,
|
||||
domainId,
|
||||
config: pdoc.config || '',
|
||||
pid: rdoc.pid,
|
||||
data: pdoc.data,
|
||||
lang: rdoc.lang,
|
||||
code: rdoc.code,
|
||||
});
|
||||
}
|
||||
|
||||
async function rejudge(domainId, rid) {
|
||||
await reset(domainId, rid);
|
||||
const rdoc = await get(domainId, rid);
|
||||
const pdoc = await problem.get(domainId, rdoc.pid);
|
||||
await task.add({
|
||||
type: 'judge',
|
||||
rid,
|
||||
domainId,
|
||||
pid: rdoc.pid,
|
||||
data: pdoc.data,
|
||||
lang: rdoc.lang,
|
||||
code: rdoc.code,
|
||||
});
|
||||
}
|
||||
|
||||
global.Hydro.model.record = module.exports = {
|
||||
add,
|
||||
get,
|
||||
getMulti,
|
||||
update,
|
||||
count,
|
||||
reset,
|
||||
getList,
|
||||
getUserInProblemMulti,
|
||||
getByUid,
|
||||
judge,
|
||||
rejudge,
|
||||
};
|
||||
@@ -0,0 +1,233 @@
|
||||
import _ from 'lodash';
|
||||
import yaml from 'js-yaml';
|
||||
import { ObjectID, FilterQuery } from 'mongodb';
|
||||
import { STATUS } from './builtin';
|
||||
import * as task from './task';
|
||||
import * as problem from './problem';
|
||||
import { RecordNotFoundError } from '../error';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('record');
|
||||
|
||||
export interface TestCase {
|
||||
time: number,
|
||||
memory: number,
|
||||
status: number,
|
||||
message: string,
|
||||
}
|
||||
|
||||
export interface Rdoc {
|
||||
_id: ObjectID,
|
||||
domainId: string,
|
||||
pid: ObjectID
|
||||
uid: number,
|
||||
lang: string,
|
||||
code: string,
|
||||
score: number,
|
||||
memory: number,
|
||||
time: number,
|
||||
judgeTexts: string[],
|
||||
compilerTexts: string[],
|
||||
testCases: TestCase[],
|
||||
rejudged: boolean,
|
||||
judger: string,
|
||||
judgeAt: Date,
|
||||
status: number,
|
||||
type: string,
|
||||
hidden: boolean,
|
||||
input?: string,
|
||||
tid?: ObjectID,
|
||||
ttype?: number,
|
||||
}
|
||||
|
||||
export interface RdocBase {
|
||||
_id?: ObjectID,
|
||||
domainId?: string,
|
||||
pid: number
|
||||
uid: number,
|
||||
lang: string,
|
||||
code: string,
|
||||
score?: number,
|
||||
memory?: number,
|
||||
time?: number,
|
||||
judgeTexts?: string[],
|
||||
compilerTexts?: string[],
|
||||
testCases?: TestCase[],
|
||||
rejudged?: boolean,
|
||||
judger?: string,
|
||||
judgeAt?: Date,
|
||||
status?: number,
|
||||
type?: string,
|
||||
hidden?: boolean,
|
||||
input?: string,
|
||||
tid?: ObjectID,
|
||||
ttype?: number,
|
||||
}
|
||||
|
||||
export interface JudgeTask {
|
||||
_id: ObjectID,
|
||||
rid: ObjectID,
|
||||
domainId: string,
|
||||
pid: number
|
||||
lang: string,
|
||||
code: string,
|
||||
data?: ObjectID,
|
||||
config: string,
|
||||
type?: string,
|
||||
}
|
||||
|
||||
export async function add(domainId: string, data: RdocBase, addTask = true) {
|
||||
_.defaults(data, {
|
||||
status: STATUS.STATUS_WAITING,
|
||||
type: 'judge',
|
||||
domainId,
|
||||
score: 0,
|
||||
time: 0,
|
||||
memory: 0,
|
||||
hidden: false,
|
||||
judgeTexts: [],
|
||||
compilerTexts: [],
|
||||
testCases: [],
|
||||
judger: null,
|
||||
judgeAt: null,
|
||||
});
|
||||
const [pdoc, res] = await Promise.all([
|
||||
problem.get(domainId, data.pid, null, false),
|
||||
coll.insertOne(data),
|
||||
]);
|
||||
if (addTask) {
|
||||
const t: any = {
|
||||
type: data.type || 'judge',
|
||||
rid: res.insertedId,
|
||||
domainId,
|
||||
pid: data.pid,
|
||||
lang: data.lang,
|
||||
code: data.code,
|
||||
};
|
||||
if (t.type === 'judge') {
|
||||
t.data = pdoc.data;
|
||||
t.config = pdoc.config;
|
||||
} else {
|
||||
t.config = yaml.safeDump({
|
||||
time: data.time,
|
||||
memory: data.memory,
|
||||
input: data.input,
|
||||
});
|
||||
}
|
||||
await task.add(t);
|
||||
}
|
||||
return res.insertedId;
|
||||
}
|
||||
|
||||
export async function get(domainId: string, _id: ObjectID) {
|
||||
const rdoc = await coll.findOne({ domainId, _id });
|
||||
if (!rdoc) throw new RecordNotFoundError(_id);
|
||||
return rdoc;
|
||||
}
|
||||
|
||||
export function getMulti(domainId: string, query: any) {
|
||||
return coll.find({ ...query, domainId });
|
||||
}
|
||||
|
||||
export async function update(
|
||||
domainId: string, _id: ObjectID,
|
||||
$set: any = {}, $push: any = {}, $unset: any = {},
|
||||
) {
|
||||
const $update: any = {};
|
||||
if ($set && Object.keys($set).length) $update.$set = $set;
|
||||
if ($push && Object.keys($push).length) $update.$push = $push;
|
||||
if ($unset && Object.keys($unset).length) $update.$unset = $unset;
|
||||
const res = await coll.findOneAndUpdate({ domainId, _id }, $update, { returnOriginal: false });
|
||||
if (!res.value) throw new RecordNotFoundError(_id);
|
||||
return res.value;
|
||||
}
|
||||
|
||||
export function reset(domainId: string, rid: ObjectID) {
|
||||
return update(domainId, rid, {
|
||||
score: 0,
|
||||
status: STATUS.STATUS_WAITING,
|
||||
time: 0,
|
||||
memory: 0,
|
||||
testCases: [],
|
||||
judgeTexts: [],
|
||||
compilerTexts: [],
|
||||
judgeAt: null,
|
||||
judger: null,
|
||||
rejudged: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function count<T>(domainId: string, query: FilterQuery<T>) {
|
||||
return coll.find({ domainId, ...query }).count();
|
||||
}
|
||||
|
||||
export async function getList(domainId: string, rids: ObjectID[], showHidden = false) {
|
||||
const r = {};
|
||||
for (const rid of rids) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const rdoc = await get(domainId, rid);
|
||||
if (rdoc.hidden && !showHidden) r[rid.toHexString()] = null;
|
||||
else r[rid.toHexString()] = rdoc;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
export function getUserInProblemMulti(
|
||||
domainId: string, uid: number, pid: number,
|
||||
getHidden = false,
|
||||
) {
|
||||
if (!getHidden) {
|
||||
return coll.find({
|
||||
domainId, uid, pid, hidden: false,
|
||||
});
|
||||
}
|
||||
return coll.find({ domainId, uid, pid });
|
||||
}
|
||||
|
||||
export function getByUid(domainId: string, uid: number) {
|
||||
return coll.find({ domainId, uid }).toArray();
|
||||
}
|
||||
|
||||
export async function judge(domainId: string, rid: ObjectID) {
|
||||
const rdoc = await get(domainId, rid);
|
||||
const pdoc = await problem.get(domainId, rdoc.pid);
|
||||
await task.add({
|
||||
type: 'judge',
|
||||
rid,
|
||||
domainId,
|
||||
config: pdoc.config || '',
|
||||
pid: rdoc.pid,
|
||||
data: pdoc.data,
|
||||
lang: rdoc.lang,
|
||||
code: rdoc.code,
|
||||
});
|
||||
}
|
||||
|
||||
export async function rejudge(domainId: string, rid: ObjectID) {
|
||||
await reset(domainId, rid);
|
||||
const rdoc = await get(domainId, rid);
|
||||
const pdoc = await problem.get(domainId, rdoc.pid);
|
||||
await task.add({
|
||||
type: 'judge',
|
||||
rid,
|
||||
domainId,
|
||||
pid: rdoc.pid,
|
||||
data: pdoc.data,
|
||||
lang: rdoc.lang,
|
||||
code: rdoc.code,
|
||||
});
|
||||
}
|
||||
|
||||
global.Hydro.model.record = {
|
||||
add,
|
||||
get,
|
||||
getMulti,
|
||||
update,
|
||||
count,
|
||||
reset,
|
||||
getList,
|
||||
getUserInProblemMulti,
|
||||
getByUid,
|
||||
judge,
|
||||
rejudge,
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
const moment = require('moment-timezone');
|
||||
const builtin = require('./builtin');
|
||||
import moment from 'moment-timezone';
|
||||
import * as builtin from './builtin';
|
||||
|
||||
const countries = moment.tz.countries();
|
||||
const tzs = new Set();
|
||||
@@ -9,22 +9,22 @@ for (const country of countries) {
|
||||
}
|
||||
const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]);
|
||||
|
||||
const FLAG_HIDDEN = 1;
|
||||
const FLAG_DISABLED = 2;
|
||||
const FLAG_SECRET = 4;
|
||||
export const FLAG_HIDDEN = 1;
|
||||
export const FLAG_DISABLED = 2;
|
||||
export const FLAG_SECRET = 4;
|
||||
|
||||
const PREFERENCE_SETTINGS = [];
|
||||
const ACCOUNT_SETTINGS = [];
|
||||
const DOMAIN_SETTINGS = [];
|
||||
const DOMAIN_USER_SETTINGS = [];
|
||||
const SYSTEM_SETTINGS = [];
|
||||
const SETTINGS = [];
|
||||
const SETTINGS_BY_KEY = {};
|
||||
const DOMAIN_USER_SETTINGS_BY_KEY = {};
|
||||
const DOMAIN_SETTINGS_BY_KEY = {};
|
||||
const SYSTEM_SETTINGS_BY_KEY = {};
|
||||
export const PREFERENCE_SETTINGS = [];
|
||||
export const ACCOUNT_SETTINGS = [];
|
||||
export const DOMAIN_SETTINGS = [];
|
||||
export const DOMAIN_USER_SETTINGS = [];
|
||||
export const SYSTEM_SETTINGS = [];
|
||||
export const SETTINGS = [];
|
||||
export const SETTINGS_BY_KEY = {};
|
||||
export const DOMAIN_USER_SETTINGS_BY_KEY = {};
|
||||
export const DOMAIN_SETTINGS_BY_KEY = {};
|
||||
export const SYSTEM_SETTINGS_BY_KEY = {};
|
||||
|
||||
const Setting = (
|
||||
export const Setting = (
|
||||
family, key, range = null,
|
||||
value = null, type = 'text', name = '',
|
||||
desc = '', flag = 0,
|
||||
@@ -32,33 +32,33 @@ const Setting = (
|
||||
family, key, range, value, type, name, desc, flag,
|
||||
});
|
||||
|
||||
const PreferenceSetting = (...settings) => {
|
||||
export const PreferenceSetting = (...settings) => {
|
||||
for (const setting of settings) {
|
||||
PREFERENCE_SETTINGS.push(setting);
|
||||
SETTINGS.push(setting);
|
||||
SETTINGS_BY_KEY[setting.key] = setting;
|
||||
}
|
||||
};
|
||||
const AccountSetting = (...settings) => {
|
||||
export const AccountSetting = (...settings) => {
|
||||
for (const setting of settings) {
|
||||
ACCOUNT_SETTINGS.push(setting);
|
||||
SETTINGS.push(setting);
|
||||
SETTINGS_BY_KEY[setting.key] = setting;
|
||||
}
|
||||
};
|
||||
const DomainUserSetting = (...settings) => {
|
||||
export const DomainUserSetting = (...settings) => {
|
||||
for (const setting of settings) {
|
||||
DOMAIN_USER_SETTINGS.push(setting);
|
||||
DOMAIN_USER_SETTINGS_BY_KEY[setting.key] = setting;
|
||||
}
|
||||
};
|
||||
const DomainSetting = (...settings) => {
|
||||
export const DomainSetting = (...settings) => {
|
||||
for (const setting of settings) {
|
||||
DOMAIN_SETTINGS.push(setting);
|
||||
DOMAIN_SETTINGS_BY_KEY[setting.key] = setting;
|
||||
}
|
||||
};
|
||||
const SystemSetting = (...settings) => {
|
||||
export const SystemSetting = (...settings) => {
|
||||
for (const setting of settings) {
|
||||
SYSTEM_SETTINGS.push(setting);
|
||||
SYSTEM_SETTINGS_BY_KEY[setting.key] = setting;
|
||||
@@ -130,7 +130,7 @@ SystemSetting(
|
||||
Setting('setting_constant', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page'),
|
||||
);
|
||||
|
||||
global.Hydro.model.setting = module.exports = {
|
||||
global.Hydro.model.setting = {
|
||||
Setting,
|
||||
PreferenceSetting,
|
||||
AccountSetting,
|
||||
@@ -1,67 +1,63 @@
|
||||
const document = require('./document');
|
||||
const { SolutionNotFoundError } = require('../error');
|
||||
import { ObjectID } from 'mongodb';
|
||||
import * as document from './document';
|
||||
import { SolutionNotFoundError } from '../error';
|
||||
|
||||
/**
|
||||
* @param {string} pid
|
||||
* @param {number} owner
|
||||
* @param {string} content
|
||||
*/
|
||||
function add(domainId, pid, owner, content) {
|
||||
export function add(domainId: string, pid: number, owner: number, content: string) {
|
||||
return document.add(
|
||||
domainId, content, owner, document.TYPE_PROBLEM_SOLUTION,
|
||||
null, document.TYPE_PROBLEM, pid, { reply: [], vote: 0 },
|
||||
);
|
||||
}
|
||||
|
||||
async function get(domainId, psid) {
|
||||
export async function get(domainId: string, psid: ObjectID) {
|
||||
const psdoc = await document.get(domainId, document.TYPE_PROBLEM_SOLUTION, psid);
|
||||
if (!psdoc) throw new SolutionNotFoundError();
|
||||
return psdoc;
|
||||
}
|
||||
|
||||
function getMany(domainId, query, sort, page, limit) {
|
||||
export function getMany(domainId: string, query: any, sort: any, page: number, limit: number) {
|
||||
return document.getMulti(domainId, document.TYPE_PROBLEM_SOLUTION, query)
|
||||
.sort(sort)
|
||||
.skip((page - 1) * limit).limit(limit)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
function edit(domainId, psid, content) {
|
||||
export function edit(domainId: string, psid: ObjectID, content: string) {
|
||||
return document.set(domainId, document.TYPE_PROBLEM_SOLUTION, psid, { content });
|
||||
}
|
||||
|
||||
function del(domainId, psid) {
|
||||
export function del(domainId: string, psid: ObjectID) {
|
||||
return document.deleteOne(domainId, document.TYPE_PROBLEM_SOLUTION, psid);
|
||||
}
|
||||
|
||||
function count(domainId, query) {
|
||||
export function count(domainId: string, query: any) {
|
||||
return document.count(domainId, document.TYPE_PROBLEM_SOLUTION, query);
|
||||
}
|
||||
|
||||
function getMulti(domainId, pid) {
|
||||
export function getMulti(domainId: string, pid: number) {
|
||||
return document.getMulti(
|
||||
domainId, document.TYPE_PROBLEM_SOLUTION,
|
||||
{ parentType: document.TYPE_PROBLEM, parentId: pid },
|
||||
).sort({ vote: -1 });
|
||||
}
|
||||
|
||||
function reply(domainId, psid, owner, content) {
|
||||
export function reply(domainId: string, psid: ObjectID, owner: number, content: string) {
|
||||
return document.push(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', content, owner);
|
||||
}
|
||||
|
||||
function getReply(domainId, psid, psrid) {
|
||||
export function getReply(domainId: string, psid: ObjectID, psrid: ObjectID) {
|
||||
return document.getSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
|
||||
}
|
||||
|
||||
function editReply(domainId, psid, psrid, content) {
|
||||
export function editReply(domainId: string, psid: ObjectID, psrid: ObjectID, content: string) {
|
||||
return document.setSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid, { content });
|
||||
}
|
||||
|
||||
function delReply(domainId, psid, psrid) {
|
||||
export function delReply(domainId: string, psid: ObjectID, psrid: ObjectID) {
|
||||
return document.deleteSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
|
||||
}
|
||||
|
||||
async function vote(domainId, psid, uid, value) {
|
||||
export async function vote(domainId: string, psid: ObjectID, uid: number, value: number) {
|
||||
let pssdoc = await document.getStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid);
|
||||
await document.setStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid, { vote: value });
|
||||
if (pssdoc) value += -pssdoc.vote;
|
||||
@@ -70,16 +66,16 @@ async function vote(domainId, psid, uid, value) {
|
||||
return [psdoc, pssdoc];
|
||||
}
|
||||
|
||||
async function getListStatus(domainId, list, uid) {
|
||||
export async function getListStatus(domainId: string, psids: ObjectID[], uid: number) {
|
||||
const result = {};
|
||||
const res = await document.getMultiStatus(
|
||||
domainId, document.TYPE_PROBLEM_SOLUTION, { uid, psid: { $in: list } },
|
||||
domainId, document.TYPE_PROBLEM_SOLUTION, { uid, psid: { $in: psids } },
|
||||
).toArray();
|
||||
for (const i of res) result[i.psid] = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
global.Hydro.model.solution = module.exports = {
|
||||
global.Hydro.model.solution = {
|
||||
count,
|
||||
add,
|
||||
get,
|
||||
@@ -1,14 +1,15 @@
|
||||
const db = require('../service/db');
|
||||
import { FindOneAndUpdateOption } from 'mongodb';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('system');
|
||||
|
||||
async function get(_id) {
|
||||
export async function get(_id: string) {
|
||||
const doc = await coll.findOne({ _id });
|
||||
if (doc) return doc.value;
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getMany(keys) {
|
||||
export async function getMany(keys: string[]) {
|
||||
const docs = await coll.find({ _id: { $in: keys } }).toArray();
|
||||
const dict = {};
|
||||
const res = [];
|
||||
@@ -21,24 +22,21 @@ async function getMany(keys) {
|
||||
return res;
|
||||
}
|
||||
|
||||
async function update(_id, operation, config) {
|
||||
export async function update(_id: string, operation: any, config: FindOneAndUpdateOption) {
|
||||
await coll.findOneAndUpdate({ _id }, operation, config);
|
||||
return get(_id);
|
||||
}
|
||||
|
||||
async function set(_id, value) {
|
||||
export async function set(_id: string, value: any) {
|
||||
await coll.findOneAndUpdate({ _id }, { $set: { value } }, { upsert: true });
|
||||
return get(_id);
|
||||
}
|
||||
/**
|
||||
* Increments the counter.
|
||||
* @returns {number} Integer value after increment.
|
||||
*/
|
||||
function inc(field) {
|
||||
|
||||
export function inc(field: string) {
|
||||
return update(field, { $inc: { value: 1 } }, { upsert: true });
|
||||
}
|
||||
|
||||
global.Hydro.model.system = module.exports = {
|
||||
global.Hydro.model.system = {
|
||||
get,
|
||||
getMany,
|
||||
update,
|
||||
@@ -1,9 +1,16 @@
|
||||
const moment = require('moment-timezone');
|
||||
const db = require('../service/db');
|
||||
import moment from 'moment-timezone';
|
||||
import { ObjectID } from 'mongodb';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('task');
|
||||
|
||||
async function add(task) {
|
||||
export interface Task {
|
||||
_id: ObjectID,
|
||||
count: number,
|
||||
executeAfter: number,
|
||||
}
|
||||
|
||||
export async function add(task: any) {
|
||||
const t = { ...task };
|
||||
if (typeof t.executeAfter === 'object') t.executeAfter = t.executeAfter.getTime();
|
||||
t.count = t.count || 1;
|
||||
@@ -12,19 +19,19 @@ async function add(task) {
|
||||
return res.insertedId;
|
||||
}
|
||||
|
||||
function get(_id) {
|
||||
export function get(_id: ObjectID) {
|
||||
return coll.findOne({ _id });
|
||||
}
|
||||
|
||||
function count(query) {
|
||||
export function count(query: any) {
|
||||
return coll.find(query).count();
|
||||
}
|
||||
|
||||
function del(_id) {
|
||||
export function del(_id: ObjectID) {
|
||||
return coll.deleteOne({ _id });
|
||||
}
|
||||
|
||||
async function getFirst(query) {
|
||||
export async function getFirst(query: any) {
|
||||
const q = { ...query };
|
||||
q.executeAfter = q.executeAfter || { $lt: new Date().getTime() };
|
||||
const res = await coll.find(q).sort('_id', 1).limit(1).toArray();
|
||||
@@ -40,7 +47,7 @@ async function getFirst(query) {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function consume(query, cb) {
|
||||
export async function consume(query, cb: Function) {
|
||||
let isRunning = false;
|
||||
const interval = setInterval(async () => {
|
||||
if (isRunning) return;
|
||||
@@ -57,6 +64,6 @@ async function consume(query, cb) {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
global.Hydro.model.task = module.exports = {
|
||||
global.Hydro.model.task = {
|
||||
add, get, del, count, getFirst, consume,
|
||||
};
|
||||
@@ -1,121 +0,0 @@
|
||||
const db = require('../service/db');
|
||||
const { ValidationError } = require('../error');
|
||||
|
||||
const coll = db.collection('token');
|
||||
|
||||
function ensureIndexes() {
|
||||
return Promise.all([
|
||||
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
|
||||
coll.createIndex('expireAt', { expireAfterSeconds: 0 }),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a token.
|
||||
* @param {number} tokenType type of the token.
|
||||
* @param {number} expireSeconds expire time, in seconds.
|
||||
* @param {object} data extra data.
|
||||
* @returns {Promise<Array>} token ID, token data
|
||||
*/
|
||||
async function add(tokenType, expireSeconds, data) {
|
||||
const now = new Date();
|
||||
const str = String.random(32);
|
||||
const res = await coll.insertOne({
|
||||
...data,
|
||||
_id: str,
|
||||
tokenType,
|
||||
createAt: now,
|
||||
updateAt: now,
|
||||
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
||||
});
|
||||
return [str, res.ops[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a token.
|
||||
* @param {string} tokenId token ID.
|
||||
* @param {number} tokenType type of the token.
|
||||
* @returns {Promise<object>} The token document, or null.
|
||||
*/
|
||||
async function get(tokenId, tokenType, doThrow = true) {
|
||||
const res = await coll.findOne({ _id: tokenId, tokenType });
|
||||
if (!res && doThrow) throw new ValidationError('token');
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a token.
|
||||
* @param {string} tokenId token ID.
|
||||
* @param {number} tokenType type of the token.
|
||||
* @param {number} expireSeconds expire time, in seconds.
|
||||
* @param {object} data extra data.
|
||||
* @returns {Promise<object>} The token document, or null.
|
||||
*/
|
||||
async function update(tokenId, tokenType, expireSeconds, data) {
|
||||
const now = new Date();
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ _id: tokenId, tokenType },
|
||||
{
|
||||
$set: {
|
||||
...data,
|
||||
updateAt: now,
|
||||
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
||||
tokenType,
|
||||
},
|
||||
},
|
||||
{ returnOriginal: false },
|
||||
);
|
||||
return res.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a token.
|
||||
* @param {string} tokenId token ID.
|
||||
* @param {int} tokenType type of the token.
|
||||
* @returns {Promise<boolean>} true if deleted, or false.
|
||||
*/
|
||||
async function del(tokenId, tokenType) {
|
||||
const result = await coll.deleteOne({ _id: tokenId, tokenType });
|
||||
return !!result.deletedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a token(or create one)
|
||||
* @param {number} tokenType type of the token.
|
||||
* @param {number} expireSeconds expire time, in seconds.
|
||||
* @param {object} data extra data.
|
||||
* @returns {Array} token ID, token data
|
||||
*/
|
||||
async function createOrUpdate(tokenType, expireSeconds, data) {
|
||||
const d = await coll.findOne({ tokenType, ...data });
|
||||
if (!d) {
|
||||
const res = await add(tokenType, expireSeconds, data);
|
||||
return res[0];
|
||||
}
|
||||
await update(d._id, tokenType, expireSeconds, data);
|
||||
return d._id;
|
||||
}
|
||||
|
||||
global.Hydro.model.token = module.exports = {
|
||||
TYPE_SESSION: 0,
|
||||
TYPE_CSRF_TOKEN: 1,
|
||||
TYPE_REGISTER: 2,
|
||||
TYPE_CHANGEMAIL: 3,
|
||||
TYPE_OAUTH: 4,
|
||||
|
||||
ensureIndexes,
|
||||
add,
|
||||
createOrUpdate,
|
||||
get,
|
||||
update,
|
||||
del,
|
||||
delByUid(uid) {
|
||||
return coll.deleteMany({ uid });
|
||||
},
|
||||
getMostRecentSessionByUid(uid) {
|
||||
return coll.findOne({ uid, tokenType: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
|
||||
},
|
||||
getSessionListByUid(uid) {
|
||||
return coll.find({ uid, tokenType: this.TYPE_SESSION }).sort('updateAt', -1).toArray();
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
import { ValidationError } from '../error';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('token');
|
||||
|
||||
export const TYPE_SESSION = 0;
|
||||
export const TYPE_CSRF_TOKEN = 1;
|
||||
export const TYPE_REGISTRATION = 2;
|
||||
export const TYPE_CHANGEMAIL = 3;
|
||||
export const TYPE_OAUTH = 4;
|
||||
export const TYPE_LOSTPASS = 5;
|
||||
|
||||
export function ensureIndexes() {
|
||||
return Promise.all([
|
||||
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
|
||||
coll.createIndex('expireAt', { expireAfterSeconds: 0 }),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function add(tokenType: number, expireSeconds: number, data: any) {
|
||||
const now = new Date();
|
||||
const str = String.random(32);
|
||||
const res = await coll.insertOne({
|
||||
...data,
|
||||
_id: str,
|
||||
tokenType,
|
||||
createAt: now,
|
||||
updateAt: now,
|
||||
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
||||
});
|
||||
return [str, res.ops[0]];
|
||||
}
|
||||
|
||||
export async function get(tokenId: string, tokenType: number, doThrow = true) {
|
||||
const res = await coll.findOne({ _id: tokenId, tokenType });
|
||||
if (!res && doThrow) throw new ValidationError('token');
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function update(
|
||||
tokenId: string, tokenType: number, expireSeconds: number,
|
||||
data: object,
|
||||
) {
|
||||
const now = new Date();
|
||||
const res = await coll.findOneAndUpdate(
|
||||
{ _id: tokenId, tokenType },
|
||||
{
|
||||
$set: {
|
||||
...data,
|
||||
updateAt: now,
|
||||
expireAt: new Date(now.getTime() + expireSeconds * 1000),
|
||||
tokenType,
|
||||
},
|
||||
},
|
||||
{ returnOriginal: false },
|
||||
);
|
||||
return res.value;
|
||||
}
|
||||
|
||||
export async function del(tokenId: string, tokenType: number) {
|
||||
const result = await coll.deleteOne({ _id: tokenId, tokenType });
|
||||
return !!result.deletedCount;
|
||||
}
|
||||
|
||||
export async function createOrUpdate(tokenType: number, expireSeconds: number, data: object) {
|
||||
const d = await coll.findOne({ tokenType, ...data });
|
||||
if (!d) {
|
||||
const res = await add(tokenType, expireSeconds, data);
|
||||
return res[0];
|
||||
}
|
||||
await update(d._id, tokenType, expireSeconds, data);
|
||||
return d._id;
|
||||
}
|
||||
|
||||
export function getSessionListByUid(uid: number) {
|
||||
return coll.find({ uid, tokenType: this.TYPE_SESSION }).sort('updateAt', -1).toArray();
|
||||
}
|
||||
|
||||
export function getMostRecentSessionByUid(uid: number) {
|
||||
return coll.findOne({ uid, tokenType: this.TYPE_SESSION }, { sort: { updateAt: -1 } });
|
||||
}
|
||||
|
||||
export function delByUid(uid: number) {
|
||||
return coll.deleteMany({ uid });
|
||||
}
|
||||
|
||||
global.Hydro.model.token = {
|
||||
TYPE_SESSION,
|
||||
TYPE_CHANGEMAIL,
|
||||
TYPE_CSRF_TOKEN,
|
||||
TYPE_OAUTH,
|
||||
TYPE_REGISTRATION,
|
||||
|
||||
ensureIndexes,
|
||||
add,
|
||||
createOrUpdate,
|
||||
get,
|
||||
update,
|
||||
del,
|
||||
delByUid,
|
||||
getMostRecentSessionByUid,
|
||||
getSessionListByUid,
|
||||
};
|
||||
@@ -1,107 +0,0 @@
|
||||
const document = require('./document');
|
||||
const { TrainingNotFoundError, TrainingAlreadyEnrollError } = require('../error');
|
||||
|
||||
function getStatus(domainId, tid, uid) {
|
||||
return document.getStatus(domainId, document.TYPE_TRAINING, tid, uid);
|
||||
}
|
||||
|
||||
function getMultiStatus(domainId, query) {
|
||||
return document.getMultiStatus(domainId, document.TYPE_TRAINING, query);
|
||||
}
|
||||
|
||||
async function getListStatus(domainId, uid, tids) {
|
||||
const tsdocs = await getMultiStatus(
|
||||
domainId, { uid, tid: { $in: Array.from(new Set(tids)) } },
|
||||
).toArray();
|
||||
const r = {};
|
||||
for (const tsdoc of tsdocs) r[tsdoc.pid] = tsdoc;
|
||||
return r;
|
||||
}
|
||||
|
||||
async function enroll(domainId, tid, uid) {
|
||||
try {
|
||||
await document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, { enroll: 1 });
|
||||
} catch (e) {
|
||||
throw new TrainingAlreadyEnrollError(tid, uid);
|
||||
}
|
||||
return await document.inc(domainId, document.TYPE_TRAINING, tid, 'enroll', 1);
|
||||
}
|
||||
|
||||
function setStatus(domainId, tid, uid, $set) {
|
||||
return document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, $set);
|
||||
}
|
||||
|
||||
function add(domainId, title, content, owner, dag = [], description = '') {
|
||||
return document.add(domainId, content, owner, document.TYPE_TRAINING, null, null, null, {
|
||||
dag,
|
||||
title,
|
||||
description,
|
||||
enroll: 0,
|
||||
});
|
||||
}
|
||||
|
||||
function edit(domainId, tid, $set) {
|
||||
return document.set(domainId, document.TYPE_TRAINING, tid, $set);
|
||||
}
|
||||
|
||||
global.Hydro.model.training = module.exports = {
|
||||
getPids(tdoc) {
|
||||
const pids = new Set();
|
||||
for (const node of tdoc.dag) {
|
||||
for (const pid of node.pids) pids.add(pid);
|
||||
}
|
||||
return Array.from(pids);
|
||||
},
|
||||
isDone(node, doneNids, donePids) {
|
||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||
&& Set.isSuperset(new Set(donePids), new Set(node.pids)));
|
||||
},
|
||||
isProgress(node, doneNids, donePids, progPids) {
|
||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
||||
&& Set.intersection(
|
||||
Set.union(new Set(donePids), new Set(progPids)),
|
||||
new Set(node.pids),
|
||||
).size);
|
||||
},
|
||||
isOpen(node, doneNids, donePids, progPids) {
|
||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
||||
&& !Set.intersection(
|
||||
Set.union(new Set(donePids), new Set(progPids)),
|
||||
new Set(node.pids),
|
||||
).size);
|
||||
},
|
||||
isInvalid(node, doneNids) {
|
||||
return (!Set.isSuperset(new Set(doneNids), new Set(node.requireNids)));
|
||||
},
|
||||
add,
|
||||
edit,
|
||||
count: (domainId, query) => document.count(domainId, document.TYPE_TRAINING, query),
|
||||
async get(domainId, tid) {
|
||||
const tdoc = await document.get(domainId, document.TYPE_TRAINING, tid);
|
||||
if (!tdoc) throw new TrainingNotFoundError(tid);
|
||||
for (const i in tdoc.dag) {
|
||||
for (const j in tdoc.dag[i].pids) {
|
||||
if (Number.isSafeInteger(parseInt(tdoc.dag[i].pids[j], 10))) {
|
||||
tdoc.dag[i].pids[j] = parseInt(tdoc.dag[i].pids[j], 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tdoc;
|
||||
},
|
||||
async getList(domainId, tids) {
|
||||
const tdocs = await this.getMulti(
|
||||
domainId, { _id: { $in: Array.from(new Set(tids)) } },
|
||||
).toArray();
|
||||
const r = {};
|
||||
for (const tdoc of tdocs) r[tdoc.docId] = tdoc;
|
||||
return r;
|
||||
},
|
||||
getMulti: (domainId, query) => document.getMulti(domainId, document.TYPE_TRAINING, query),
|
||||
getMultiStatus,
|
||||
getStatus,
|
||||
enroll,
|
||||
setStatus,
|
||||
getListStatus,
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
import { ObjectID } from 'mongodb';
|
||||
import * as document from './document';
|
||||
import { TrainingNotFoundError, TrainingAlreadyEnrollError } from '../error';
|
||||
|
||||
export function getStatus(domainId: string, tid: ObjectID, uid: number) {
|
||||
return document.getStatus(domainId, document.TYPE_TRAINING, tid, uid);
|
||||
}
|
||||
|
||||
export function getMultiStatus(domainId: string, query: any) {
|
||||
return document.getMultiStatus(domainId, document.TYPE_TRAINING, query);
|
||||
}
|
||||
|
||||
export async function getListStatus(domainId: string, uid: number, tids: ObjectID[]) {
|
||||
const tsdocs = await getMultiStatus(
|
||||
domainId, { uid, tid: { $in: Array.from(new Set(tids)) } },
|
||||
).toArray();
|
||||
const r = {};
|
||||
for (const tsdoc of tsdocs) r[tsdoc.pid] = tsdoc;
|
||||
return r;
|
||||
}
|
||||
|
||||
export async function enroll(domainId: string, tid: ObjectID, uid: number) {
|
||||
try {
|
||||
await document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, { enroll: 1 });
|
||||
} catch (e) {
|
||||
throw new TrainingAlreadyEnrollError(tid, uid);
|
||||
}
|
||||
return await document.inc(domainId, document.TYPE_TRAINING, tid, 'enroll', 1);
|
||||
}
|
||||
|
||||
export function setStatus(domainId: string, tid: ObjectID, uid: number, $set: any) {
|
||||
return document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, $set);
|
||||
}
|
||||
|
||||
export function add(
|
||||
domainId: string, title: string, content: string,
|
||||
owner: number, dag = [], description = '',
|
||||
) {
|
||||
return document.add(domainId, content, owner, document.TYPE_TRAINING, null, null, null, {
|
||||
dag,
|
||||
title,
|
||||
description,
|
||||
enroll: 0,
|
||||
});
|
||||
}
|
||||
|
||||
export function edit(domainId: string, tid: ObjectID, $set) {
|
||||
return document.set(domainId, document.TYPE_TRAINING, tid, $set);
|
||||
}
|
||||
|
||||
export function getPids(tdoc): number[] {
|
||||
const pids: Set<number> = new Set();
|
||||
for (const node of tdoc.dag) {
|
||||
for (const pid of node.pids) pids.add(pid);
|
||||
}
|
||||
return Array.from(pids);
|
||||
}
|
||||
|
||||
export function isDone(node, doneNids, donePids) {
|
||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||
&& Set.isSuperset(new Set(donePids), new Set(node.pids)));
|
||||
}
|
||||
|
||||
export function isProgress(node, doneNids, donePids, progPids) {
|
||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
||||
&& Set.intersection(
|
||||
Set.union(new Set(donePids), new Set(progPids)),
|
||||
new Set(node.pids),
|
||||
).size);
|
||||
}
|
||||
|
||||
export function isOpen(node, doneNids, donePids, progPids) {
|
||||
return (Set.isSuperset(new Set(doneNids), new Set(node.requireNids))
|
||||
&& !Set.isSuperset(new Set(donePids), new Set(node.pids))
|
||||
&& !Set.intersection(
|
||||
Set.union(new Set(donePids), new Set(progPids)),
|
||||
new Set(node.pids),
|
||||
).size);
|
||||
}
|
||||
|
||||
export const isInvalid = (node, doneNids) =>
|
||||
!Set.isSuperset(new Set(doneNids), new Set(node.requireNids));
|
||||
|
||||
export const count = (domainId: string, query: any) =>
|
||||
document.count(domainId, document.TYPE_TRAINING, query);
|
||||
|
||||
export async function get(domainId, tid) {
|
||||
const tdoc = await document.get(domainId, document.TYPE_TRAINING, tid);
|
||||
if (!tdoc) throw new TrainingNotFoundError(tid);
|
||||
for (const i in tdoc.dag) {
|
||||
for (const j in tdoc.dag[i].pids) {
|
||||
if (Number.isSafeInteger(parseInt(tdoc.dag[i].pids[j], 10))) {
|
||||
tdoc.dag[i].pids[j] = parseInt(tdoc.dag[i].pids[j], 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tdoc;
|
||||
}
|
||||
|
||||
export async function getList(domainId: string, tids: ObjectID[]) {
|
||||
const tdocs = await this.getMulti(
|
||||
domainId, { _id: { $in: Array.from(new Set(tids)) } },
|
||||
).toArray();
|
||||
const r = {};
|
||||
for (const tdoc of tdocs) r[tdoc.docId] = tdoc;
|
||||
return r;
|
||||
}
|
||||
|
||||
export const getMulti = (domainId: string, query: any = {}) =>
|
||||
document.getMulti(domainId, document.TYPE_TRAINING, query);
|
||||
|
||||
global.Hydro.model.training = {
|
||||
getPids,
|
||||
isDone,
|
||||
isProgress,
|
||||
isOpen,
|
||||
isInvalid,
|
||||
add,
|
||||
edit,
|
||||
count,
|
||||
get,
|
||||
getList,
|
||||
getMulti,
|
||||
getMultiStatus,
|
||||
getStatus,
|
||||
enroll,
|
||||
setStatus,
|
||||
getListStatus,
|
||||
};
|
||||
@@ -1,15 +1,15 @@
|
||||
const { BUILTIN_ROLES, BUILTIN_USERS, PRIV } = require('./builtin');
|
||||
const document = require('./document');
|
||||
const system = require('./system');
|
||||
const token = require('./token');
|
||||
const setting = require('./setting');
|
||||
const { UserNotFoundError, UserAlreadyExistError, LoginError } = require('../error');
|
||||
const pwhash = require('../lib/hash.hydro');
|
||||
const db = require('../service/db');
|
||||
import * as document from './document';
|
||||
import * as system from './system';
|
||||
import * as token from './token';
|
||||
import * as setting from './setting';
|
||||
import { BUILTIN_ROLES, BUILTIN_USERS, PRIV } from './builtin';
|
||||
import { UserNotFoundError, UserAlreadyExistError, LoginError } from '../error';
|
||||
import pwhash from '../lib/hash.hydro';
|
||||
import * as db from '../service/db';
|
||||
|
||||
const coll = db.collection('user');
|
||||
|
||||
function setPassword(uid, password) {
|
||||
export function setPassword(uid: number, password: string) {
|
||||
const salt = String.random();
|
||||
return coll.findOneAndUpdate(
|
||||
{ _id: uid },
|
||||
@@ -17,7 +17,33 @@ function setPassword(uid, password) {
|
||||
);
|
||||
}
|
||||
|
||||
class USER {
|
||||
export class User {
|
||||
udoc: () => any;
|
||||
|
||||
dudoc: () => any;
|
||||
|
||||
_id: number;
|
||||
|
||||
mail: string;
|
||||
|
||||
uname: string;
|
||||
|
||||
salt: () => string;
|
||||
|
||||
hash: () => string;
|
||||
|
||||
hashType: string;
|
||||
|
||||
priv: number;
|
||||
|
||||
regat: Date;
|
||||
|
||||
loginat: Date;
|
||||
|
||||
perm: string;
|
||||
|
||||
role: string;
|
||||
|
||||
constructor(udoc, dudoc) {
|
||||
this.udoc = () => udoc;
|
||||
this.dudoc = () => dudoc;
|
||||
@@ -48,15 +74,15 @@ class USER {
|
||||
}
|
||||
}
|
||||
|
||||
hasPerm(p) {
|
||||
hasPerm(p: string) {
|
||||
return this.perm.includes(p);
|
||||
}
|
||||
|
||||
hasPriv(p) {
|
||||
hasPriv(p: number) {
|
||||
return this.priv & p;
|
||||
}
|
||||
|
||||
checkPassword(password) {
|
||||
checkPassword(password: string) {
|
||||
const h = global.Hydro.lib[`hash.${this.hashType}`];
|
||||
if (!h) throw new Error('Unknown hash method');
|
||||
if (!(h(password, this.salt(), this) === this.hash())) {
|
||||
@@ -67,7 +93,7 @@ class USER {
|
||||
}
|
||||
}
|
||||
|
||||
async function getInDomain(domainId, udoc) {
|
||||
export async function getInDomain(domainId: string, udoc: User) {
|
||||
let dudoc = await document.getStatus(domainId, document.TYPE_DOMAIN_USER, 0, udoc._id);
|
||||
dudoc = dudoc || {};
|
||||
if (udoc._id === 1) dudoc.role = 'guest';
|
||||
@@ -77,7 +103,7 @@ async function getInDomain(domainId, udoc) {
|
||||
return dudoc;
|
||||
}
|
||||
|
||||
async function getById(domainId, _id, throwError = false) {
|
||||
export async function getById(domainId: string, _id: number, throwError = false) {
|
||||
const udoc = _id === 0 || _id === 1
|
||||
? BUILTIN_USERS[_id]
|
||||
: await coll.findOne({ _id });
|
||||
@@ -86,18 +112,18 @@ async function getById(domainId, _id, throwError = false) {
|
||||
else return null;
|
||||
}
|
||||
const dudoc = await getInDomain(domainId, udoc);
|
||||
return new USER(udoc, dudoc);
|
||||
return new User(udoc, dudoc);
|
||||
}
|
||||
|
||||
async function getList(domainId, uids) {
|
||||
uids = new Set(uids);
|
||||
export async function getList(domainId: string, uids: number[]) {
|
||||
const _uids = new Set(uids);
|
||||
const r = {};
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
for (const uid of uids) r[uid] = await getById(domainId, uid);
|
||||
for (const uid of _uids) r[uid] = await getById(domainId, uid);
|
||||
return r;
|
||||
}
|
||||
|
||||
async function getByUname(domainId, uname, ignoreMissing = false) {
|
||||
export async function getByUname(domainId: string, uname: string, ignoreMissing = false) {
|
||||
const unameLower = uname.trim().toLowerCase();
|
||||
const udoc = (unameLower === 'guest')
|
||||
? BUILTIN_USERS[0]
|
||||
@@ -109,10 +135,10 @@ async function getByUname(domainId, uname, ignoreMissing = false) {
|
||||
throw new UserNotFoundError(uname);
|
||||
}
|
||||
const dudoc = await getInDomain(domainId, udoc);
|
||||
return new USER(udoc, dudoc);
|
||||
return new User(udoc, dudoc);
|
||||
}
|
||||
|
||||
async function getByEmail(domainId, mail, ignoreMissing = false) {
|
||||
export async function getByEmail(domainId: string, mail: string, ignoreMissing = false) {
|
||||
const mailLower = mail.trim().toLowerCase();
|
||||
const udoc = (mailLower === 'guest@hydro.local')
|
||||
? BUILTIN_USERS[0]
|
||||
@@ -124,21 +150,21 @@ async function getByEmail(domainId, mail, ignoreMissing = false) {
|
||||
throw new UserNotFoundError(mail);
|
||||
}
|
||||
const dudoc = await getInDomain(domainId, udoc);
|
||||
return new USER(udoc, dudoc);
|
||||
return new User(udoc, dudoc);
|
||||
}
|
||||
|
||||
function setById(uid, $set, $unset) {
|
||||
const op = {};
|
||||
export function setById(uid: number, $set = {}, $unset = {}) {
|
||||
const op: any = {};
|
||||
if ($set && Object.keys($set).length) op.$set = $set;
|
||||
if ($unset && Object.keys($unset).length) op.$unset = $unset;
|
||||
return coll.findOneAndUpdate({ _id: uid }, op);
|
||||
}
|
||||
|
||||
function setEmail(uid, mail) {
|
||||
export function setEmail(uid: number, mail: string) {
|
||||
return setById(uid, { mail, mailLower: mail.trim().toLowerCase() });
|
||||
}
|
||||
|
||||
async function changePassword(uid, currentPassword, newPassword) {
|
||||
export async function changePassword(uid: number, currentPassword: string, newPassword: string) {
|
||||
const udoc = await getById('system', uid);
|
||||
udoc.checkPassword(currentPassword);
|
||||
const salt = String.random();
|
||||
@@ -148,7 +174,7 @@ async function changePassword(uid, currentPassword, newPassword) {
|
||||
);
|
||||
}
|
||||
|
||||
async function inc(_id, field, n = 1) {
|
||||
export async function inc(_id: number, field: string, n = 1) {
|
||||
const udoc = await coll.findOne({ _id });
|
||||
if (!udoc) throw new UserNotFoundError(_id);
|
||||
udoc[field] = udoc[field] + n || n;
|
||||
@@ -156,22 +182,20 @@ async function inc(_id, field, n = 1) {
|
||||
return udoc;
|
||||
}
|
||||
|
||||
function setInDomain(domainId, uid, params) {
|
||||
export function setInDomain(domainId: string, uid: number, params: any) {
|
||||
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, params);
|
||||
}
|
||||
|
||||
async function incDomain(domainId, uid, field, n = 1) {
|
||||
export async function incDomain(domainId: string, uid: number, field: string, n = 1) {
|
||||
// @ts-ignore
|
||||
const dudoc = await getInDomain(domainId, { _id: uid });
|
||||
dudoc[field] = dudoc[field] + n || n;
|
||||
await setInDomain(domainId, uid, { [field]: dudoc[field] });
|
||||
return dudoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<number>} uid
|
||||
*/
|
||||
async function create({
|
||||
uid, mail, uname, password, regip = '127.0.0.1', priv = PRIV.PRIV_DEFAULT,
|
||||
export async function create({
|
||||
uid = null, mail, uname, password, regip = '127.0.0.1', priv = PRIV.PRIV_DEFAULT,
|
||||
}) {
|
||||
const salt = String.random();
|
||||
if (!uid) uid = await system.inc('user');
|
||||
@@ -198,30 +222,30 @@ async function create({
|
||||
return uid;
|
||||
}
|
||||
|
||||
function getMulti(params) {
|
||||
export function getMulti(params) {
|
||||
return coll.find(params);
|
||||
}
|
||||
|
||||
function setMultiInDomain(domainId, query, params) {
|
||||
export function setMultiInDomain(domainId: string, query: any, params: any) {
|
||||
return document.setMultiStatus(domainId, document.TYPE_DOMAIN_USER, query, params);
|
||||
}
|
||||
|
||||
async function getPrefixList(prefix, limit = 50) {
|
||||
export async function getPrefixList(prefix: string, limit = 50) {
|
||||
prefix = prefix.toLowerCase();
|
||||
const $regex = new RegExp(`\\A\\Q${prefix.replace(/\\E/gmi, /\\E\\E\\Q/gmi)}\\E`, 'gmi');
|
||||
const $regex = new RegExp(`\\A\\Q${prefix}\\E`, 'gmi');
|
||||
const udocs = await coll.find({ unameLower: { $regex } }).limit(limit).toArray();
|
||||
return udocs;
|
||||
}
|
||||
|
||||
function setPriv(uid, priv) {
|
||||
export function setPriv(uid: number, priv: number) {
|
||||
return coll.findOneAndUpdate({ _id: uid }, { $set: priv }, { returnOriginal: false });
|
||||
}
|
||||
|
||||
function setRole(domainId, uid, role) {
|
||||
export function setRole(domainId: string, uid: number, role: string) {
|
||||
return document.setStatus(domainId, document.TYPE_DOMAIN_USER, 0, uid, { role });
|
||||
}
|
||||
|
||||
function setRoles(domainId, roles) {
|
||||
export function setRoles(domainId: string, roles: any) {
|
||||
const tasks = [];
|
||||
for (const role in roles) {
|
||||
tasks.push(document.set(
|
||||
@@ -231,7 +255,7 @@ function setRoles(domainId, roles) {
|
||||
return Promise.all(tasks);
|
||||
}
|
||||
|
||||
async function getRoles(domainId) {
|
||||
export async function getRoles(domainId: string) {
|
||||
const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_USER).sort('_id', 1).toArray();
|
||||
const roles = [];
|
||||
for (const doc of docs) {
|
||||
@@ -240,40 +264,40 @@ async function getRoles(domainId) {
|
||||
return roles;
|
||||
}
|
||||
|
||||
function getRole(domainId, name) {
|
||||
export function getRole(domainId: string, name: string) {
|
||||
return document.get(domainId, document.TYPE_DOMAIN_USER, name);
|
||||
}
|
||||
|
||||
function getMultiInDomain(domainId, query) {
|
||||
export function getMultiInDomain(domainId: string, query: any = {}) {
|
||||
return document.getMultiStatus(domainId, document.TYPE_DOMAIN_USER, query);
|
||||
}
|
||||
|
||||
function addRole(domainId, name, permission) {
|
||||
export function addRole(domainId: string, name: string, permission: string) {
|
||||
return document.add(domainId, permission, 1, document.TYPE_DOMAIN_USER, name);
|
||||
}
|
||||
|
||||
function deleteRoles(domainId, roles) {
|
||||
export function deleteRoles(domainId: string, roles) {
|
||||
return Promise.all([
|
||||
document.deleteMulti(domainId, document.TYPE_DOMAIN_USER, { docId: { $in: roles } }),
|
||||
document.deleteMultiStatus(domainId, document.TYPE_DOMAIN_USER, { role: { $in: roles } }),
|
||||
]);
|
||||
}
|
||||
|
||||
function ban(uid) {
|
||||
export function ban(uid: number) {
|
||||
return Promise.all([
|
||||
setPriv(uid, PRIV.PRIV_NONE),
|
||||
token.delByUid(uid),
|
||||
]);
|
||||
}
|
||||
|
||||
function ensureIndexes() {
|
||||
export function ensureIndexes() {
|
||||
return Promise.all([
|
||||
coll.createIndex('unameLower', { unique: true }),
|
||||
coll.createIndex('mailLower', { sparse: true }),
|
||||
]);
|
||||
}
|
||||
|
||||
global.Hydro.model.user = module.exports = {
|
||||
global.Hydro.model.user = {
|
||||
changePassword,
|
||||
create,
|
||||
getByEmail,
|
||||
Reference in new issue
Block a user