From af9a8fb206d7d95bc34ccd86c92471a562b10699 Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 20 Aug 2020 17:29:46 +0800 Subject: [PATCH] fix response pipe, remove multipart --- package.json | 2 +- packages/hydrooj/locales/zh_CN.yaml | 3 +- packages/hydrooj/package.json | 6 +-- packages/hydrooj/src/handler/domain.ts | 4 +- packages/hydrooj/src/handler/misc.ts | 7 ++- packages/hydrooj/src/handler/problem.ts | 3 +- packages/hydrooj/src/interface.ts | 1 + packages/hydrooj/src/model/domain.ts | 2 +- packages/hydrooj/src/model/file.ts | 4 +- packages/hydrooj/src/service/db.ts | 11 ++++- packages/hydrooj/src/service/gridfs.ts | 6 +-- packages/hydrooj/src/service/server.ts | 65 +++++++++++++++---------- packages/migrate-vijos/package.json | 2 +- 13 files changed, 70 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 2d64e5911..c69049dd3 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "eslint-import-resolver-typescript": "^2.2.1", "eslint-plugin-import": "^2.22.0", "globby": "^11.0.1", - "jest": "^26.4.0", + "jest": "^26.4.1", "latest-version": "^5.1.0", "ora": "^5.0.0", "semver": "^7.3.2", diff --git a/packages/hydrooj/locales/zh_CN.yaml b/packages/hydrooj/locales/zh_CN.yaml index bea37663a..3a4460fc2 100644 --- a/packages/hydrooj/locales/zh_CN.yaml +++ b/packages/hydrooj/locales/zh_CN.yaml @@ -138,8 +138,8 @@ Content: 内容 Contest scoreboard is not visible.: 当前比赛成绩表隐藏,暂不可显示。 Contest Scoreboard: 比赛成绩表 contest_create: 创建比赛 -contest_detail: 比赛详情 contest_detail_problem_submit: 递交比赛题目 +contest_detail: 比赛详情 contest_edit: 编辑比赛 contest_main: 比赛 contest_scoreboard: 比赛成绩表 @@ -238,6 +238,7 @@ Domain ID: 域 ID domain_dashboard: 管理域 domain_discussion: 讨论节点 domain_edit: 编辑域资料 +domain_file: 我的文件 domain_join_applications: 加域申请 domain_join: 加入域 domain_main: 首页 diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 99a23378f..442ed3c8d 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.12.19", + "version": "2.12.20", "bin": "bin/hydrooj.js", "main": "dist/loader.js", "typings": "dist/loader.d.ts", @@ -25,7 +25,7 @@ "lodash": "^4.17.20", "lru-cache": "^6.0.0", "moment-timezone": "^0.5.31", - "mongodb": "^3.5.9", + "mongodb": "^3.6.0", "nodemailer": "^6.4.10", "ps-list": "^7.2.0", "serialize-javascript": "^4.0.0", @@ -55,4 +55,4 @@ "build": "tsc", "build:watch": "tsc --watch" } -} +} \ No newline at end of file diff --git a/packages/hydrooj/src/handler/domain.ts b/packages/hydrooj/src/handler/domain.ts index 27ee6fb0b..59602f9ba 100644 --- a/packages/hydrooj/src/handler/domain.ts +++ b/packages/hydrooj/src/handler/domain.ts @@ -6,6 +6,7 @@ import { } from '../error'; import * as user from '../model/user'; import * as domain from '../model/domain'; +import * as system from '../model/system'; import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting'; import { PERM, PERMS_BY_FAMILY, PRIV } from '../model/builtin'; import { gravatar } from '../lib/misc'; @@ -199,6 +200,7 @@ class DomainJoinApplicationsHandler extends ManageHandler { if (!this.response.body.joinSettings) { delete this.response.body.expirations[domain.JOIN_EXPIRATION_KEEP_CURRENT]; } + this.response.body.url_prefix = await system.get('server.url'); this.response.body.path = [ ['Hydro', 'homepage'], ['domain', null], @@ -207,7 +209,7 @@ class DomainJoinApplicationsHandler extends ManageHandler { 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('expire', Types.UnsignedInt, true) @param('invitationCode', Types.String, true) diff --git a/packages/hydrooj/src/handler/misc.ts b/packages/hydrooj/src/handler/misc.ts index 838694b43..3988be73e 100644 --- a/packages/hydrooj/src/handler/misc.ts +++ b/packages/hydrooj/src/handler/misc.ts @@ -7,7 +7,7 @@ import * as file from '../model/file'; import * as user from '../model/user'; import * as db from '../service/db'; import { - Route, Handler, Types, param, multipart, + Route, Handler, Types, param, } from '../service/server'; import { BadRequestError } from '../error'; @@ -19,8 +19,8 @@ class FileDownloadHandler extends Handler { @param('name', Types.String, true) async get(domainId: string, fileId: ObjectID, secret: string, name: string) { if (name) name = Buffer.from(name, 'base64').toString(); - this.response.attachment(name || fileId.toHexString()); - this.response.body = await file.getWithSecret(fileId, secret); + const doc = 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() }; } - @multipart(256 * 1024) @param('title', Types.String) async post(domainId: string, title: string) { if (!this.request.files.file) throw new BadRequestError(); diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 640eb0154..29bfe1899 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -22,7 +22,7 @@ import * as solution from '../model/solution'; import { PERM, PRIV, CONSTANT } from '../model/builtin'; import * as bus from '../service/bus'; import { - Route, Connection, Handler, ConnectionHandler, Types, param, multipart, + Route, Connection, Handler, ConnectionHandler, Types, param, } from '../service/server'; 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'; } - @multipart(256 * 1024) async post({ domainId }) { if (!this.request.files.file) throw new ValidationError('file'); this.pdoc = await problem.setTestdata( diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index 2ec4a289c..b28c58b01 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -237,6 +237,7 @@ export interface DomainDoc extends Dictionary { gravatar: string, bulletin: string, pidCounter: number, + join?: any, } // Message diff --git a/packages/hydrooj/src/model/domain.ts b/packages/hydrooj/src/model/domain.ts index 0066b87dc..2ecef837b 100644 --- a/packages/hydrooj/src/model/domain.ts +++ b/packages/hydrooj/src/model/domain.ts @@ -74,7 +74,7 @@ export async function getList(domainIds: string[]): Promise diff --git a/packages/hydrooj/src/model/file.ts b/packages/hydrooj/src/model/file.ts index 3375e3f09..8f000974a 100644 --- a/packages/hydrooj/src/model/file.ts +++ b/packages/hydrooj/src/model/file.ts @@ -60,7 +60,7 @@ export async function getWithSecret(_id: ObjectID, secret: string, reject?: Func const stream = gridfs.openDownloadStream(_id); stream.on('error', (err) => { console.error(err); - if (reject) reject(); + if (reject) reject(err); }); return stream; } @@ -71,7 +71,7 @@ export async function get(_id: ObjectID, reject?: Function) { const stream = gridfs.openDownloadStream(_id); stream.on('error', (err) => { console.error(err); - if (reject) reject(); + if (reject) reject(err); }); return stream; } diff --git a/packages/hydrooj/src/service/db.ts b/packages/hydrooj/src/service/db.ts index f23f5c8a6..3795c84ef 100644 --- a/packages/hydrooj/src/service/db.ts +++ b/packages/hydrooj/src/service/db.ts @@ -10,12 +10,19 @@ mongourl += `${opts.host}:${opts.port}/${opts.name}`; // eslint-disable-next-line import/no-mutable-exports 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 }) .then((Client) => { db = Client.db(opts.name); 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) { @@ -23,4 +30,4 @@ export function collection(c: string) { return db.collection(c); } -global.Hydro.service.db = { collection, db }; +global.Hydro.service.db = { collection, db, db2 }; diff --git a/packages/hydrooj/src/service/gridfs.ts b/packages/hydrooj/src/service/gridfs.ts index 0491b6a5f..b75bb0001 100644 --- a/packages/hydrooj/src/service/gridfs.ts +++ b/packages/hydrooj/src/service/gridfs.ts @@ -1,8 +1,8 @@ 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; -export default fs; +export = fs; diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index c78962d83..e4edfd113 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -1,5 +1,6 @@ /* eslint-disable prefer-destructuring */ import assert from 'assert'; +import { PassThrough } from 'stream'; import path from 'path'; import os from 'os'; import http from 'http'; @@ -99,7 +100,16 @@ export const Types: Types = { }, ], 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) => { if (range instanceof Array) { 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) { const originalMethod = obj.value; obj.value = async function checkCsrfToken(...args: any[]) { @@ -219,7 +213,12 @@ export async function prepare() { maxAge: 365 * 24 * 60 * 60, })); } - app.use(Body()); + app.use(Body({ + multipart: true, + formidable: { + maxFileSize: 256 * 1024 * 1024, + }, + })); } export class Handler { @@ -251,7 +250,7 @@ export class Handler { template?: string, redirect?: string, disposition?: string, - attachment: (name: string) => void, + attachment: (name: string, stream?: any) => void, }; session: any; @@ -288,7 +287,13 @@ export class Handler { status: null, template: 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, }; this.UIContext = { @@ -344,9 +349,13 @@ export class Handler { if (!this.user.hasPriv(...args)) throw new PrivilegeError(...args); } - url(name: string, kwargs = {}) { + url(name: string, kwargs: any = {}) { 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 { if (this.args.domainId !== 'system' || args.domainId) { name += '_with_domainId'; @@ -422,6 +431,7 @@ export class Handler { } async finish() { + if (!this.response.body) return; try { await this.renderBody(); } catch (error) { @@ -468,6 +478,8 @@ export class Handler { async putResponse() { 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) { this.ctx.response.type = 'application/octet-stream'; this.ctx.response.status = 302; @@ -677,9 +689,13 @@ export class ConnectionHandler { if (!this.user.hasPriv(...args)) throw new PrivilegeError(...args); } - url(name: string, kwargs = {}) { + url(name: string, kwargs: any = {}) { 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 { if (this.args.domainId !== 'system' || args.domainId) { name += '_with_domainId'; @@ -803,7 +819,6 @@ global.Hydro.service.server = { server, router, param, - multipart, requireCsrfToken, Handler, ConnectionHandler, diff --git a/packages/migrate-vijos/package.json b/packages/migrate-vijos/package.json index 03f566165..776305d19 100644 --- a/packages/migrate-vijos/package.json +++ b/packages/migrate-vijos/package.json @@ -18,4 +18,4 @@ "js-yaml": "^3.14.0", "mongodb": "^3.6.0" } -} +} \ No newline at end of file