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
+194 -272

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 { buildContent } from 'hydrooj/dist/lib/content';
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 { FileTooLargeError, ValidationError } from 'hydrooj/dist/error';
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/fps-importer",
"version": "1.1.9",
"version": "1.1.10",
"description": "Import FPS problems",
"main": "package.json",
"repository": "https://github.com/hydro-dev/Hydro.git",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "2.20.21",
"version": "2.20.22",
"bin": "bin/hydrooj.js",
"main": "dist/loader.js",
"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 document from '../model/document';
import * as problem from '../model/problem';
import * as domain from '../model/domain';
import * as record from '../model/record';
import domain from '../model/domain';
import record from '../model/record';
import user from '../model/user';
import * as message from '../model/message';
import message from '../model/message';
import {
Route, Handler, Types, param,
} from '../service/server';
+1 -1
View File
@@ -8,7 +8,7 @@ import paginate from '../lib/paginate';
import * as system from '../model/system';
import user from '../model/user';
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 document from '../model/document';
import { PERM, PRIV } from '../model/builtin';
+1 -1
View File
@@ -7,7 +7,7 @@ import {
import { log2 } from '../utils';
import type { DomainDoc } from '../interface';
import user from '../model/user';
import * as domain from '../model/domain';
import domain from '../model/domain';
import * as discussion from '../model/discussion';
import * as system from '../model/system';
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 * as mail from '../lib/mail';
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 system from '../model/system';
import user from '../model/user';
import * as setting from '../model/setting';
import * as domain from '../model/domain';
import domain from '../model/domain';
import * as discussion from '../model/discussion';
import token from '../model/token';
import * as training from '../model/training';
+2 -2
View File
@@ -12,14 +12,14 @@ import {
Route, Handler, Types, param,
} from '../service/server';
import * as bus from '../service/bus';
import * as domain from '../model/domain';
import domain from '../model/domain';
import { PERM } from '../model/builtin';
import user from '../model/user';
import * as system from '../model/system';
import * as contest from '../model/contest';
import * as discussion from '../model/discussion';
import * as problem from '../model/problem';
import * as record from '../model/record';
import record from '../model/record';
import * as document from '../model/document';
import paginate from '../lib/paginate';
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 { Logger } from '../logger';
import difficultyAlgorithm from '../lib/difficulty';
import * as record from '../model/record';
import record from '../model/record';
import * as problem from '../model/problem';
import * as builtin from '../model/builtin';
import * as contest from '../model/contest';
import * as domain from '../model/domain';
import * as task from '../model/task';
import domain from '../model/domain';
import task from '../model/task';
import * as bus from '../service/bus';
import {
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 user from '../model/user';
import { STATUS, PRIV } from '../model/builtin';
import * as record from '../model/record';
import record from '../model/record';
import {
Route, Connection, Handler, ConnectionHandler, param, Types,
} from '../service/server';
+3 -3
View File
@@ -13,10 +13,10 @@ import { isTitle, isContent, isPid } from '../lib/validator';
import difficultyAlgorithm from '../lib/difficulty';
import * as system from '../model/system';
import * as problem from '../model/problem';
import * as record from '../model/record';
import * as domain from '../model/domain';
import record from '../model/record';
import domain from '../model/domain';
import user from '../model/user';
import * as solution from '../model/solution';
import solution from '../model/solution';
import { PERM, PRIV } from '../model/builtin';
import storage from '../service/storage';
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 * as system from '../model/system';
import * as problem from '../model/problem';
import * as record from '../model/record';
import record from '../model/record';
import * as contest from '../model/contest';
import user from '../model/user';
import paginate from '../lib/paginate';
+3 -3
View File
@@ -8,11 +8,11 @@ import {
Route, Handler, Types, param, post,
} from '../service/server';
import user from '../model/user';
import * as oauth from '../model/oauth';
import oauth from '../model/oauth';
import token from '../model/token';
import * as record from '../model/record';
import record from '../model/record';
import * as problem from '../model/problem';
import * as task from '../model/task';
import task from '../model/task';
import * as system from '../model/system';
import { PERM, PRIV } from '../model/builtin';
import { isEmail, isPassword, isUname } from '../lib/validator';
+64 -77
View File
@@ -2,6 +2,7 @@ import { Dictionary } from 'lodash';
import { FilterQuery } from 'mongodb';
import { BUILTIN_ROLES, PRIV } from './builtin';
import { DomainDoc } from '../interface';
import { ArgMethod } from '../utils';
import { NumberKeys } from '../typeutils';
import * as bus from '../service/bus';
import db from '../service/db';
@@ -9,29 +10,37 @@ import db from '../service/db';
const coll = db.collection('domain');
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',
interface DomainUserArg {
_id: number,
priv: number,
}
class DomainModel {
static JOIN_METHOD_NONE = 0;
static JOIN_METHOD_ALL = 1;
static JOIN_METHOD_CODE = 2;
static JOIN_METHOD_RANGE = {
[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 const JOIN_EXPIRATION_KEEP_CURRENT = 0;
export const JOIN_EXPIRATION_UNLIMITED = -1;
static JOIN_EXPIRATION_KEEP_CURRENT = 0;
static JOIN_EXPIRATION_UNLIMITED = -1;
export const JOIN_EXPIRATION_RANGE = {
[JOIN_EXPIRATION_KEEP_CURRENT]: 'Keep current expiration',
static JOIN_EXPIRATION_RANGE = {
[DomainModel.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',
[DomainModel.JOIN_EXPIRATION_UNLIMITED]: 'Never expire',
};
export async function add(domainId: string, owner: number, name: string, bulletin: string) {
@ArgMethod
static async add(domainId: string, owner: number, name: string, bulletin: string) {
const ddoc: DomainDoc = {
_id: domainId,
owner,
@@ -46,7 +55,8 @@ export async function add(domainId: string, owner: number, name: string, bulleti
return domainId;
}
export async function get(domainId: string): Promise<DomainDoc | null> {
@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);
@@ -54,7 +64,8 @@ export async function get(domainId: string): Promise<DomainDoc | null> {
return result;
}
export async function getByHost(host: string): Promise<DomainDoc | null> {
@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);
@@ -62,18 +73,19 @@ export async function getByHost(host: string): Promise<DomainDoc | null> {
return result;
}
export function getMulti(query: FilterQuery<DomainDoc> = {}) {
static getMulti(query: FilterQuery<DomainDoc> = {}) {
return coll.find(query);
}
export async function edit(domainId: string, $set: Partial<DomainDoc>) {
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;
}
export async function inc(domainId: string, field: NumberKeys<DomainDoc>, n: number): Promise<number | null> {
@ArgMethod
static async inc(domainId: string, field: NumberKeys<DomainDoc>, n: number): Promise<number | null> {
const res = await coll.findOneAndUpdate(
{ _id: domainId },
// FIXME
@@ -84,22 +96,25 @@ export async function inc(domainId: string, field: NumberKeys<DomainDoc>, n: num
return res.value[field];
}
export async function getList(domainIds: string[]): Promise<Dictionary<DomainDoc>> {
@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 get(domainId);
for (const domainId of domainIds) r[domainId] = await DomainModel.get(domainId);
return r;
}
export function setUserRole(domainId: string, uid: number, role: string) {
@ArgMethod
static 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) {
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 get(arg);
if (typeof arg === 'string') ddoc = await DomainModel.get(arg);
else ddoc = arg;
const roles = [];
const r = [];
@@ -115,22 +130,22 @@ export async function getRoles(arg: string | DomainDoc) {
return roles;
}
export async function setRoles(domainId: string, roles: Dictionary<bigint>) {
const current = await get(domainId);
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 } });
}
export async function addRole(domainId: string, name: string, permission: bigint) {
const current = await get(domainId);
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 } });
}
export async function deleteRoles(domainId: string, roles: string[]) {
const current = await get(domainId);
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 }),
@@ -138,17 +153,13 @@ export async function deleteRoles(domainId: string, roles: string[]) {
]);
}
interface DomainUserArg {
_id: number,
priv: number,
}
export async function getDomainUser(domainId: string, udoc: DomainUserArg) {
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 get(domainId);
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])
@@ -156,27 +167,29 @@ export async function getDomainUser(domainId: string, udoc: DomainUserArg) {
return dudoc;
}
export function setMultiUserInDomain(domainId: string, query: any, params: any) {
static setMultiUserInDomain(domainId: string, query: any, params: any) {
return collUser.updateMany({ domainId, ...query }, { $set: params });
}
export function getMultiUserInDomain(domainId: string, query: any = {}) {
static getMultiUserInDomain(domainId: string, query: any = {}) {
return collUser.find({ domainId, ...query });
}
export function setUserInDomain(domainId: string, uid: number, params: any) {
static setUserInDomain(domainId: string, uid: number, params: any) {
return collUser.updateOne({ domainId, uid }, { $set: params });
}
export async function incUserInDomain(domainId: string, uid: number, field: string, n = 1) {
@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 setUserInDomain(domainId, uid, { [field]: dudoc[field] });
await DomainModel.setUserInDomain(domainId, uid, { [field]: dudoc[field] });
return dudoc;
}
export async function getDictUserByDomainId(uid: number) {
@ArgMethod
static async getDictUserByDomainId(uid: number) {
const [dudocs, ddocs] = await Promise.all([
collUser.find({ uid }).toArray(),
coll.find({ owner: uid }).toArray(),
@@ -184,57 +197,31 @@ export async function getDictUserByDomainId(uid: number) {
const dudict = {};
for (const dudoc of dudocs) {
// eslint-disable-next-line no-await-in-loop
dudict[dudoc.domainId] = await get(dudoc.domainId);
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;
}
export function getJoinSettings(ddoc: DomainDoc, roles: string[]) {
static getJoinSettings(ddoc: DomainDoc, roles: string[]) {
if (!ddoc.join) return null;
const joinSettings = ddoc.join;
if (joinSettings.method === JOIN_METHOD_NONE) return null;
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;
}
export async function getPrefixSearch(prefix: string, limit = 50) {
@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;
}
}
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,
};
export = DomainModel;
global.Hydro.model.domain = DomainModel;
+19 -24
View File
@@ -1,17 +1,20 @@
import { FilterQuery, ObjectID } from 'mongodb';
import user from './user';
import { Mdoc } from '../interface';
import { ArgMethod } from '../utils';
import db from '../service/db';
import * as bus from '../service/bus';
const coll = db.collection('message');
export const FLAG_UNREAD = 1;
export const FLAG_ALERT = 2;
class MessageModel {
static FLAG_UNREAD = 1;
static FLAG_ALERT = 2;
export async function send(
@ArgMethod
static async send(
from: number, to: number,
content: string, flag: number = FLAG_UNREAD,
content: string, flag: number = MessageModel.FLAG_UNREAD,
): Promise<Mdoc> {
const res = await coll.insertOne({
from, to, content, flag,
@@ -27,21 +30,22 @@ export async function send(
return mdoc;
}
export async function get(_id: ObjectID): Promise<Mdoc | null> {
static async get(_id: ObjectID): Promise<Mdoc | null> {
return await coll.findOne({ _id });
}
export async function getByUser(uid: number): Promise<Mdoc[]> {
@ArgMethod
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)
.skip((page - 1) * limit).limit(limit)
.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(
{ _id: messageId },
{ $bit: { flag: { xor: flag } } },
@@ -50,17 +54,19 @@ export async function setFlag(messageId: ObjectID, flag: number): Promise<Mdoc |
return result.value;
}
export async function del(_id: ObjectID) {
static async del(_id: ObjectID) {
return await coll.deleteOne({ _id });
}
export function count(query: FilterQuery<Mdoc> = {}) {
@ArgMethod
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 }] });
}
}
function ensureIndexes() {
return Promise.all([
@@ -70,16 +76,5 @@ function ensureIndexes() {
}
bus.once('app/started', ensureIndexes);
global.Hydro.model.message = {
FLAG_UNREAD,
FLAG_ALERT,
count,
get,
getByUser,
del,
setFlag,
getMany,
getMulti,
send,
};
export = MessageModel;
global.Hydro.model.message = MessageModel;
+6 -6
View File
@@ -12,7 +12,8 @@ const cache = new Lru({
maxAge: 5000,
});
export async function get(_id: string) {
class OauthModel {
static async get(_id: string) {
const res = cache.get(_id);
if (res !== undefined) return res;
const doc = await coll.findOne({ _id });
@@ -20,7 +21,7 @@ export async function get(_id: string) {
return null;
}
export async function set(_id: string, value: number) {
static async set(_id: string, value: number) {
cache.set(_id, value);
const res = await coll.findOneAndUpdate(
{ _id },
@@ -29,8 +30,7 @@ export async function set(_id: string, value: number) {
);
return res.value.uid;
}
}
global.Hydro.model.oauth = {
get,
set,
};
export = OauthModel;
global.Hydro.model.oauth = OauthModel;
+1 -1
View File
@@ -3,7 +3,7 @@ import { ObjectID, FilterQuery } from 'mongodb';
import { Dictionary, pick } from 'lodash';
import { STATUS } from './builtin';
import * as document from './document';
import * as domain from './domain';
import domain from './domain';
import { buildProjection } from '../utils';
import type { ProblemStatusDoc, Pdict, Document } from '../interface';
import { Content } from '../loader';
+44 -90
View File
@@ -4,76 +4,40 @@ import {
import { Dictionary } from 'lodash';
import moment from 'moment';
import { STATUS } from './builtin';
import * as task from './task';
import task from './task';
import * as problem from './problem';
import {
Rdoc, TestCase, ContestInfo, ProblemConfig,
} from '../interface';
import { Time } from '../utils';
import { Rdoc, ContestInfo, ProblemConfig } from '../interface';
import { ArgMethod, Time } from '../utils';
import * as bus from '../service/bus';
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 {
_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 get(domainId: string, _id: ObjectID): Promise<Rdoc | null> {
const res = await coll.findOne({ _id });
static async get(domainId: string, _id: ObjectID): Promise<Rdoc | null> {
const res = await RecordModel.coll.findOne({ _id });
if (res && res.domainId === domainId) return res;
return null;
}
export async function stat(domainId?: string) {
@ArgMethod
static async 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(),
RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-5, 'minutes')) }, ...domainId ? { domainId } : {} }).count(),
RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'hour')) }, ...domainId ? { domainId } : {} }).count(),
RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'day')) }, ...domainId ? { domainId } : {} }).count(),
RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'week')) }, ...domainId ? { domainId } : {} }).count(),
RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'month')) }, ...domainId ? { domainId } : {} }).count(),
RecordModel.coll.find({ _id: { $gte: Time.getObjectID(moment().add(-1, 'year')) }, ...domainId ? { domainId } : {} }).count(),
RecordModel.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);
static async judge(domainId: string, rid: ObjectID, priority = 1, config: ProblemConfig = {}) {
const rdoc = await RecordModel.get(domainId, rid);
let data = [];
if (rdoc.pid) {
const pdoc = await problem.get(domainId, rdoc.pid);
@@ -91,7 +55,7 @@ export async function judge(domainId: string, rid: ObjectID, priority = 1, confi
});
}
export async function add(
static async add(
domainId: string, pid: number, uid: number,
lang: string, code: string, addTask: boolean, contestOrConfig?: ContestInfo | string,
) {
@@ -119,16 +83,16 @@ export async function add(
data.input = contestOrConfig;
data.hidden = true;
} else data.contest = contestOrConfig;
const res = await coll.insertOne(data);
if (addTask) await judge(domainId, res.insertedId);
const res = await RecordModel.coll.insertOne(data);
if (addTask) await RecordModel.judge(domainId, res.insertedId);
return res.insertedId;
}
export function getMulti(domainId: string, query: any) {
return coll.find({ ...query, domainId });
static getMulti(domainId: string, query: any) {
return RecordModel.coll.find({ ...query, domainId });
}
export async function update(
static async update(
domainId: string, _id: ObjectID,
$set?: MatchKeysAndValues<Rdoc>,
$push?: PushOperator<Rdoc>,
@@ -139,17 +103,17 @@ export async function update(
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(
const res = await RecordModel.coll.findOneAndUpdate(
{ _id, domainId },
$update,
{ returnOriginal: false },
);
return res.value;
}
return await get(domainId, _id);
return await RecordModel.get(domainId, _id);
}
export function reset(domainId: string, rid: ObjectID, isRejudge: boolean) {
static reset(domainId: string, rid: ObjectID, isRejudge: boolean) {
const upd: any = {
score: 0,
status: STATUS.STATUS_WAITING,
@@ -162,19 +126,19 @@ export function reset(domainId: string, rid: ObjectID, isRejudge: boolean) {
judger: null,
};
if (isRejudge) upd.rejudged = true;
return update(domainId, rid, upd);
return RecordModel.update(domainId, rid, upd);
}
export function count(domainId: string, query: any) {
return coll.find({ domainId, ...query }).count();
static count(domainId: string, query: any) {
return RecordModel.coll.find({ domainId, ...query }).count();
}
export async function getList(
static async 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();
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;
@@ -182,35 +146,25 @@ export async function getList(
return r;
}
export function getUserInProblemMulti(
@ArgMethod
static getUserInProblemMulti(
domainId: string, uid: number, pid: number,
getHidden = false,
getHidden: boolean = false,
) {
if (!getHidden) {
return coll.find({
return RecordModel.coll.find({
domainId, uid, pid, hidden: false,
});
}
return coll.find({ domainId, uid, pid });
return RecordModel.coll.find({ domainId, uid, pid });
}
export function getByUid(domainId: string, uid: number, limit: number): Promise<Rdoc[]> {
return coll.find({ domainId, uid }).limit(limit).toArray();
@ArgMethod
static getByUid(domainId: string, uid: number, limit: number): Promise<Rdoc[]> {
return RecordModel.coll.find({ domainId, uid }).limit(limit).toArray();
}
}
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,
};
bus.on('problem/delete', (domainId, docId) => RecordModel.coll.deleteMany({ domainId, pid: docId }));
export = RecordModel;
global.Hydro.model.record = RecordModel;
+17 -28
View File
@@ -3,65 +3,66 @@ import * as document from './document';
import { SolutionNotFoundError } from '../error';
import * as bus from '../service/bus';
export function add(domainId: string, pid: number, owner: number, content: string) {
class SolutionModel {
static 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 },
);
}
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);
if (!psdoc) throw new SolutionNotFoundError();
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)
.sort(sort)
.skip((page - 1) * limit).limit(limit)
.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 });
}
export async function del(domainId: string, psid: ObjectID) {
static async del(domainId: string, psid: ObjectID) {
return await Promise.all([
document.deleteOne(domainId, document.TYPE_PROBLEM_SOLUTION, 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);
}
export function getMulti(domainId: string, pid: number) {
static getMulti(domainId: string, pid: number) {
return document.getMulti(
domainId, document.TYPE_PROBLEM_SOLUTION,
{ parentType: document.TYPE_PROBLEM, parentId: pid },
).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);
}
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);
}
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 });
}
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);
}
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);
await document.setStatus(domainId, document.TYPE_PROBLEM_SOLUTION, psid, uid, { vote: value });
if (pssdoc) value += -pssdoc.vote;
@@ -70,7 +71,7 @@ export async function vote(domainId: string, psid: ObjectID, uid: number, value:
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 res = await document.getMultiStatus(
domainId, document.TYPE_PROBLEM_SOLUTION, { uid, psid: { $in: psids } },
@@ -78,6 +79,7 @@ export async function getListStatus(domainId: string, psids: ObjectID[], uid: nu
for (const i of res) result[i.psid] = i;
return result;
}
}
bus.on('problem/delete', async (domainId, docId) => {
const psids = await document.getMulti(
@@ -90,18 +92,5 @@ bus.on('problem/delete', async (domainId, docId) => {
]);
});
global.Hydro.model.solution = {
count,
add,
get,
edit,
del,
getMany,
getMulti,
reply,
getReply,
editReply,
delReply,
vote,
getListStatus,
};
export = SolutionModel;
global.Hydro.model.solution = SolutionModel;
+13 -16
View File
@@ -7,7 +7,8 @@ import db from '../service/db';
const logger = new Logger('model/task');
const coll = db.collection('task');
export async function add(task: Partial<Task> & { type: string }) {
class TaskModel {
static async add(task: Partial<Task> & { type: string }) {
const t: Task = {
...task,
count: task.count ?? 1,
@@ -19,19 +20,19 @@ export async function add(task: Partial<Task> & { type: string }) {
return res.insertedId;
}
export function get(_id: ObjectID) {
static get(_id: ObjectID) {
return coll.findOne({ _id });
}
export function count(query: FilterQuery<Task>) {
static count(query: FilterQuery<Task>) {
return coll.find(query).count();
}
export function del(_id: ObjectID) {
static del(_id: ObjectID) {
return coll.deleteOne({ _id });
}
export async function getFirst(query: FilterQuery<Task>) {
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 } });
@@ -47,12 +48,12 @@ export async function getFirst(query: FilterQuery<Task>) {
return null;
}
export async function consume(query: any, cb: Function) {
static async consume(query: any, cb: Function) {
let isRunning = false;
const interval = setInterval(async () => {
if (isRunning) return;
isRunning = true;
const res = await getFirst(query);
const res = await TaskModel.getFirst(query);
if (res) {
try {
await cb(res);
@@ -64,15 +65,11 @@ export async function consume(query: any, cb: Function) {
}, 100);
}
export class Consumer {
Consumer = class Consumer {
consuming: boolean;
running?: Promise<any>;
interval: NodeJS.Timeout;
filter: any;
func: Function;
constructor(filter: any, func: Function) {
@@ -84,7 +81,7 @@ export class Consumer {
async consume() {
if (this.running || !this.consuming) return;
const res = await getFirst(this.filter);
const res = await TaskModel.getFirst(this.filter);
if (res) {
this.running = this.func(res);
if (this.running instanceof Promise) await this.running;
@@ -97,8 +94,8 @@ export class Consumer {
clearInterval(this.interval);
if (this.running) await this.running;
}
};
}
global.Hydro.model.task = {
Consumer, add, get, del, count, getFirst, consume,
};
export = TaskModel;
global.Hydro.model.task = TaskModel;
+1 -1
View File
@@ -2,7 +2,7 @@ import { Collection } from 'mongodb';
import * as system from './system';
import token from './token';
import * as setting from './setting';
import * as domain from './domain';
import domain from './domain';
import { BUILTIN_USERS, PERM, PRIV } from './builtin';
import { ArgMethod } from '../utils';
import { UserNotFoundError, UserAlreadyExistError, LoginError } from '../error';
+2 -2
View File
@@ -2,10 +2,10 @@
import { NumericDictionary } from 'lodash';
import { ObjectID, FilterQuery } from 'mongodb';
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 problem from '../model/problem';
import * as record from '../model/record';
import record from '../model/record';
import { STATUS } from '../model/builtin';
import rating from '../lib/rating';
import paginate from '../lib/paginate';
+1 -1
View File
@@ -27,7 +27,7 @@ import {
import hash from '../lib/hash.hydro';
import * as misc from '../lib/misc';
import user from '../model/user';
import * as domain from '../model/domain';
import domain from '../model/domain';
import * as system from '../model/system';
import blacklist from '../model/blacklist';
import token from '../model/token';
+1 -1
View File
@@ -12,7 +12,7 @@ import gridfs from './service/gridfs';
import storage from './service/storage';
import db from './service/db';
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 system from './model/system';
import difficultyAlgorithm from './lib/difficulty';
+1 -1
View File
@@ -1,7 +1,7 @@
import { FilterQuery } from 'mongodb';
import { Pdoc } from 'hydrooj/dist/interface';
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 { PERM, PRIV } from 'hydrooj/dist/model/builtin';
import {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/syzoj-ng-api",
"version": "0.0.1",
"version": "0.0.2",
"description": "A syzoj-ng API interface",
"main": "package.json",
"repository": "https://github.com/hydro-dev/Hydro.git",