fix response pipe, remove multipart

This commit is contained in:
undefined committed 2020-08-20 17:29:46 +08:00
1 parent 65a50275b1
commit af9a8fb206
13 files changed
+70 -46

No files matched your search

+1 -1
View File
@@ -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",
+2 -1
View File
@@ -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: 首页
+3 -3
View File
@@ -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"
} }
} }
+3 -1
View File
@@ -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)
+3 -4
View File
@@ -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();
+1 -2
View File
@@ -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(
+1
View File
@@ -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
+1 -1
View File
@@ -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[]>
+2 -2
View File
@@ -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;
} }
+9 -2
View File
@@ -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 };
+3 -3
View File
@@ -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;
+40 -25
View File
@@ -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,
+1 -1
View File
@@ -18,4 +18,4 @@
"js-yaml": "^3.14.0", "js-yaml": "^3.14.0",
"mongodb": "^3.6.0" "mongodb": "^3.6.0"
} }
} }