mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
fix response pipe, remove multipart
This commit is contained in:
1 parent
65a50275b1
commit
af9a8fb206
13 files changed
+70
-46
No files matched your search
+1
-1
@@ -27,7 +27,7 @@
|
|||||||
"eslint-import-resolver-typescript": "^2.2.1",
|
"eslint-import-resolver-typescript": "^2.2.1",
|
||||||
"eslint-plugin-import": "^2.22.0",
|
"eslint-plugin-import": "^2.22.0",
|
||||||
"globby": "^11.0.1",
|
"globby": "^11.0.1",
|
||||||
"jest": "^26.4.0",
|
"jest": "^26.4.1",
|
||||||
"latest-version": "^5.1.0",
|
"latest-version": "^5.1.0",
|
||||||
"ora": "^5.0.0",
|
"ora": "^5.0.0",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ Content: 内容
|
|||||||
Contest scoreboard is not visible.: 当前比赛成绩表隐藏,暂不可显示。
|
Contest scoreboard is not visible.: 当前比赛成绩表隐藏,暂不可显示。
|
||||||
Contest Scoreboard: 比赛成绩表
|
Contest Scoreboard: 比赛成绩表
|
||||||
contest_create: 创建比赛
|
contest_create: 创建比赛
|
||||||
contest_detail: 比赛详情
|
|
||||||
contest_detail_problem_submit: 递交比赛题目
|
contest_detail_problem_submit: 递交比赛题目
|
||||||
|
contest_detail: 比赛详情
|
||||||
contest_edit: 编辑比赛
|
contest_edit: 编辑比赛
|
||||||
contest_main: 比赛
|
contest_main: 比赛
|
||||||
contest_scoreboard: 比赛成绩表
|
contest_scoreboard: 比赛成绩表
|
||||||
@@ -238,6 +238,7 @@ Domain ID: 域 ID
|
|||||||
domain_dashboard: 管理域
|
domain_dashboard: 管理域
|
||||||
domain_discussion: 讨论节点
|
domain_discussion: 讨论节点
|
||||||
domain_edit: 编辑域资料
|
domain_edit: 编辑域资料
|
||||||
|
domain_file: 我的文件
|
||||||
domain_join_applications: 加域申请
|
domain_join_applications: 加域申请
|
||||||
domain_join: 加入域
|
domain_join: 加入域
|
||||||
domain_main: 首页
|
domain_main: 首页
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.12.19",
|
"version": "2.12.20",
|
||||||
"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",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"moment-timezone": "^0.5.31",
|
"moment-timezone": "^0.5.31",
|
||||||
"mongodb": "^3.5.9",
|
"mongodb": "^3.6.0",
|
||||||
"nodemailer": "^6.4.10",
|
"nodemailer": "^6.4.10",
|
||||||
"ps-list": "^7.2.0",
|
"ps-list": "^7.2.0",
|
||||||
"serialize-javascript": "^4.0.0",
|
"serialize-javascript": "^4.0.0",
|
||||||
@@ -55,4 +55,4 @@
|
|||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"build:watch": "tsc --watch"
|
"build:watch": "tsc --watch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from '../error';
|
} from '../error';
|
||||||
import * as user from '../model/user';
|
import * as user from '../model/user';
|
||||||
import * as domain from '../model/domain';
|
import * as domain from '../model/domain';
|
||||||
|
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';
|
||||||
import { PERM, PERMS_BY_FAMILY, PRIV } from '../model/builtin';
|
import { PERM, PERMS_BY_FAMILY, PRIV } from '../model/builtin';
|
||||||
import { gravatar } from '../lib/misc';
|
import { gravatar } from '../lib/misc';
|
||||||
@@ -199,6 +200,7 @@ class DomainJoinApplicationsHandler extends ManageHandler {
|
|||||||
if (!this.response.body.joinSettings) {
|
if (!this.response.body.joinSettings) {
|
||||||
delete this.response.body.expirations[domain.JOIN_EXPIRATION_KEEP_CURRENT];
|
delete this.response.body.expirations[domain.JOIN_EXPIRATION_KEEP_CURRENT];
|
||||||
}
|
}
|
||||||
|
this.response.body.url_prefix = await system.get('server.url');
|
||||||
this.response.body.path = [
|
this.response.body.path = [
|
||||||
['Hydro', 'homepage'],
|
['Hydro', 'homepage'],
|
||||||
['domain', null],
|
['domain', null],
|
||||||
@@ -207,7 +209,7 @@ class DomainJoinApplicationsHandler extends ManageHandler {
|
|||||||
this.response.template = 'domain_join_applications.html';
|
this.response.template = 'domain_join_applications.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
@param('method', Types.Range(domain.JOIN_METHOD_RANGE))
|
@param('method', Types.Range([domain.JOIN_METHOD_NONE, domain.JOIN_METHOD_ALL, domain.JOIN_METHOD_CODE]))
|
||||||
@param('role', Types.String, true)
|
@param('role', Types.String, true)
|
||||||
@param('expire', Types.UnsignedInt, true)
|
@param('expire', Types.UnsignedInt, true)
|
||||||
@param('invitationCode', Types.String, true)
|
@param('invitationCode', Types.String, true)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import * as file from '../model/file';
|
|||||||
import * as user from '../model/user';
|
import * as user from '../model/user';
|
||||||
import * as db from '../service/db';
|
import * as db from '../service/db';
|
||||||
import {
|
import {
|
||||||
Route, Handler, Types, param, multipart,
|
Route, Handler, Types, param,
|
||||||
} from '../service/server';
|
} from '../service/server';
|
||||||
import { BadRequestError } from '../error';
|
import { BadRequestError } from '../error';
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ class FileDownloadHandler extends Handler {
|
|||||||
@param('name', Types.String, true)
|
@param('name', Types.String, true)
|
||||||
async get(domainId: string, fileId: ObjectID, secret: string, name: string) {
|
async get(domainId: string, fileId: ObjectID, secret: string, name: string) {
|
||||||
if (name) name = Buffer.from(name, 'base64').toString();
|
if (name) name = Buffer.from(name, 'base64').toString();
|
||||||
this.response.attachment(name || fileId.toHexString());
|
const doc = await file.getWithSecret(fileId, secret);
|
||||||
this.response.body = await file.getWithSecret(fileId, secret);
|
this.response.attachment(name || fileId.toHexString(), doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,6 @@ class FileUploadHandler extends Handler {
|
|||||||
this.response.body = { fdoc: null, usage: this.user.usage, quota: await this.getQuota() };
|
this.response.body = { fdoc: null, usage: this.user.usage, quota: await this.getQuota() };
|
||||||
}
|
}
|
||||||
|
|
||||||
@multipart(256 * 1024)
|
|
||||||
@param('title', Types.String)
|
@param('title', Types.String)
|
||||||
async post(domainId: string, title: string) {
|
async post(domainId: string, title: string) {
|
||||||
if (!this.request.files.file) throw new BadRequestError();
|
if (!this.request.files.file) throw new BadRequestError();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import * as solution from '../model/solution';
|
|||||||
import { PERM, PRIV, CONSTANT } from '../model/builtin';
|
import { PERM, PRIV, CONSTANT } from '../model/builtin';
|
||||||
import * as bus from '../service/bus';
|
import * as bus from '../service/bus';
|
||||||
import {
|
import {
|
||||||
Route, Connection, Handler, ConnectionHandler, Types, param, multipart,
|
Route, Connection, Handler, ConnectionHandler, Types, param,
|
||||||
} from '../service/server';
|
} from '../service/server';
|
||||||
|
|
||||||
const parseCategory = (value: string) => flatten(value.split('+').map((e) => e.split(','))).map((e) => e.trim());
|
const parseCategory = (value: string) => flatten(value.split('+').map((e) => e.split(','))).map((e) => e.trim());
|
||||||
@@ -422,7 +422,6 @@ class ProblemDataUploadHandler extends ProblemManageHandler {
|
|||||||
this.response.template = 'problem_upload.html';
|
this.response.template = 'problem_upload.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
@multipart(256 * 1024)
|
|
||||||
async post({ domainId }) {
|
async post({ domainId }) {
|
||||||
if (!this.request.files.file) throw new ValidationError('file');
|
if (!this.request.files.file) throw new ValidationError('file');
|
||||||
this.pdoc = await problem.setTestdata(
|
this.pdoc = await problem.setTestdata(
|
||||||
|
|||||||
@@ -237,6 +237,7 @@ export interface DomainDoc extends Dictionary<any> {
|
|||||||
gravatar: string,
|
gravatar: string,
|
||||||
bulletin: string,
|
bulletin: string,
|
||||||
pidCounter: number,
|
pidCounter: number,
|
||||||
|
join?: any,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message
|
// Message
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export async function getList(domainIds: string[]): Promise<Dictionary<DomainDoc
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setUserRole(domainId: string, uid: number, role: string) {
|
export function setUserRole(domainId: string, uid: number, role: string) {
|
||||||
return collUser.updateOne({ uid, domainId }, { role }, { upsert: true });
|
return collUser.updateOne({ uid, domainId }, { $set: { role } }, { upsert: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRoles(domainId: string): Promise<any[]>
|
export async function getRoles(domainId: string): Promise<any[]>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export async function getWithSecret(_id: ObjectID, secret: string, reject?: Func
|
|||||||
const stream = gridfs.openDownloadStream(_id);
|
const stream = gridfs.openDownloadStream(_id);
|
||||||
stream.on('error', (err) => {
|
stream.on('error', (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
if (reject) reject();
|
if (reject) reject(err);
|
||||||
});
|
});
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ export async function get(_id: ObjectID, reject?: Function) {
|
|||||||
const stream = gridfs.openDownloadStream(_id);
|
const stream = gridfs.openDownloadStream(_id);
|
||||||
stream.on('error', (err) => {
|
stream.on('error', (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
if (reject) reject();
|
if (reject) reject(err);
|
||||||
});
|
});
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,19 @@ mongourl += `${opts.host}:${opts.port}/${opts.name}`;
|
|||||||
|
|
||||||
// eslint-disable-next-line import/no-mutable-exports
|
// eslint-disable-next-line import/no-mutable-exports
|
||||||
export let db: mongodb.Db = null;
|
export let db: mongodb.Db = null;
|
||||||
|
// eslint-disable-next-line import/no-mutable-exports
|
||||||
|
export let db2: mongodb.Db = null;
|
||||||
|
|
||||||
mongodb.MongoClient.connect(mongourl, { useNewUrlParser: true, useUnifiedTopology: true })
|
mongodb.MongoClient.connect(mongourl, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||||
.then((Client) => {
|
.then((Client) => {
|
||||||
db = Client.db(opts.name);
|
db = Client.db(opts.name);
|
||||||
global.Hydro.service.db.db = db;
|
global.Hydro.service.db.db = db;
|
||||||
bus.publish('system_database_connected', null);
|
mongodb.MongoClient.connect(mongourl, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||||
|
.then((Client1) => {
|
||||||
|
db2 = Client1.db(opts.name);
|
||||||
|
global.Hydro.service.db.db2 = db2;
|
||||||
|
bus.publish('system_database_connected', null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export function collection(c: string) {
|
export function collection(c: string) {
|
||||||
@@ -23,4 +30,4 @@ export function collection(c: string) {
|
|||||||
return db.collection(c);
|
return db.collection(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Hydro.service.db = { collection, db };
|
global.Hydro.service.db = { collection, db, db2 };
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { GridFSBucket } from 'mongodb';
|
import { GridFSBucket } from 'mongodb';
|
||||||
import { db } from './db';
|
import { db2 } from './db';
|
||||||
|
|
||||||
export const fs = new GridFSBucket(db);
|
const fs = new GridFSBucket(db2);
|
||||||
|
|
||||||
global.Hydro.service.gridfs = fs;
|
global.Hydro.service.gridfs = fs;
|
||||||
|
|
||||||
export default fs;
|
export = fs;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable prefer-destructuring */
|
/* eslint-disable prefer-destructuring */
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
@@ -99,7 +100,16 @@ export const Types: Types = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
Range: (range) => [
|
Range: (range) => [
|
||||||
null,
|
(v) => {
|
||||||
|
if (range instanceof Array) {
|
||||||
|
for (const item of range) {
|
||||||
|
if (typeof item === 'number') {
|
||||||
|
if (item === parseInt(v, 10)) return parseInt(v, 10);
|
||||||
|
} else if (item === v) return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
},
|
||||||
(v) => {
|
(v) => {
|
||||||
if (range instanceof Array) {
|
if (range instanceof Array) {
|
||||||
for (const item of range) {
|
for (const item of range) {
|
||||||
@@ -181,22 +191,6 @@ export function param(name: string, ...args: any): MethodDecorator {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function multipart(maxSizeKiB: number): MethodDecorator {
|
|
||||||
return function multipartDecorator(target: any, funcName: string, obj: any) {
|
|
||||||
const originalMethod = obj.value;
|
|
||||||
obj.value = async function checkCsrfToken(...args: any[]) {
|
|
||||||
const body = Body({
|
|
||||||
multipart: true,
|
|
||||||
formidable: {
|
|
||||||
maxFileSize: maxSizeKiB * 1024,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return await body.call(this.ctx, this.ctx, () => originalMethod.call(this, ...args));
|
|
||||||
};
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function requireCsrfToken(target: any, funcName: string, obj: any) {
|
export function requireCsrfToken(target: any, funcName: string, obj: any) {
|
||||||
const originalMethod = obj.value;
|
const originalMethod = obj.value;
|
||||||
obj.value = async function checkCsrfToken(...args: any[]) {
|
obj.value = async function checkCsrfToken(...args: any[]) {
|
||||||
@@ -219,7 +213,12 @@ export async function prepare() {
|
|||||||
maxAge: 365 * 24 * 60 * 60,
|
maxAge: 365 * 24 * 60 * 60,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
app.use(Body());
|
app.use(Body({
|
||||||
|
multipart: true,
|
||||||
|
formidable: {
|
||||||
|
maxFileSize: 256 * 1024 * 1024,
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Handler {
|
export class Handler {
|
||||||
@@ -251,7 +250,7 @@ export class Handler {
|
|||||||
template?: string,
|
template?: string,
|
||||||
redirect?: string,
|
redirect?: string,
|
||||||
disposition?: string,
|
disposition?: string,
|
||||||
attachment: (name: string) => void,
|
attachment: (name: string, stream?: any) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
session: any;
|
session: any;
|
||||||
@@ -288,7 +287,13 @@ export class Handler {
|
|||||||
status: null,
|
status: null,
|
||||||
template: null,
|
template: null,
|
||||||
redirect: null,
|
redirect: null,
|
||||||
attachment: (name) => ctx.attachment(name),
|
attachment: (name, stream) => {
|
||||||
|
ctx.attachment(name);
|
||||||
|
if (stream) {
|
||||||
|
this.response.body = null;
|
||||||
|
ctx.body = stream.pipe(new PassThrough());
|
||||||
|
}
|
||||||
|
},
|
||||||
disposition: null,
|
disposition: null,
|
||||||
};
|
};
|
||||||
this.UIContext = {
|
this.UIContext = {
|
||||||
@@ -344,9 +349,13 @@ export class Handler {
|
|||||||
if (!this.user.hasPriv(...args)) throw new PrivilegeError(...args);
|
if (!this.user.hasPriv(...args)) throw new PrivilegeError(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
url(name: string, kwargs = {}) {
|
url(name: string, kwargs: any = {}) {
|
||||||
let res = '#';
|
let res = '#';
|
||||||
const args: any = { ...kwargs };
|
const args: any = {};
|
||||||
|
for (const key in kwargs) {
|
||||||
|
if (kwargs[key] instanceof ObjectID) args[key] = kwargs[key].toHexString();
|
||||||
|
else args[key] = kwargs[key].toString();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (this.args.domainId !== 'system' || args.domainId) {
|
if (this.args.domainId !== 'system' || args.domainId) {
|
||||||
name += '_with_domainId';
|
name += '_with_domainId';
|
||||||
@@ -422,6 +431,7 @@ export class Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async finish() {
|
async finish() {
|
||||||
|
if (!this.response.body) return;
|
||||||
try {
|
try {
|
||||||
await this.renderBody();
|
await this.renderBody();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -468,6 +478,8 @@ export class Handler {
|
|||||||
|
|
||||||
async putResponse() {
|
async putResponse() {
|
||||||
if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition);
|
if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition);
|
||||||
|
console.log(this.response);
|
||||||
|
if (!this.response.body) return;
|
||||||
if (this.response.redirect && !this.request.json) {
|
if (this.response.redirect && !this.request.json) {
|
||||||
this.ctx.response.type = 'application/octet-stream';
|
this.ctx.response.type = 'application/octet-stream';
|
||||||
this.ctx.response.status = 302;
|
this.ctx.response.status = 302;
|
||||||
@@ -677,9 +689,13 @@ export class ConnectionHandler {
|
|||||||
if (!this.user.hasPriv(...args)) throw new PrivilegeError(...args);
|
if (!this.user.hasPriv(...args)) throw new PrivilegeError(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
url(name: string, kwargs = {}) {
|
url(name: string, kwargs: any = {}) {
|
||||||
let res = '#';
|
let res = '#';
|
||||||
const args: any = { ...kwargs };
|
const args: any = {};
|
||||||
|
for (const key in kwargs) {
|
||||||
|
if (kwargs[key] instanceof ObjectID) args[key] = kwargs[key].toHexString();
|
||||||
|
else args[key] = kwargs[key].toString();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (this.args.domainId !== 'system' || args.domainId) {
|
if (this.args.domainId !== 'system' || args.domainId) {
|
||||||
name += '_with_domainId';
|
name += '_with_domainId';
|
||||||
@@ -803,7 +819,6 @@ global.Hydro.service.server = {
|
|||||||
server,
|
server,
|
||||||
router,
|
router,
|
||||||
param,
|
param,
|
||||||
multipart,
|
|
||||||
requireCsrfToken,
|
requireCsrfToken,
|
||||||
Handler,
|
Handler,
|
||||||
ConnectionHandler,
|
ConnectionHandler,
|
||||||
|
|||||||
@@ -18,4 +18,4 @@
|
|||||||
"js-yaml": "^3.14.0",
|
"js-yaml": "^3.14.0",
|
||||||
"mongodb": "^3.6.0"
|
"mongodb": "^3.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user