This commit is contained in:
undefined committed 2020-08-06 14:24:16 +08:00
1 parent 8389519249
commit a5886ecb99
37 files changed
+940 -162

No files matched your search

+4 -1
View File
@@ -58,7 +58,10 @@ export async function load(call: Entry) {
await ins.run({ username: 'Root', password: 'rootroot' });
}
for (const postInit of global.Hydro.postInit) {
await postInit().catch(console.error);
await postInit().catch((e) => {
console.error('Error running postInit:', postInit.toString());
console.error(e);
});
}
return await modelSystem.get('server.worker');
}
+2 -2
View File
@@ -118,7 +118,7 @@ class ContestBoardcastHandler extends ContestHandler {
const tdoc = await contest.get(domainId, tid);
if (tdoc.owner !== this.user._id) throw new PermissionError('Boardcast Message');
const tsdocs = await contest.getMultiStatus(domainId, { docId: tid }).toArray();
const uids = Array.from(new Set(tsdocs.map((tsdoc) => tsdoc.uid)));
const uids: number[] = Array.from(new Set(tsdocs.map((tsdoc) => tsdoc.uid)));
await Promise.all(
uids.map((uid) => message.send(this.user._id, uid, content, message.FLAG_ALERT)),
);
@@ -343,7 +343,7 @@ class ContestCodeHandler extends ContestHandler {
},
}).toArray();
for (const rdoc of rdocs) {
zip.addFile(`${rnames[rdoc._id]}.${rdoc.lang}`, rdoc.code);
zip.addFile(`${rnames[rdoc._id.toHexString()]}.${rdoc.lang}`, Buffer.from(rdoc.code));
}
await this.binary(zip.toBuffer(), `${tdoc.title}.zip`);
}
+1 -1
View File
@@ -395,7 +395,7 @@ class HomeworkCodeHandler extends HomeworkHandler {
},
}).toArray();
for (const rdoc of rdocs) {
zip.addFile(`${rnames[rdoc._id]}.${rdoc.lang}`, rdoc.code);
zip.addFile(`${rnames[rdoc._id.toHexString()]}.${rdoc.lang}`, Buffer.from(rdoc.code));
}
await this.binary(zip.toBuffer(), `${tdoc.title}.zip`);
}
+3 -2
View File
@@ -18,6 +18,7 @@ import { isEmail, isPassword, isUname } from '../lib/validator';
import { sendMail } from '../lib/mail';
import * as misc from '../lib/misc';
import paginate from '../lib/paginate';
import { User } from '../interface';
class UserLoginHandler extends Handler {
async get() {
@@ -201,12 +202,12 @@ class UserSearchHandler extends Handler {
@param('exectMatch', Types.Boolean)
async get(domainId: string, q: string, exactMatch = false) {
let udoc = await user.getById(domainId, parseInt(q, 10));
const udocs = udoc ? [udoc] : [];
const udocs: User[] = udoc ? [udoc] : [];
udoc = await user.getByUname(domainId, q);
if (udoc) udocs.push(udoc);
udoc = await user.getByEmail(domainId, q);
if (udoc) udocs.push(udoc);
if (!exactMatch) udocs.push(...await user.getPrefixList(q, 20));
if (!exactMatch) udocs.push(...await user.getPrefixList(domainId, q, 20));
for (const i in udocs) {
udocs[i].gravatar = misc.gravatar(udocs[i].gravatar || '');
}
+64 -56
View File
@@ -247,9 +247,10 @@ export interface Mdoc {
export interface Ufdoc {
_id: ObjectID,
secret: string,
md5: string,
md5?: string,
owner: number,
size: number,
size?: number,
filename: string,
}
// Blacklist
@@ -357,68 +358,75 @@ export interface JudgeResultBody {
export type PathComponent = [string, string, any?, boolean?];
export interface Model {
blacklist: typeof import('./model/blacklist'),
builtin: typeof import('./model/builtin'),
contest: typeof import('./model/contest'),
discussion: typeof import('./model/discussion'),
document: typeof import('./model/document'),
domain: typeof import('./model/domain'),
file: typeof import('./model/file'),
message: typeof import('./model/message'),
opcount: typeof import('./model/opcount'),
problem: typeof import('./model/problem'),
record: typeof import('./model/record'),
setting: typeof import('./model/setting'),
solution: typeof import('./model/solution'),
system: typeof import('./model/system'),
task: typeof import('./model/task'),
token: typeof import('./model/token'),
training: typeof import('./model/training'),
user: typeof import('./model/user'),
[key: string]: any,
}
export interface Service {
bus: typeof import('./service/bus'),
db: typeof import('./service/db'),
gridfs: GridFSBucket,
monitor: typeof import('./service/monitor'),
server: typeof import('./service/server'),
[key: string]: any,
}
export interface Lib {
download: typeof import('./lib/download').default,
superagent: typeof import('./lib/superagent'),
'hash.hydro': typeof import('./lib/hash.hydro').default,
i18n: typeof import('./lib/i18n').default,
'import.syzoj': typeof import('./lib/import.syzoj').syzoj,
jwt: typeof import('./lib/jwt'),
logger: typeof import('./lib/logger').default,
mail: typeof import('./lib/mail'),
md5: typeof import('./lib/md5').default,
misc: typeof import('./lib/misc'),
nav: typeof import('./lib/nav').default,
paginate: typeof import('./lib/paginate').default,
rank: typeof import('./lib/rank').default,
rating: typeof import('./lib/rating').default,
testdataConfig: typeof import('./lib/testdataConfig'),
sha1: typeof import('./lib/sha1').default,
sysinfo: typeof import('./lib/sysinfo'),
'testdata.convert.ini': typeof import('./lib/testdata.convert.ini').default,
useragent: typeof import('./lib/useragent'),
validator: typeof import('./lib/validator'),
[key: string]: any
}
declare global {
namespace NodeJS {
interface Global {
Hydro: {
model: {
blacklist: typeof import('./model/blacklist'),
builtin: typeof import('./model/builtin'),
contest: typeof import('./model/contest'),
discussion: typeof import('./model/discussion'),
document: typeof import('./model/document'),
domain: typeof import('./model/domain'),
file: typeof import('./model/file'),
message: typeof import('./model/message'),
opcount: typeof import('./model/opcount'),
problem: typeof import('./model/problem'),
record: typeof import('./model/record'),
setting: typeof import('./model/setting'),
solution: typeof import('./model/solution'),
system: typeof import('./model/system'),
task: typeof import('./model/task'),
token: typeof import('./model/token'),
training: typeof import('./model/training'),
user: typeof import('./model/user'),
[key: string]: any,
},
handler: { [key: string]: Function },
script: { [key: string]: Script },
service: {
bus: typeof import('./service/bus'),
db: typeof import('./service/db'),
gridfs: GridFSBucket,
monitor: typeof import('./service/monitor'),
server: typeof import('./service/server'),
[key: string]: any,
},
lib: {
download: typeof import('./lib/download').default,
'hash.hydro': typeof import('./lib/hash.hydro').default,
i18n: typeof import('./lib/i18n').default,
'import.syzoj': typeof import('./lib/import.syzoj').syzoj,
jwt: typeof import('./lib/jwt'),
logger: typeof import('./lib/logger').default,
mail: typeof import('./lib/mail'),
md5: typeof import('./lib/md5').default,
misc: typeof import('./lib/misc'),
nav: typeof import('./lib/nav').default,
paginate: typeof import('./lib/paginate').default,
rank: typeof import('./lib/rank').default,
rating: typeof import('./lib/rating').default,
testdataConfig: typeof import('./lib/testdataConfig'),
sha1: typeof import('./lib/sha1').default,
sysinfo: typeof import('./lib/sysinfo'),
'testdata.convert.ini': typeof import('./lib/testdata.convert.ini').default,
useragent: typeof import('./lib/useragent'),
validator: typeof import('./lib/validator'),
[key: string]: any
},
model: Model,
handler: Dict<Function>,
script: Dict<Script>,
service: Service,
lib: Lib,
stat: any,
ui: {
manifest: Dict<string>,
template: Dict<string>,
nav: any,
template: { [key: string]: string },
},
error: typeof import('./error'),
locales: Dict<Dict<string>>,
+52
View File
@@ -0,0 +1,52 @@
import superagent from 'superagent';
import proxyAgent from 'proxy-agent';
namespace Proxy {
function getUrl() {
return this._url;
}
function setUrl(v: string) {
this._url = v;
// eslint-disable-next-line no-use-before-define
proxy.call(this, this._proxyUri);
}
function setupUrl(req: superagent.Request) {
const desc = Object.getOwnPropertyDescriptor(req, 'url');
if (desc.get === getUrl && desc.set === setUrl) return;
// @ts-ignore
req._url = req.url;
desc.get = getUrl;
desc.set = setUrl;
delete desc.value;
delete desc.writable;
Object.defineProperty(req, 'url', desc);
}
function proxy(uri: string) {
if (!uri) return this;
setupUrl(this);
const agent = proxyAgent(uri);
if (agent) this.agent(agent);
this._proxyUri = uri;
return this;
}
export function setup(agent, uri = '') {
const Request = agent.Request;
if (Request) {
Request.prototype.proxy = proxy;
return agent;
}
return proxy.call(agent, uri);
}
}
declare module 'superagent' {
namespace request {
interface Request {
proxy(url: string): this;
}
}
}
Proxy.setup(superagent);
export = superagent;
+4 -3
View File
@@ -1,9 +1,10 @@
import { Collection } from 'mongodb';
import * as db from '../service/db';
import { Bdoc } from '../interface';
const coll = db.collection('blacklist');
const coll: Collection<Bdoc> = db.collection('blacklist');
export async function add(ip: string): Promise<Bdoc> {
export async function add(ip: string) {
/**
* Add a ip into blacklist.
* @param {string} ip
@@ -18,7 +19,7 @@ export async function add(ip: string): Promise<Bdoc> {
return res.value;
}
export function get(ip: string): Promise<Bdoc | null> {
export function get(ip: string) {
/**
* Get a ip, return null if not.
* @param {string} ip
+5 -26
View File
@@ -1,4 +1,4 @@
import { ObjectID } from 'mongodb';
import { ObjectID, Collection } from 'mongodb';
import * as fs from 'fs';
import { Dictionary } from 'lodash';
import { ForbiddenError, NotFoundError } from '../error';
@@ -7,7 +7,7 @@ import gridfs from '../service/gridfs';
import hash from '../lib/hash.hydro';
import { Ufdoc } from '../interface';
const coll = db.collection('file');
const coll: Collection<Ufdoc> = db.collection('file');
const collFile = db.collection('fs.files');
const collChunk = db.collection('fs.chunks');
@@ -25,11 +25,10 @@ export async function add(
const w = gridfs.openUploadStream(filename);
await coll.insertOne({
...meta,
_id: w.id,
_id: w.id as ObjectID,
secret: String.random(32),
owner,
filename,
count: 1,
});
await new Promise((resolve, reject) => {
w.on('error', reject);
@@ -37,7 +36,7 @@ export async function add(
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].length } });
await coll.updateOne({ _id: w.id as ObjectID }, { $set: { md5: c[0].md5, size: c[0].length } });
return w.id as ObjectID;
}
@@ -49,26 +48,6 @@ export function del(_id: ObjectID) {
]);
}
export async function inc(_id: ObjectID): Promise<number> {
const doc = await coll.findOneAndUpdate(
{ _id },
{ $inc: { count: 1 } },
{ returnOriginal: false },
);
return doc.value?.count;
}
export async function dec(_id: ObjectID): Promise<number> {
const file = await coll.findOneAndUpdate(
{ _id },
{ $inc: { count: -1 } },
{ returnOriginal: false },
);
if (!file.value) return 0;
if (!file.value.count) await del(_id);
return file.value.count;
}
export async function getWithSecret(_id: ObjectID, secret: string, reject?: Function) {
const file = await coll.findOne({ _id });
if (!file) throw new NotFoundError(_id);
@@ -144,5 +123,5 @@ function ensureIndexes() {
global.Hydro.postInit.push(ensureIndexes);
global.Hydro.model.file = {
add, get, del, inc, dec, getMeta, getWithSecret, getMulti, getMetaDict, url,
add, get, del, getMeta, getWithSecret, getMulti, getMetaDict, url,
};
+2 -2
View File
@@ -1,4 +1,4 @@
import { ObjectID } from 'mongodb';
import { ObjectID, Collection } from 'mongodb';
import { Dictionary } from 'lodash';
import { STATUS } from './builtin';
import * as task from './task';
@@ -8,7 +8,7 @@ import {
} from '../interface';
import * as db from '../service/db';
const coll = db.collection('record');
const coll: Collection<Rdoc> = db.collection('record');
export interface RdocBase {
_id?: ObjectID,
+3 -2
View File
@@ -1,7 +1,8 @@
import { Collection } from 'mongodb';
import { TokenDoc } from '../interface';
import * as db from '../service/db';
const coll = db.collection('token');
const coll: Collection<TokenDoc> = db.collection('token');
export const TYPE_SESSION = 0;
export const TYPE_REGISTRATION = 2;
@@ -11,7 +12,7 @@ export const TYPE_LOSTPASS = 5;
function ensureIndexes() {
return Promise.all([
coll.createIndex([{ uid: 1 }, { tokenType: 1 }, { updateAt: -1 }], { sparse: true }),
coll.createIndex({ uid: 1, tokenType: 1, updateAt: -1 }, { sparse: true }),
coll.createIndex('expireAt', { expireAfterSeconds: 0 }),
]);
}
+8 -4
View File
@@ -1,3 +1,4 @@
import { Collection } from 'mongodb';
import * as system from './system';
import * as token from './token';
import * as setting from './setting';
@@ -8,7 +9,7 @@ import pwhash from '../lib/hash.hydro';
import * as db from '../service/db';
import { User as _User, Udoc, Udict } from '../interface';
const coll = db.collection('user');
const coll: Collection<Udoc> = db.collection('user');
export async function setPassword(uid: number, password: string): Promise<Udoc> {
const salt = String.random();
@@ -203,11 +204,14 @@ export function getMulti(params: any) {
return coll.find(params);
}
export async function getPrefixList(prefix: string, limit = 50) {
export async function getPrefixList(domainId: string, prefix: string, limit = 50) {
prefix = prefix.toLowerCase();
const $regex = new RegExp(`\\A\\Q${prefix}\\E`, 'gmi');
const udocs = await coll.find({ unameLower: { $regex } }).limit(limit).toArray();
return udocs;
const uids = await coll.find({ unameLower: { $regex } })
.limit(limit).map((doc) => doc._id).toArray();
const users = [];
for (const uid of uids) users.push(getById(domainId, uid));
return await Promise.all(users);
}
export async function setPriv(uid: number, priv: number): Promise<Udoc | null> {
-48
View File
@@ -1,8 +1,6 @@
import fs from 'fs';
import cluster from 'cluster';
import path from 'path';
import * as superagent from 'superagent';
import proxyAgent from 'proxy-agent';
import serialize from 'serialize-javascript';
import Lru from 'lru-cache';
@@ -41,52 +39,6 @@ export function lrucache(target: any, funcName: string, obj: any) {
return obj;
}
namespace Proxy {
function getUrl() {
return this._url;
}
function setUrl(v: string) {
this._url = v;
// eslint-disable-next-line no-use-before-define
proxy.call(this, this._proxyUri);
}
function setupUrl(req: superagent.Request) {
const desc = Object.getOwnPropertyDescriptor(req, 'url');
if (desc.get === getUrl && desc.set === setUrl) return;
// @ts-ignore
req._url = req.url;
desc.get = getUrl;
desc.set = setUrl;
delete desc.value;
delete desc.writable;
Object.defineProperty(req, 'url', desc);
}
function proxy(uri: string) {
if (!uri) return this;
setupUrl(this);
const agent = proxyAgent(uri);
if (agent) this.agent(agent);
this._proxyUri = uri;
return this;
}
export function setup(agent, uri = '') {
const Request = agent.Request;
if (Request) {
Request.prototype.proxy = proxy;
return agent;
}
return proxy.call(agent, uri);
}
}
declare module 'superagent' {
interface SuperAgentRequest {
proxy(url: string): this;
}
}
Proxy.setup(superagent);
if (!cluster.worker) {
// @ts-ignore
cluster.worker = { id: 0 };