core: change export mode

This commit is contained in:
undefined committed 2021-03-16 00:06:40 +08:00
1 parent b371ee6464
commit 0bed88ff99
26 files changed
+609 -687

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ import type { ProblemConfig } from 'hydrooj';
import { Route, Handler } from 'hydrooj/dist/service/server'; import { Route, Handler } from 'hydrooj/dist/service/server';
import { buildContent } from 'hydrooj/dist/lib/content'; import { buildContent } from 'hydrooj/dist/lib/content';
import { ProblemAdd } from 'hydrooj/dist/lib/ui'; import { ProblemAdd } from 'hydrooj/dist/lib/ui';
import * as solution from 'hydrooj/dist/model/solution'; import solution from 'hydrooj/dist/model/solution';
import { PERM } from 'hydrooj/dist/model/builtin'; import { PERM } from 'hydrooj/dist/model/builtin';
import { FileTooLargeError, ValidationError } from 'hydrooj/dist/error'; import { FileTooLargeError, ValidationError } from 'hydrooj/dist/error';
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/fps-importer", "name": "@hydrooj/fps-importer",
"version": "1.1.9", "version": "1.1.10",
"description": "Import FPS problems", "description": "Import FPS problems",
"main": "package.json", "main": "package.json",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.20.21", "version": "2.20.22",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "dist/loader.js", "main": "dist/loader.js",
"typings": "dist/loader.d.ts", "typings": "dist/loader.d.ts",
+3 -3
View File
@@ -13,10 +13,10 @@ import * as contest from '../model/contest';
import * as system from '../model/system'; import * as system from '../model/system';
import * as document from '../model/document'; import * as document from '../model/document';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
import * as domain from '../model/domain'; import domain from '../model/domain';
import * as record from '../model/record'; import record from '../model/record';
import user from '../model/user'; import user from '../model/user';
import * as message from '../model/message'; import message from '../model/message';
import { import {
Route, Handler, Types, param, Route, Handler, Types, param,
} from '../service/server'; } from '../service/server';
+1 -1
View File
@@ -8,7 +8,7 @@ import paginate from '../lib/paginate';
import * as system from '../model/system'; import * as system from '../model/system';
import user from '../model/user'; import user from '../model/user';
import * as oplog from '../model/oplog'; import * as oplog from '../model/oplog';
import * as message from '../model/message'; import message from '../model/message';
import * as discussion from '../model/discussion'; import * as discussion from '../model/discussion';
import * as document from '../model/document'; import * as document from '../model/document';
import { PERM, PRIV } from '../model/builtin'; import { PERM, PRIV } from '../model/builtin';
+1 -1
View File
@@ -7,7 +7,7 @@ import {
import { log2 } from '../utils'; import { log2 } from '../utils';
import type { DomainDoc } from '../interface'; import type { DomainDoc } from '../interface';
import user from '../model/user'; import user from '../model/user';
import * as domain from '../model/domain'; import domain from '../model/domain';
import * as discussion from '../model/discussion'; import * as discussion from '../model/discussion';
import * as system from '../model/system'; import * as system from '../model/system';
import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting'; import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
+2 -2
View File
@@ -11,12 +11,12 @@ import * as misc from '../lib/misc';
import { md5 } from '../lib/crypto'; import { md5 } from '../lib/crypto';
import * as mail from '../lib/mail'; import * as mail from '../lib/mail';
import * as contest from '../model/contest'; import * as contest from '../model/contest';
import * as message from '../model/message'; import message from '../model/message';
import * as document from '../model/document'; import * as document from '../model/document';
import * as system from '../model/system'; import * as system from '../model/system';
import user from '../model/user'; import user from '../model/user';
import * as setting from '../model/setting'; import * as setting from '../model/setting';
import * as domain from '../model/domain'; import domain from '../model/domain';
import * as discussion from '../model/discussion'; import * as discussion from '../model/discussion';
import token from '../model/token'; import token from '../model/token';
import * as training from '../model/training'; import * as training from '../model/training';
+2 -2
View File
@@ -12,14 +12,14 @@ import {
Route, Handler, Types, param, Route, Handler, Types, param,
} from '../service/server'; } from '../service/server';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
import * as domain from '../model/domain'; import domain from '../model/domain';
import { PERM } from '../model/builtin'; import { PERM } from '../model/builtin';
import user from '../model/user'; import user from '../model/user';
import * as system from '../model/system'; import * as system from '../model/system';
import * as contest from '../model/contest'; import * as contest from '../model/contest';
import * as discussion from '../model/discussion'; import * as discussion from '../model/discussion';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
import * as record from '../model/record'; import record from '../model/record';
import * as document from '../model/document'; import * as document from '../model/document';
import paginate from '../lib/paginate'; import paginate from '../lib/paginate';
import { isTitle, isContent } from '../lib/validator'; import { isTitle, isContent } from '../lib/validator';
+3 -3
View File
@@ -3,12 +3,12 @@ import { JudgeResultBody, Rdoc, TestCase } from '../interface';
import { sleep } from '../utils'; import { sleep } from '../utils';
import { Logger } from '../logger'; import { Logger } from '../logger';
import difficultyAlgorithm from '../lib/difficulty'; import difficultyAlgorithm from '../lib/difficulty';
import * as record from '../model/record'; import record from '../model/record';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
import * as builtin from '../model/builtin'; import * as builtin from '../model/builtin';
import * as contest from '../model/contest'; import * as contest from '../model/contest';
import * as domain from '../model/domain'; import domain from '../model/domain';
import * as task from '../model/task'; import task from '../model/task';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
import { import {
Route, Handler, Connection, ConnectionHandler, post, Types, Route, Handler, Connection, ConnectionHandler, post, Types,
+1 -1
View File
@@ -7,7 +7,7 @@ import * as setting from '../model/setting';
import * as system from '../model/system'; import * as system from '../model/system';
import user from '../model/user'; import user from '../model/user';
import { STATUS, PRIV } from '../model/builtin'; import { STATUS, PRIV } from '../model/builtin';
import * as record from '../model/record'; import record from '../model/record';
import { import {
Route, Connection, Handler, ConnectionHandler, param, Types, Route, Connection, Handler, ConnectionHandler, param, Types,
} from '../service/server'; } from '../service/server';
+3 -3
View File
@@ -13,10 +13,10 @@ import { isTitle, isContent, isPid } from '../lib/validator';
import difficultyAlgorithm from '../lib/difficulty'; import difficultyAlgorithm from '../lib/difficulty';
import * as system from '../model/system'; import * as system from '../model/system';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
import * as record from '../model/record'; import record from '../model/record';
import * as domain from '../model/domain'; import domain from '../model/domain';
import user from '../model/user'; import user from '../model/user';
import * as solution from '../model/solution'; import solution from '../model/solution';
import { PERM, PRIV } from '../model/builtin'; import { PERM, PRIV } from '../model/builtin';
import storage from '../service/storage'; import storage from '../service/storage';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
+1 -1
View File
@@ -4,7 +4,7 @@ import { Rdoc } from '../interface';
import { PERM, STATUS, PRIV } from '../model/builtin'; import { PERM, STATUS, PRIV } from '../model/builtin';
import * as system from '../model/system'; import * as system from '../model/system';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
import * as record from '../model/record'; import record from '../model/record';
import * as contest from '../model/contest'; import * as contest from '../model/contest';
import user from '../model/user'; import user from '../model/user';
import paginate from '../lib/paginate'; import paginate from '../lib/paginate';
+3 -3
View File
@@ -8,11 +8,11 @@ import {
Route, Handler, Types, param, post, Route, Handler, Types, param, post,
} from '../service/server'; } from '../service/server';
import user from '../model/user'; import user from '../model/user';
import * as oauth from '../model/oauth'; import oauth from '../model/oauth';
import token from '../model/token'; import token from '../model/token';
import * as record from '../model/record'; import record from '../model/record';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
import * as task from '../model/task'; import task from '../model/task';
import * as system from '../model/system'; import * as system from '../model/system';
import { PERM, PRIV } from '../model/builtin'; import { PERM, PRIV } from '../model/builtin';
import { isEmail, isPassword, isUname } from '../lib/validator'; import { isEmail, isPassword, isUname } from '../lib/validator';
+204 -217
View File
@@ -2,6 +2,7 @@ import { Dictionary } from 'lodash';
import { FilterQuery } from 'mongodb'; import { FilterQuery } from 'mongodb';
import { BUILTIN_ROLES, PRIV } from './builtin'; import { BUILTIN_ROLES, PRIV } from './builtin';
import { DomainDoc } from '../interface'; import { DomainDoc } from '../interface';
import { ArgMethod } from '../utils';
import { NumberKeys } from '../typeutils'; import { NumberKeys } from '../typeutils';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
import db from '../service/db'; import db from '../service/db';
@@ -9,232 +10,218 @@ import db from '../service/db';
const coll = db.collection('domain'); const coll = db.collection('domain');
const collUser = db.collection('domain.user'); const collUser = db.collection('domain.user');
export const JOIN_METHOD_NONE = 0;
export const JOIN_METHOD_ALL = 1;
export const JOIN_METHOD_CODE = 2;
export const JOIN_METHOD_RANGE = {
[JOIN_METHOD_NONE]: 'No user is allowed to join this domain',
[JOIN_METHOD_ALL]: 'Any user is allowed to join this domain',
[JOIN_METHOD_CODE]: 'Any user is allowed to join this domain with an invitation code',
};
export const JOIN_EXPIRATION_KEEP_CURRENT = 0;
export const JOIN_EXPIRATION_UNLIMITED = -1;
export const JOIN_EXPIRATION_RANGE = {
[JOIN_EXPIRATION_KEEP_CURRENT]: 'Keep current expiration',
3: 'In 3 hours',
24: 'In 1 day',
[24 * 3]: 'In 3 days',
[24 * 7]: 'In 1 week',
[24 * 30]: 'In 1 month',
[JOIN_EXPIRATION_UNLIMITED]: 'Never expire',
};
export async function add(domainId: string, owner: number, name: string, bulletin: string) {
const ddoc: DomainDoc = {
_id: domainId,
owner,
name,
bulletin,
roles: {},
gravatar: '',
pidCounter: 0,
};
await bus.serial('domain/create', ddoc);
await coll.insertOne(ddoc);
return domainId;
}
export async function get(domainId: string): Promise<DomainDoc | null> {
const query: FilterQuery<DomainDoc> = { _id: domainId };
await bus.serial('domain/before-get', query);
const result = await coll.findOne(query);
await bus.serial('domain/get', result);
return result;
}
export async function getByHost(host: string): Promise<DomainDoc | null> {
const query: FilterQuery<DomainDoc> = { host };
await bus.serial('domain/before-get', query);
const result = await coll.findOne(query);
await bus.serial('domain/get', result);
return result;
}
export function getMulti(query: FilterQuery<DomainDoc> = {}) {
return coll.find(query);
}
export async function edit(domainId: string, $set: Partial<DomainDoc>) {
await bus.serial('domain/before-update', domainId, $set);
const result = await coll.findOneAndUpdate({ _id: domainId }, { $set }, { returnOriginal: false });
await bus.serial('domain/update', domainId, $set, result.value);
return result.value;
}
export async function inc(domainId: string, field: NumberKeys<DomainDoc>, n: number): Promise<number | null> {
const res = await coll.findOneAndUpdate(
{ _id: domainId },
// FIXME
// @ts-ignore
{ $inc: { [field]: n } },
{ returnOriginal: false },
);
return res.value[field];
}
export async function getList(domainIds: string[]): Promise<Dictionary<DomainDoc>> {
const r = {};
// eslint-disable-next-line no-await-in-loop
for (const domainId of domainIds) r[domainId] = await get(domainId);
return r;
}
export function setUserRole(domainId: string, uid: number, role: string) {
return collUser.updateOne({ uid, domainId }, { $set: { role } }, { upsert: true });
}
export async function getRoles(domainId: string): Promise<any[]>;
export async function getRoles(domain: DomainDoc): Promise<any[]>;
export async function getRoles(arg: string | DomainDoc) {
let ddoc: DomainDoc;
if (typeof arg === 'string') ddoc = await get(arg);
else ddoc = arg;
const roles = [];
const r = [];
for (const role in ddoc.roles) {
roles.push({ _id: role, perm: BigInt(ddoc.roles[role]) });
r.push(role);
}
for (const role in BUILTIN_ROLES) {
if (!r.includes(role)) {
roles.push({ _id: role, perm: BUILTIN_ROLES[role] });
}
}
return roles;
}
export async function setRoles(domainId: string, roles: Dictionary<bigint>) {
const current = await get(domainId);
for (const role in roles) {
current.roles[role] = roles[role].toString();
}
return await coll.updateOne({ _id: domainId }, { $set: { roles: current.roles } });
}
export async function addRole(domainId: string, name: string, permission: bigint) {
const current = await get(domainId);
current.roles[name] = permission.toString();
return await coll.updateOne({ _id: domainId }, { $set: { roles: current.roles } });
}
export async function deleteRoles(domainId: string, roles: string[]) {
const current = await get(domainId);
for (const role of roles) delete current.roles[role];
await Promise.all([
coll.updateOne({ _id: domainId }, { $set: current }),
collUser.updateMany({ domainId, role: { $in: roles } }, { $set: { role: 'default' } }),
]);
}
interface DomainUserArg { interface DomainUserArg {
_id: number, _id: number,
priv: number, priv: number,
} }
export async function getDomainUser(domainId: string, udoc: DomainUserArg) {
let dudoc = await collUser.findOne({ domainId, uid: udoc._id });
dudoc = dudoc || {};
if (!(udoc.priv & PRIV.PRIV_USER_PROFILE)) dudoc.role = 'guest';
if (udoc.priv & PRIV.PRIV_MANAGE_ALL_DOMAIN) dudoc.role = 'admin';
dudoc.role = dudoc.role || 'default';
const ddoc = await get(domainId);
if (ddoc.owner === udoc._id) dudoc.role = 'admin';
dudoc.perm = ddoc.roles[dudoc.role]
? BigInt(ddoc.roles[dudoc.role])
: BUILTIN_ROLES[dudoc.role];
return dudoc;
}
export function setMultiUserInDomain(domainId: string, query: any, params: any) { class DomainModel {
return collUser.updateMany({ domainId, ...query }, { $set: params }); static JOIN_METHOD_NONE = 0;
} static JOIN_METHOD_ALL = 1;
static JOIN_METHOD_CODE = 2;
export function getMultiUserInDomain(domainId: string, query: any = {}) { static JOIN_METHOD_RANGE = {
return collUser.find({ domainId, ...query }); [DomainModel.JOIN_METHOD_NONE]: 'No user is allowed to join this domain',
} [DomainModel.JOIN_METHOD_ALL]: 'Any user is allowed to join this domain',
[DomainModel.JOIN_METHOD_CODE]: 'Any user is allowed to join this domain with an invitation code',
};
export function setUserInDomain(domainId: string, uid: number, params: any) { static JOIN_EXPIRATION_KEEP_CURRENT = 0;
return collUser.updateOne({ domainId, uid }, { $set: params }); static JOIN_EXPIRATION_UNLIMITED = -1;
}
export async function incUserInDomain(domainId: string, uid: number, field: string, n = 1) { static JOIN_EXPIRATION_RANGE = {
// @ts-ignore [DomainModel.JOIN_EXPIRATION_KEEP_CURRENT]: 'Keep current expiration',
const dudoc = await getDomainUser(domainId, { _id: uid }); 3: 'In 3 hours',
dudoc[field] = dudoc[field] + n || n; 24: 'In 1 day',
await setUserInDomain(domainId, uid, { [field]: dudoc[field] }); [24 * 3]: 'In 3 days',
return dudoc; [24 * 7]: 'In 1 week',
} [24 * 30]: 'In 1 month',
[DomainModel.JOIN_EXPIRATION_UNLIMITED]: 'Never expire',
};
export async function getDictUserByDomainId(uid: number) { @ArgMethod
const [dudocs, ddocs] = await Promise.all([ static async add(domainId: string, owner: number, name: string, bulletin: string) {
collUser.find({ uid }).toArray(), const ddoc: DomainDoc = {
coll.find({ owner: uid }).toArray(), _id: domainId,
]); owner,
const dudict = {}; name,
for (const dudoc of dudocs) { bulletin,
// eslint-disable-next-line no-await-in-loop roles: {},
dudict[dudoc.domainId] = await get(dudoc.domainId); gravatar: '',
dudict[dudoc.domainId].role = dudoc.role; pidCounter: 0,
};
await bus.serial('domain/create', ddoc);
await coll.insertOne(ddoc);
return domainId;
}
@ArgMethod
static async get(domainId: string): Promise<DomainDoc | null> {
const query: FilterQuery<DomainDoc> = { _id: domainId };
await bus.serial('domain/before-get', query);
const result = await coll.findOne(query);
await bus.serial('domain/get', result);
return result;
}
@ArgMethod
static async getByHost(host: string): Promise<DomainDoc | null> {
const query: FilterQuery<DomainDoc> = { host };
await bus.serial('domain/before-get', query);
const result = await coll.findOne(query);
await bus.serial('domain/get', result);
return result;
}
static getMulti(query: FilterQuery<DomainDoc> = {}) {
return coll.find(query);
}
static async edit(domainId: string, $set: Partial<DomainDoc>) {
await bus.serial('domain/before-update', domainId, $set);
const result = await coll.findOneAndUpdate({ _id: domainId }, { $set }, { returnOriginal: false });
await bus.serial('domain/update', domainId, $set, result.value);
return result.value;
}
@ArgMethod
static async inc(domainId: string, field: NumberKeys<DomainDoc>, n: number): Promise<number | null> {
const res = await coll.findOneAndUpdate(
{ _id: domainId },
// FIXME
// @ts-ignore
{ $inc: { [field]: n } },
{ returnOriginal: false },
);
return res.value[field];
}
@ArgMethod
static async getList(domainIds: string[]): Promise<Dictionary<DomainDoc>> {
const r = {};
// eslint-disable-next-line no-await-in-loop
for (const domainId of domainIds) r[domainId] = await DomainModel.get(domainId);
return r;
}
@ArgMethod
static setUserRole(domainId: string, uid: number, role: string) {
return collUser.updateOne({ uid, domainId }, { $set: { role } }, { upsert: true });
}
static async getRoles(domainId: string): Promise<any[]>;
static async getRoles(domain: DomainDoc): Promise<any[]>;
@ArgMethod
static async getRoles(arg: string | DomainDoc) {
let ddoc: DomainDoc;
if (typeof arg === 'string') ddoc = await DomainModel.get(arg);
else ddoc = arg;
const roles = [];
const r = [];
for (const role in ddoc.roles) {
roles.push({ _id: role, perm: BigInt(ddoc.roles[role]) });
r.push(role);
}
for (const role in BUILTIN_ROLES) {
if (!r.includes(role)) {
roles.push({ _id: role, perm: BUILTIN_ROLES[role] });
}
}
return roles;
}
static async setRoles(domainId: string, roles: Dictionary<bigint>) {
const current = await DomainModel.get(domainId);
for (const role in roles) {
current.roles[role] = roles[role].toString();
}
return await coll.updateOne({ _id: domainId }, { $set: { roles: current.roles } });
}
static async addRole(domainId: string, name: string, permission: bigint) {
const current = await DomainModel.get(domainId);
current.roles[name] = permission.toString();
return await coll.updateOne({ _id: domainId }, { $set: { roles: current.roles } });
}
static async deleteRoles(domainId: string, roles: string[]) {
const current = await DomainModel.get(domainId);
for (const role of roles) delete current.roles[role];
await Promise.all([
coll.updateOne({ _id: domainId }, { $set: current }),
collUser.updateMany({ domainId, role: { $in: roles } }, { $set: { role: 'default' } }),
]);
}
static async getDomainUser(domainId: string, udoc: DomainUserArg) {
let dudoc = await collUser.findOne({ domainId, uid: udoc._id });
dudoc = dudoc || {};
if (!(udoc.priv & PRIV.PRIV_USER_PROFILE)) dudoc.role = 'guest';
if (udoc.priv & PRIV.PRIV_MANAGE_ALL_DOMAIN) dudoc.role = 'admin';
dudoc.role = dudoc.role || 'default';
const ddoc = await DomainModel.get(domainId);
if (ddoc.owner === udoc._id) dudoc.role = 'admin';
dudoc.perm = ddoc.roles[dudoc.role]
? BigInt(ddoc.roles[dudoc.role])
: BUILTIN_ROLES[dudoc.role];
return dudoc;
}
static setMultiUserInDomain(domainId: string, query: any, params: any) {
return collUser.updateMany({ domainId, ...query }, { $set: params });
}
static getMultiUserInDomain(domainId: string, query: any = {}) {
return collUser.find({ domainId, ...query });
}
static setUserInDomain(domainId: string, uid: number, params: any) {
return collUser.updateOne({ domainId, uid }, { $set: params });
}
@ArgMethod
static async incUserInDomain(domainId: string, uid: number, field: string, n: number = 1) {
// @ts-ignore
const dudoc = await getDomainUser(domainId, { _id: uid });
dudoc[field] = dudoc[field] + n || n;
await DomainModel.setUserInDomain(domainId, uid, { [field]: dudoc[field] });
return dudoc;
}
@ArgMethod
static async getDictUserByDomainId(uid: number) {
const [dudocs, ddocs] = await Promise.all([
collUser.find({ uid }).toArray(),
coll.find({ owner: uid }).toArray(),
]);
const dudict = {};
for (const dudoc of dudocs) {
// eslint-disable-next-line no-await-in-loop
dudict[dudoc.domainId] = await DomainModel.get(dudoc.domainId);
dudict[dudoc.domainId].role = dudoc.role;
}
for (const ddoc of ddocs) dudict[ddoc._id] = 'admin';
return dudict;
}
static getJoinSettings(ddoc: DomainDoc, roles: string[]) {
if (!ddoc.join) return null;
const joinSettings = ddoc.join;
if (joinSettings.method === DomainModel.JOIN_METHOD_NONE) return null;
if (!roles.includes(joinSettings.role)) return null;
if (joinSettings.expire && joinSettings.expire < new Date()) return null;
return joinSettings;
}
@ArgMethod
static async getPrefixSearch(prefix: string, limit: number = 50) {
const $regex = new RegExp(prefix, 'mi');
const ddocs = await coll.find({
$or: [{ _id: { $regex } }, { name: { $regex } }],
}).limit(limit).toArray();
return ddocs;
} }
for (const ddoc of ddocs) dudict[ddoc._id] = 'admin';
return dudict;
} }
export function getJoinSettings(ddoc: DomainDoc, roles: string[]) { export = DomainModel;
if (!ddoc.join) return null; global.Hydro.model.domain = DomainModel;
const joinSettings = ddoc.join;
if (joinSettings.method === JOIN_METHOD_NONE) return null;
if (!roles.includes(joinSettings.role)) return null;
if (joinSettings.expire && joinSettings.expire < new Date()) return null;
return joinSettings;
}
export async function getPrefixSearch(prefix: string, limit = 50) {
const $regex = new RegExp(prefix, 'mi');
const ddocs = await coll.find({
$or: [{ _id: { $regex } }, { name: { $regex } }],
}).limit(limit).toArray();
return ddocs;
}
global.Hydro.model.domain = {
JOIN_METHOD_NONE,
JOIN_METHOD_ALL,
JOIN_METHOD_CODE,
JOIN_METHOD_RANGE,
JOIN_EXPIRATION_KEEP_CURRENT,
JOIN_EXPIRATION_UNLIMITED,
JOIN_EXPIRATION_RANGE,
getRoles,
add,
inc,
get,
getByHost,
edit,
getMulti,
getList,
setRoles,
addRole,
deleteRoles,
setUserRole,
getDomainUser,
setMultiUserInDomain,
setUserInDomain,
incUserInDomain,
getMultiUserInDomain,
getDictUserByDomainId,
getJoinSettings,
getPrefixSearch,
};
+53 -58
View File
@@ -1,65 +1,71 @@
import { FilterQuery, ObjectID } from 'mongodb'; import { FilterQuery, ObjectID } from 'mongodb';
import user from './user'; import user from './user';
import { Mdoc } from '../interface'; import { Mdoc } from '../interface';
import { ArgMethod } from '../utils';
import db from '../service/db'; import db from '../service/db';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
const coll = db.collection('message'); const coll = db.collection('message');
export const FLAG_UNREAD = 1; class MessageModel {
export const FLAG_ALERT = 2; static FLAG_UNREAD = 1;
static FLAG_ALERT = 2;
export async function send( @ArgMethod
from: number, to: number, static async send(
content: string, flag: number = FLAG_UNREAD, from: number, to: number,
): Promise<Mdoc> { content: string, flag: number = MessageModel.FLAG_UNREAD,
const res = await coll.insertOne({ ): Promise<Mdoc> {
from, to, content, flag, const res = await coll.insertOne({
}); from, to, content, flag,
const mdoc = { });
from, to, content, _id: res.insertedId, flag, const mdoc = {
}; from, to, content, _id: res.insertedId, flag,
if (from !== to) { };
// ENHANCE domainId? if (from !== to) {
const udoc = await user.getById('system', to); // ENHANCE domainId?
bus.boardcast('user/message', to, mdoc, udoc); const udoc = await user.getById('system', to);
bus.boardcast('user/message', to, mdoc, udoc);
}
return mdoc;
} }
return mdoc;
}
export async function get(_id: ObjectID): Promise<Mdoc | null> { static async get(_id: ObjectID): Promise<Mdoc | null> {
return await coll.findOne({ _id }); return await coll.findOne({ _id });
} }
export async function getByUser(uid: number): Promise<Mdoc[]> { @ArgMethod
return await coll.find({ $or: [{ from: uid }, { to: uid }] }).sort('_id', 1).toArray(); static async getByUser(uid: number): Promise<Mdoc[]> {
} return await coll.find({ $or: [{ from: uid }, { to: uid }] }).sort('_id', 1).toArray();
}
export async function getMany(query: FilterQuery<Mdoc>, sort: any, page: number, limit: number): Promise<Mdoc[]> { static async getMany(query: FilterQuery<Mdoc>, sort: any, page: number, limit: number): Promise<Mdoc[]> {
return await coll.find(query).sort(sort) return await coll.find(query).sort(sort)
.skip((page - 1) * limit).limit(limit) .skip((page - 1) * limit).limit(limit)
.toArray(); .toArray();
} }
export async function setFlag(messageId: ObjectID, flag: number): Promise<Mdoc | null> { static async setFlag(messageId: ObjectID, flag: number): Promise<Mdoc | null> {
const result = await coll.findOneAndUpdate( const result = await coll.findOneAndUpdate(
{ _id: messageId }, { _id: messageId },
{ $bit: { flag: { xor: flag } } }, { $bit: { flag: { xor: flag } } },
{ returnOriginal: false }, { returnOriginal: false },
); );
return result.value; return result.value;
} }
export async function del(_id: ObjectID) { static async del(_id: ObjectID) {
return await coll.deleteOne({ _id }); return await coll.deleteOne({ _id });
} }
export function count(query: FilterQuery<Mdoc> = {}) { @ArgMethod
return coll.find(query).count(); static count(query: FilterQuery<Mdoc> = {}) {
} return coll.find(query).count();
}
export function getMulti(uid: number) { static getMulti(uid: number) {
return coll.find({ $or: [{ from: uid }, { to: uid }] }); return coll.find({ $or: [{ from: uid }, { to: uid }] });
}
} }
function ensureIndexes() { function ensureIndexes() {
@@ -70,16 +76,5 @@ function ensureIndexes() {
} }
bus.once('app/started', ensureIndexes); bus.once('app/started', ensureIndexes);
global.Hydro.model.message = { export = MessageModel;
FLAG_UNREAD, global.Hydro.model.message = MessageModel;
FLAG_ALERT,
count,
get,
getByUser,
del,
setFlag,
getMany,
getMulti,
send,
};
+20 -20
View File
@@ -12,25 +12,25 @@ const cache = new Lru({
maxAge: 5000, maxAge: 5000,
}); });
export async function get(_id: string) { class OauthModel {
const res = cache.get(_id); static async get(_id: string) {
if (res !== undefined) return res; const res = cache.get(_id);
const doc = await coll.findOne({ _id }); if (res !== undefined) return res;
if (doc) return doc.uid; const doc = await coll.findOne({ _id });
return null; if (doc) return doc.uid;
return null;
}
static async set(_id: string, value: number) {
cache.set(_id, value);
const res = await coll.findOneAndUpdate(
{ _id },
{ $set: { value } },
{ upsert: true, returnOriginal: false },
);
return res.value.uid;
}
} }
export async function set(_id: string, value: number) { export = OauthModel;
cache.set(_id, value); global.Hydro.model.oauth = OauthModel;
const res = await coll.findOneAndUpdate(
{ _id },
{ $set: { value } },
{ upsert: true, returnOriginal: false },
);
return res.value.uid;
}
global.Hydro.model.oauth = {
get,
set,
};
+1 -1
View File
@@ -3,7 +3,7 @@ import { ObjectID, FilterQuery } from 'mongodb';
import { Dictionary, pick } from 'lodash'; import { Dictionary, pick } from 'lodash';
import { STATUS } from './builtin'; import { STATUS } from './builtin';
import * as document from './document'; import * as document from './document';
import * as domain from './domain'; import domain from './domain';
import { buildProjection } from '../utils'; import { buildProjection } from '../utils';
import type { ProblemStatusDoc, Pdict, Document } from '../interface'; import type { ProblemStatusDoc, Pdict, Document } from '../interface';
import { Content } from '../loader'; import { Content } from '../loader';
+151 -197
View File
@@ -4,213 +4,167 @@ import {
import { Dictionary } from 'lodash'; import { Dictionary } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import { STATUS } from './builtin'; import { STATUS } from './builtin';
import * as task from './task'; import task from './task';
import * as problem from './problem'; import * as problem from './problem';
import { import { Rdoc, ContestInfo, ProblemConfig } from '../interface';
Rdoc, TestCase, ContestInfo, ProblemConfig, import { ArgMethod, Time } from '../utils';
} from '../interface';
import { Time } from '../utils';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
import db from '../service/db'; import db from '../service/db';
export const coll: Collection<Rdoc> = db.collection('record'); class RecordModel {
static coll: Collection<Rdoc> = db.collection('record');
export interface RdocBase { static async get(domainId: string, _id: ObjectID): Promise<Rdoc | null> {
_id?: ObjectID, const res = await RecordModel.coll.findOne({ _id });
domainId?: string, if (res && res.domainId === domainId) return res;
pid: number return null;
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 get(domainId: string, _id: ObjectID): Promise<Rdoc | null> {
const res = await coll.findOne({ _id });
if (res && res.domainId === domainId) return res;
return null;
}
export async function stat(domainId?: string) {
const [d5min, d1h, day, week, month, year, total] = await Promise.all([
coll.find({ _id: { $gte: Time.getObjectID(moment().add(-5, 'minutes')) }, ...domainId ? { domainId } : {} }).count(),
coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'hour')) }, ...domainId ? { domainId } : {} }).count(),
coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'day')) }, ...domainId ? { domainId } : {} }).count(),
coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'week')) }, ...domainId ? { domainId } : {} }).count(),
coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'month')) }, ...domainId ? { domainId } : {} }).count(),
coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'year')) }, ...domainId ? { domainId } : {} }).count(),
coll.find(domainId ? { domainId } : {}).count(),
]);
return {
d5min, d1h, day, week, month, year, total,
};
}
export async function judge(domainId: string, rid: ObjectID, priority = 1, config: ProblemConfig = {}) {
const rdoc = await get(domainId, rid);
let data = [];
if (rdoc.pid) {
const pdoc = await problem.get(domainId, rdoc.pid);
data = pdoc.data;
} }
delete rdoc._id;
await task.add({
...rdoc,
priority,
type: 'judge',
rid,
domainId,
config,
data,
});
}
export async function add( @ArgMethod
domainId: string, pid: number, uid: number, static async stat(domainId?: string) {
lang: string, code: string, addTask: boolean, contestOrConfig?: ContestInfo | string, const [d5min, d1h, day, week, month, year, total] = await Promise.all([
) { RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-5, 'minutes')) }, ...domainId ? { domainId } : {} }).count(),
const data: Rdoc = { RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'hour')) }, ...domainId ? { domainId } : {} }).count(),
status: STATUS.STATUS_WAITING, RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'day')) }, ...domainId ? { domainId } : {} }).count(),
_id: new ObjectID(), RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'week')) }, ...domainId ? { domainId } : {} }).count(),
uid, RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'month')) }, ...domainId ? { domainId } : {} }).count(),
code, RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'year')) }, ...domainId ? { domainId } : {} }).count(),
lang, RecordModel.coll.find(domainId ? { domainId } : {}).count(),
pid, ]);
domainId, return {
score: 0, d5min, d1h, day, week, month, year, total,
time: 0, };
memory: 0,
hidden: false,
judgeTexts: [],
compilerTexts: [],
testCases: [],
judger: null,
judgeAt: null,
rejudged: false,
};
if (typeof contestOrConfig === 'string') {
// is Run
data.input = contestOrConfig;
data.hidden = true;
} else data.contest = contestOrConfig;
const res = await coll.insertOne(data);
if (addTask) await judge(domainId, res.insertedId);
return res.insertedId;
}
export function getMulti(domainId: string, query: any) {
return coll.find({ ...query, domainId });
}
export async function update(
domainId: string, _id: ObjectID,
$set?: MatchKeysAndValues<Rdoc>,
$push?: PushOperator<Rdoc>,
$unset?: OnlyFieldsOfType<Rdoc, any, true | '' | 1>,
): Promise<Rdoc | null> {
const $update: UpdateQuery<Rdoc> = {};
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;
if (Object.keys($update).length) {
const res = await coll.findOneAndUpdate(
{ _id, domainId },
$update,
{ returnOriginal: false },
);
return res.value;
} }
return await get(domainId, _id);
}
export function reset(domainId: string, rid: ObjectID, isRejudge: boolean) { static async judge(domainId: string, rid: ObjectID, priority = 1, config: ProblemConfig = {}) {
const upd: any = { const rdoc = await RecordModel.get(domainId, rid);
score: 0, let data = [];
status: STATUS.STATUS_WAITING, if (rdoc.pid) {
time: 0, const pdoc = await problem.get(domainId, rdoc.pid);
memory: 0, data = pdoc.data;
testCases: [], }
judgeTexts: [], delete rdoc._id;
compilerTexts: [], await task.add({
judgeAt: null, ...rdoc,
judger: null, priority,
}; type: 'judge',
if (isRejudge) upd.rejudged = true; rid,
return update(domainId, rid, upd); domainId,
} config,
data,
export function count(domainId: string, query: any) {
return coll.find({ domainId, ...query }).count();
}
export async function getList(
domainId: string, rids: ObjectID[], showHidden: boolean,
): Promise<Dictionary<Rdoc>> {
const r = {};
rids = Array.from(new Set(rids));
const rdocs = await coll.find({ domainId, _id: { $in: rids } }).toArray();
for (const rdoc of rdocs) {
if (rdoc.hidden && !showHidden) r[rdoc._id.toHexString()] = null;
else r[rdoc._id.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 });
static async add(
domainId: string, pid: number, uid: number,
lang: string, code: string, addTask: boolean, contestOrConfig?: ContestInfo | string,
) {
const data: Rdoc = {
status: STATUS.STATUS_WAITING,
_id: new ObjectID(),
uid,
code,
lang,
pid,
domainId,
score: 0,
time: 0,
memory: 0,
hidden: false,
judgeTexts: [],
compilerTexts: [],
testCases: [],
judger: null,
judgeAt: null,
rejudged: false,
};
if (typeof contestOrConfig === 'string') {
// is Run
data.input = contestOrConfig;
data.hidden = true;
} else data.contest = contestOrConfig;
const res = await RecordModel.coll.insertOne(data);
if (addTask) await RecordModel.judge(domainId, res.insertedId);
return res.insertedId;
}
static getMulti(domainId: string, query: any) {
return RecordModel.coll.find({ ...query, domainId });
}
static async update(
domainId: string, _id: ObjectID,
$set?: MatchKeysAndValues<Rdoc>,
$push?: PushOperator<Rdoc>,
$unset?: OnlyFieldsOfType<Rdoc, any, true | '' | 1>,
): Promise<Rdoc | null> {
const $update: UpdateQuery<Rdoc> = {};
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;
if (Object.keys($update).length) {
const res = await RecordModel.coll.findOneAndUpdate(
{ _id, domainId },
$update,
{ returnOriginal: false },
);
return res.value;
}
return await RecordModel.get(domainId, _id);
}
static reset(domainId: string, rid: ObjectID, isRejudge: boolean) {
const upd: any = {
score: 0,
status: STATUS.STATUS_WAITING,
time: 0,
memory: 0,
testCases: [],
judgeTexts: [],
compilerTexts: [],
judgeAt: null,
judger: null,
};
if (isRejudge) upd.rejudged = true;
return RecordModel.update(domainId, rid, upd);
}
static count(domainId: string, query: any) {
return RecordModel.coll.find({ domainId, ...query }).count();
}
static async getList(
domainId: string, rids: ObjectID[], showHidden: boolean,
): Promise<Dictionary<Rdoc>> {
const r = {};
rids = Array.from(new Set(rids));
const rdocs = await RecordModel.coll.find({ domainId, _id: { $in: rids } }).toArray();
for (const rdoc of rdocs) {
if (rdoc.hidden && !showHidden) r[rdoc._id.toHexString()] = null;
else r[rdoc._id.toHexString()] = rdoc;
}
return r;
}
@ArgMethod
static getUserInProblemMulti(
domainId: string, uid: number, pid: number,
getHidden: boolean = false,
) {
if (!getHidden) {
return RecordModel.coll.find({
domainId, uid, pid, hidden: false,
});
}
return RecordModel.coll.find({ domainId, uid, pid });
}
@ArgMethod
static getByUid(domainId: string, uid: number, limit: number): Promise<Rdoc[]> {
return RecordModel.coll.find({ domainId, uid }).limit(limit).toArray();
}
} }
export function getByUid(domainId: string, uid: number, limit: number): Promise<Rdoc[]> { bus.on('problem/delete', (domainId, docId) => RecordModel.coll.deleteMany({ domainId, pid: docId }));
return coll.find({ domainId, uid }).limit(limit).toArray(); export = RecordModel;
} global.Hydro.model.record = RecordModel;
bus.on('problem/delete', (domainId, docId) => coll.deleteMany({ domainId, pid: docId }));
global.Hydro.model.record = {
coll,
add,
get,
getMulti,
stat,
update,
count,
reset,
getList,
getUserInProblemMulti,
getByUid,
judge,
};
+66 -77
View File
@@ -3,80 +3,82 @@ import * as document from './document';
import { SolutionNotFoundError } from '../error'; import { SolutionNotFoundError } from '../error';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
export function add(domainId: string, pid: number, owner: number, content: string) { class SolutionModel {
return document.add( static add(domainId: string, pid: number, owner: number, content: string) {
domainId, content, owner, document.TYPE_PROBLEM_SOLUTION, return document.add(
null, document.TYPE_PROBLEM, pid, { reply: [], vote: 0 }, domainId, content, owner, document.TYPE_PROBLEM_SOLUTION,
); null, document.TYPE_PROBLEM, pid, { reply: [], vote: 0 },
} );
}
export async function get(domainId: string, psid: ObjectID) { static async get(domainId: string, psid: ObjectID) {
const psdoc = await document.get(domainId, document.TYPE_PROBLEM_SOLUTION, psid); const psdoc = await document.get(domainId, document.TYPE_PROBLEM_SOLUTION, psid);
if (!psdoc) throw new SolutionNotFoundError(); if (!psdoc) throw new SolutionNotFoundError();
return psdoc; return psdoc;
} }
export function getMany(domainId: string, query: any, sort: any, page: number, limit: number) { static getMany(domainId: string, query: any, sort: any, page: number, limit: number) {
return document.getMulti(domainId, document.TYPE_PROBLEM_SOLUTION, query) return document.getMulti(domainId, document.TYPE_PROBLEM_SOLUTION, query)
.sort(sort) .sort(sort)
.skip((page - 1) * limit).limit(limit) .skip((page - 1) * limit).limit(limit)
.toArray(); .toArray();
} }
export function edit(domainId: string, psid: ObjectID, content: string) { static edit(domainId: string, psid: ObjectID, content: string) {
return document.set(domainId, document.TYPE_PROBLEM_SOLUTION, psid, { content }); return document.set(domainId, document.TYPE_PROBLEM_SOLUTION, psid, { content });
} }
export async function del(domainId: string, psid: ObjectID) { static async del(domainId: string, psid: ObjectID) {
return await Promise.all([ return await Promise.all([
document.deleteOne(domainId, document.TYPE_PROBLEM_SOLUTION, psid), document.deleteOne(domainId, document.TYPE_PROBLEM_SOLUTION, psid),
document.deleteMultiStatus(domainId, document.TYPE_PROBLEM_SOLUTION, { docId: psid }), document.deleteMultiStatus(domainId, document.TYPE_PROBLEM_SOLUTION, { docId: psid }),
]); ]);
} }
export function count(domainId: string, query: any) { static count(domainId: string, query: any) {
return document.count(domainId, document.TYPE_PROBLEM_SOLUTION, query); return document.count(domainId, document.TYPE_PROBLEM_SOLUTION, query);
} }
export function getMulti(domainId: string, pid: number) { static getMulti(domainId: string, pid: number) {
return document.getMulti( return document.getMulti(
domainId, document.TYPE_PROBLEM_SOLUTION, domainId, document.TYPE_PROBLEM_SOLUTION,
{ parentType: document.TYPE_PROBLEM, parentId: pid }, { parentType: document.TYPE_PROBLEM, parentId: pid },
).sort({ vote: -1 }); ).sort({ vote: -1 });
} }
export function reply(domainId: string, psid: ObjectID, owner: number, content: string) { static reply(domainId: string, psid: ObjectID, owner: number, content: string) {
return document.push(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', content, owner); return document.push(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', content, owner);
} }
export function getReply(domainId: string, psid: ObjectID, psrid: ObjectID) { static getReply(domainId: string, psid: ObjectID, psrid: ObjectID) {
return document.getSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid); return document.getSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
} }
export function editReply(domainId: string, psid: ObjectID, psrid: ObjectID, content: string) { static editReply(domainId: string, psid: ObjectID, psrid: ObjectID, content: string) {
return document.setSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid, { content }); return document.setSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid, { content });
} }
export function delReply(domainId: string, psid: ObjectID, psrid: ObjectID) { static delReply(domainId: string, psid: ObjectID, psrid: ObjectID) {
return document.deleteSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid); return document.deleteSub(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'reply', psrid);
} }
export async function vote(domainId: string, psid: ObjectID, uid: number, value: number) { static async vote(domainId: string, psid: ObjectID, uid: number, value: number) {
let pssdoc = await document.getStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid); let pssdoc = await document.getStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid);
await document.setStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid, { vote: value }); await document.setStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid, { vote: value });
if (pssdoc) value += -pssdoc.vote; if (pssdoc) value += -pssdoc.vote;
const psdoc = await document.inc(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'vote', value); const psdoc = await document.inc(domainId, document.TYPE_PROBLEM_SOLUTION, psid, 'vote', value);
pssdoc = await document.getStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid); pssdoc = await document.getStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid);
return [psdoc, pssdoc]; return [psdoc, pssdoc];
} }
export async function getListStatus(domainId: string, psids: ObjectID[], uid: number) { static async getListStatus(domainId: string, psids: ObjectID[], uid: number) {
const result = {}; const result = {};
const res = await document.getMultiStatus( const res = await document.getMultiStatus(
domainId, document.TYPE_PROBLEM_SOLUTION, { uid, psid: { $in: psids } }, domainId, document.TYPE_PROBLEM_SOLUTION, { uid, psid: { $in: psids } },
).toArray(); ).toArray();
for (const i of res) result[i.psid] = i; for (const i of res) result[i.psid] = i;
return result; return result;
}
} }
bus.on('problem/delete', async (domainId, docId) => { bus.on('problem/delete', async (domainId, docId) => {
@@ -90,18 +92,5 @@ bus.on('problem/delete', async (domainId, docId) => {
]); ]);
}); });
global.Hydro.model.solution = { export = SolutionModel;
count, global.Hydro.model.solution = SolutionModel;
add,
get,
edit,
del,
getMany,
getMulti,
reply,
getReply,
editReply,
delReply,
vote,
getListStatus,
};
+84 -87
View File
@@ -7,98 +7,95 @@ import db from '../service/db';
const logger = new Logger('model/task'); const logger = new Logger('model/task');
const coll = db.collection('task'); const coll = db.collection('task');
export async function add(task: Partial<Task> & { type: string }) { class TaskModel {
const t: Task = { static async add(task: Partial<Task> & { type: string }) {
...task, const t: Task = {
count: task.count ?? 1, ...task,
priority: task.priority ?? 1, count: task.count ?? 1,
executeAfter: task.executeAfter || new Date(), priority: task.priority ?? 1,
_id: new ObjectID(), executeAfter: task.executeAfter || new Date(),
}; _id: new ObjectID(),
const res = await coll.insertOne(t); };
return res.insertedId; const res = await coll.insertOne(t);
} return res.insertedId;
export function get(_id: ObjectID) {
return coll.findOne({ _id });
}
export function count(query: FilterQuery<Task>) {
return coll.find(query).count();
}
export function del(_id: ObjectID) {
return coll.deleteOne({ _id });
}
export async function getFirst(query: FilterQuery<Task>) {
const q = { ...query };
q.executeAfter = q.executeAfter || { $lt: new Date() };
const res = await coll.findOneAndDelete(q, { sort: { priority: -1 } });
if (res.value) {
logger.debug('%o', res.value);
if (res.value.interval) {
await coll.insertOne({
...res.value, executeAfter: moment().add(...res.value.interval).toDate(),
});
}
return res.value;
} }
return null;
}
export async function consume(query: any, cb: Function) { static get(_id: ObjectID) {
let isRunning = false; return coll.findOne({ _id });
const interval = setInterval(async () => { }
if (isRunning) return;
isRunning = true; static count(query: FilterQuery<Task>) {
const res = await getFirst(query); return coll.find(query).count();
if (res) { }
try {
await cb(res); static del(_id: ObjectID) {
} catch (e) { return coll.deleteOne({ _id });
clearInterval(interval); }
static async getFirst(query: FilterQuery<Task>) {
const q = { ...query };
q.executeAfter = q.executeAfter || { $lt: new Date() };
const res = await coll.findOneAndDelete(q, { sort: { priority: -1 } });
if (res.value) {
logger.debug('%o', res.value);
if (res.value.interval) {
await coll.insertOne({
...res.value, executeAfter: moment().add(...res.value.interval).toDate(),
});
}
return res.value;
}
return null;
}
static async consume(query: any, cb: Function) {
let isRunning = false;
const interval = setInterval(async () => {
if (isRunning) return;
isRunning = true;
const res = await TaskModel.getFirst(query);
if (res) {
try {
await cb(res);
} catch (e) {
clearInterval(interval);
}
}
isRunning = false;
}, 100);
}
Consumer = class Consumer {
consuming: boolean;
running?: Promise<any>;
interval: NodeJS.Timeout;
filter: any;
func: Function;
constructor(filter: any, func: Function) {
this.consuming = true;
this.filter = filter;
this.func = func;
this.interval = setInterval(this.consume.bind(this), 100);
}
async consume() {
if (this.running || !this.consuming) return;
const res = await TaskModel.getFirst(this.filter);
if (res) {
this.running = this.func(res);
if (this.running instanceof Promise) await this.running;
this.running = null;
} }
} }
isRunning = false;
}, 100);
}
export class Consumer { async destory() {
consuming: boolean; this.consuming = false;
clearInterval(this.interval);
running?: Promise<any>; if (this.running) await this.running;
interval: NodeJS.Timeout;
filter: any;
func: Function;
constructor(filter: any, func: Function) {
this.consuming = true;
this.filter = filter;
this.func = func;
this.interval = setInterval(this.consume.bind(this), 100);
}
async consume() {
if (this.running || !this.consuming) return;
const res = await getFirst(this.filter);
if (res) {
this.running = this.func(res);
if (this.running instanceof Promise) await this.running;
this.running = null;
} }
} };
async destory() {
this.consuming = false;
clearInterval(this.interval);
if (this.running) await this.running;
}
} }
global.Hydro.model.task = { export = TaskModel;
Consumer, add, get, del, count, getFirst, consume, global.Hydro.model.task = TaskModel;
};
+1 -1
View File
@@ -2,7 +2,7 @@ import { Collection } from 'mongodb';
import * as system from './system'; import * as system from './system';
import token from './token'; import token from './token';
import * as setting from './setting'; import * as setting from './setting';
import * as domain from './domain'; import domain from './domain';
import { BUILTIN_USERS, PERM, PRIV } from './builtin'; import { BUILTIN_USERS, PERM, PRIV } from './builtin';
import { ArgMethod } from '../utils'; import { ArgMethod } from '../utils';
import { UserNotFoundError, UserAlreadyExistError, LoginError } from '../error'; import { UserNotFoundError, UserAlreadyExistError, LoginError } from '../error';
+2 -2
View File
@@ -2,10 +2,10 @@
import { NumericDictionary } from 'lodash'; import { NumericDictionary } from 'lodash';
import { ObjectID, FilterQuery } from 'mongodb'; import { ObjectID, FilterQuery } from 'mongodb';
import { Tdoc, Pdoc, Udoc } from '../interface'; import { Tdoc, Pdoc, Udoc } from '../interface';
import * as domain from '../model/domain'; import domain from '../model/domain';
import * as contest from '../model/contest'; import * as contest from '../model/contest';
import * as problem from '../model/problem'; import * as problem from '../model/problem';
import * as record from '../model/record'; import record from '../model/record';
import { STATUS } from '../model/builtin'; import { STATUS } from '../model/builtin';
import rating from '../lib/rating'; import rating from '../lib/rating';
import paginate from '../lib/paginate'; import paginate from '../lib/paginate';
+1 -1
View File
@@ -27,7 +27,7 @@ import {
import hash from '../lib/hash.hydro'; import hash from '../lib/hash.hydro';
import * as misc from '../lib/misc'; import * as misc from '../lib/misc';
import user from '../model/user'; import user from '../model/user';
import * as domain from '../model/domain'; import domain from '../model/domain';
import * as system from '../model/system'; import * as system from '../model/system';
import blacklist from '../model/blacklist'; import blacklist from '../model/blacklist';
import token from '../model/token'; import token from '../model/token';
+1 -1
View File
@@ -12,7 +12,7 @@ import gridfs from './service/gridfs';
import storage from './service/storage'; import storage from './service/storage';
import db from './service/db'; import db from './service/db';
import * as problem from './model/problem'; import * as problem from './model/problem';
import * as domain from './model/domain'; import domain from './model/domain';
import * as document from './model/document'; import * as document from './model/document';
import * as system from './model/system'; import * as system from './model/system';
import difficultyAlgorithm from './lib/difficulty'; import difficultyAlgorithm from './lib/difficulty';
+1 -1
View File
@@ -1,7 +1,7 @@
import { FilterQuery } from 'mongodb'; import { FilterQuery } from 'mongodb';
import { Pdoc } from 'hydrooj/dist/interface'; import { Pdoc } from 'hydrooj/dist/interface';
import * as problem from 'hydrooj/dist/model/problem'; import * as problem from 'hydrooj/dist/model/problem';
import * as record from 'hydrooj/dist/model/record'; import record from 'hydrooj/dist/model/record';
import user from 'hydrooj/dist/model/user'; import user from 'hydrooj/dist/model/user';
import { PERM, PRIV } from 'hydrooj/dist/model/builtin'; import { PERM, PRIV } from 'hydrooj/dist/model/builtin';
import { import {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/syzoj-ng-api", "name": "@hydrooj/syzoj-ng-api",
"version": "0.0.1", "version": "0.0.2",
"description": "A syzoj-ng API interface", "description": "A syzoj-ng API interface",
"main": "package.json", "main": "package.json",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",