socket 断线自动重连,文件上传功能

This commit is contained in:
undefined committed 2020-07-14 16:36:03 +08:00
1 parent 95bfd98b43
commit 10956f1fee
18 files changed
+157 -50

No files matched your search

+6 -5
View File
@@ -308,6 +308,7 @@ class HomeMessagesHandler extends Handler {
async postSend(domainId: string, uid: number, content: string) {
const udoc = await user.getById('system', uid);
if (!udoc) throw new UserNotFoundError(uid);
if (udoc.gravatar) udoc.gravatar = misc.gravatar(udoc.gravatar);
const mdoc = await message.send(this.user._id, uid, content);
// TODO(twd2): improve here: projection
if (this.user._id !== uid) {
@@ -330,7 +331,7 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
bus.subscribe([`user_message-${this.user._id}`], this, 'onMessageReceived');
}
async onMessageReceived(e) {
async onMessageReceived(e: any) {
this.send(e.value);
}
@@ -342,17 +343,17 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
class HomeFileHandler extends Handler {
async get() {
const ufdocs = await file.getMulti({ owner: this.user._id }).toArray();
const fdict = await file.getMetaDict(ufdocs.map((ufdoc) => ufdoc._id));
this.response.template = 'home_file.html';
this.response.body = { ufdocs, fdict };
this.response.body = { ufdocs };
}
async postDelete(ufid) {
@param('ufid', Types.ObjectID)
async postDelete(domainId: string, ufid: ObjectID) {
const ufdoc = await file.getMeta(ufid);
if (ufdoc.owner !== this.user._id) this.checkPriv(PRIV.PRIV_DELETE_FILE);
else this.checkPriv(PRIV.PRIV_DELETE_FILE_SELF);
const result = await file.del(ufdoc._id);
if (result) await user.inc(this.user._id, 'usage', ufdoc.length);
if (result) await user.inc(this.user._id, 'usage', -ufdoc.size);
this.back();
}
}
+38
View File
@@ -1,4 +1,5 @@
/* eslint-disable camelcase */
import fs from 'fs';
import { ObjectID } from 'mongodb';
import { PRIV } from '../model/builtin';
import * as system from '../model/system';
@@ -9,6 +10,7 @@ import * as db from '../service/db';
import {
Route, Handler, Types, param,
} from '../service/server';
import { BadRequestError } from '../error';
const coll = db.collection('status');
@@ -23,6 +25,41 @@ class FileDownloadHandler extends Handler {
}
}
class FileUploadHandler extends Handler {
async getQuota() {
let quota = await system.get('user.quota');
if (this.user.hasPriv(PRIV.PRIV_UNLIMITED_QUOTA)) {
quota = 2 ** 63 - 1;
}
return quota;
}
async get() {
this.response.template = 'fs_upload.html';
this.response.body = { fdoc: null, usage: this.user.usage, quota: await this.getQuota() };
}
@param('title', Types.String)
async post(domainId: string, title: string) {
if (!this.request.files.file) throw new BadRequestError();
const quota = await this.getQuota();
const lfdoc = await fs.promises.stat(this.request.files.file.path);
let ufid: ObjectID;
const udoc = await user.inc(this.user._id, 'usage', lfdoc.size);
try {
ufid = await file.add(this.request.files.file.path, title, this.user._id);
} catch (e) {
await user.inc(this.user._id, 'usage', -lfdoc.size);
throw e;
}
const fdoc = await file.getMeta(ufid);
this.response.template = 'fs_upload.html';
this.response.body = {
fdoc, ufid, usage: udoc.usage, quota,
};
}
}
class StatusHandler extends Handler {
async get() {
const stats = await coll.find().sort({ type: 1, updateAt: -1 }).toArray();
@@ -109,6 +146,7 @@ class UiSettingsHandler extends Handler {
export async function apply() {
Route('file_download', '/fs/:fileId/:secret', FileDownloadHandler);
Route('file_download_with_name', '/fs/:fileId/:name/:secret', FileDownloadHandler);
Route('file_upload', '/fs/upload', FileUploadHandler, PRIV.PRIV_CREATE_FILE);
Route('status', '/status', StatusHandler);
Route('status_update', '/status/update', StatusUpdateHandler);
Route('switch_language', '/language/:lang', SwitchLanguageHandler);
+23 -5
View File
@@ -13,20 +13,25 @@ function _timestamp() {
return Math.floor(Number(new Date()) / 1000000);
}
export async function add(streamOrPath: fs.ReadStream | string, filename: string, meta = {}) {
export async function add(
streamOrPath: fs.ReadStream | string, filename: string,
owner = 1, meta = {},
) {
let stream: fs.ReadStream;
if (typeof streamOrPath === 'string') stream = fs.createReadStream(streamOrPath);
else stream = streamOrPath;
const w = gridfs.openUploadStream(filename);
await coll.insertOne({ ...meta, _id: w.id, secret: String.random(32) });
await coll.insertOne({
...meta, _id: w.id, secret: String.random(32), owner, filename,
});
await new Promise((resolve, reject) => {
w.on('error', reject);
w.on('finish', resolve);
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].size } });
return w.id;
await coll.updateOne({ _id: w.id }, { $set: { md5: c[0].md5, size: c[0].length } });
return w.id as ObjectID;
}
export function del(_id: ObjectID) {
@@ -92,13 +97,26 @@ export function getMulti(query: any) {
return coll.find(query);
}
export async function url(_id: ObjectID, name: string | undefined) {
async function _url(_id: ObjectID, name: string | undefined) {
const file = await coll.findOne({ _id });
const secret = hash(file.secret, _timestamp().toString());
if (name) return `/fs/${_id}/${Buffer.from(name).toString('base64')}/${secret}`;
return `/fs/${_id}/${secret}`;
}
function __url(file: any, name: string | undefined) {
const secret = hash(file.secret, _timestamp().toString());
if (name) return `/fs/${file._id}/${Buffer.from(name).toString('base64')}/${secret}`;
return `/fs/${file._id}/${secret}`;
}
export function url(_id: ObjectID, name: string | undefined): Promise<string>;
export function url(file: any, name: string | undefined): string;
export function url(arg0: any, name: any) {
if (arg0 instanceof ObjectID) return _url(arg0, name);
return __url(arg0, name);
}
global.Hydro.model.file = {
add, get, del, inc, dec, getMeta, getWithSecret, getMulti, getMetaDict, url,
};
+3 -1
View File
@@ -99,6 +99,7 @@ AccountSetting(
Setting('setting_customize', 'backgroundImage', null,
'/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image',
'Choose the background image in your profile page.'),
Setting('setting_storage', 'usage', null, 0, 'number', 'Userfile Usage', null, FLAG_DISABLED | FLAG_HIDDEN),
);
DomainSetting(
@@ -118,7 +119,6 @@ SystemSetting(
Setting('setting_smtp', 'smtp.port', null, 465, 'number', 'SMTP Server Port'),
Setting('setting_smtp', 'smtp.from', null, null, 'text', 'Mail From'),
Setting('setting_smtp', 'smtp.secure', null, false, 'boolean', 'SSL'),
Setting('setting_db', 'db.ver', null, 1, 'number', 'Database version', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_server', 'server.worker', null, 1, 'number', 'Server Workers Number'),
Setting('setting_server', 'server.hostname', null, null, 'text', 'Server Hostname'),
Setting('setting_server', 'server.host', null, null, 'text', 'Server Host'),
@@ -154,6 +154,8 @@ SystemSetting(
Setting('setting_constant', 'DISCUSSION_ON_MAIN', null, 20, 'number', 'DISCUSSION_ON_MAIN'),
Setting('setting_constant', 'CONTEST_ON_MAIN', null, 10, 'number', 'CONTEST_ON_MAIN'),
Setting('setting_constant', 'TRAINING_ON_MAIN', null, 10, 'number', 'TRAINING_ON_MAIN'),
Setting('setting_storage', 'db.ver', null, 1, 'number', 'Database version', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_storage', 'user', null, 1, 'number', 'User Counter', null, FLAG_DISABLED | FLAG_HIDDEN),
);
global.Hydro.postInit.push(
+2 -2
View File
@@ -68,14 +68,14 @@ export class User implements Udoc {
for (const key in setting.SETTINGS_BY_KEY) {
if (udoc[key]) this[key] = udoc[key];
else if (setting.SETTINGS_BY_KEY[key].value) {
else if (setting.SETTINGS_BY_KEY[key].value !== null) {
this[key] = setting.SETTINGS_BY_KEY[key].value;
}
}
for (const key in setting.DOMAIN_USER_SETTINGS_BY_KEY) {
if (dudoc[key]) this[key] = dudoc[key];
else if (setting.DOMAIN_USER_SETTINGS_BY_KEY[key].value) {
else if (setting.DOMAIN_USER_SETTINGS_BY_KEY[key].value !== null) {
this[key] = setting.DOMAIN_USER_SETTINGS_BY_KEY[key].value;
}
}