You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.2 KiB
TypeScript
105 lines
3.2 KiB
TypeScript
import { ObjectID } from 'mongodb';
|
|
import * as fs from 'fs';
|
|
import { ForbiddenError, NotFoundError } from '../error';
|
|
import * as db from '../service/db';
|
|
import gridfs from '../service/gridfs';
|
|
import hash from '../lib/hash.hydro';
|
|
|
|
const coll = db.collection('file');
|
|
const collFile = db.collection('fs.files');
|
|
const collChunk = db.collection('fs.chunks');
|
|
|
|
function _timestamp() {
|
|
return Math.floor(Number(new Date()) / 1000000);
|
|
}
|
|
|
|
export async function add(streamOrPath: fs.ReadStream | string, filename: string, 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 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;
|
|
}
|
|
|
|
export function del(_id: ObjectID) {
|
|
return Promise.all([
|
|
coll.deleteOne({ _id }),
|
|
collFile.deleteOne({ _id }),
|
|
collChunk.deleteMany({ files_id: _id }),
|
|
]);
|
|
}
|
|
|
|
export async function inc(_id: ObjectID) {
|
|
const doc = await coll.findOneAndUpdate(
|
|
{ _id },
|
|
{ $inc: { count: 1 } },
|
|
{ returnOriginal: false },
|
|
);
|
|
return (doc.value || {}).count;
|
|
}
|
|
|
|
export async function dec(_id: ObjectID) {
|
|
const file = await coll.findOneAndUpdate(
|
|
{ _id },
|
|
{ $inc: { count: -1 } },
|
|
{ returnOriginal: false },
|
|
);
|
|
if (!file.value.count) await del(_id);
|
|
return file.value.count;
|
|
}
|
|
|
|
export async function getWithSecret(_id: ObjectID, secret: string) {
|
|
const file = await coll.findOne({ _id });
|
|
if (!file) throw new NotFoundError(_id);
|
|
const timestamp = _timestamp();
|
|
if (!(hash(file.secret, timestamp.toString()) === secret)) {
|
|
if (!(hash(file.secret, (timestamp - 1).toString()) === secret)) {
|
|
throw new ForbiddenError();
|
|
}
|
|
}
|
|
return gridfs.openDownloadStream(_id);
|
|
}
|
|
|
|
export async function get(_id: ObjectID) {
|
|
const file = await coll.findOne({ _id });
|
|
if (!file) throw new NotFoundError(_id);
|
|
return gridfs.openDownloadStream(_id);
|
|
}
|
|
|
|
export function getMeta(_id: ObjectID) {
|
|
return coll.findOne({ _id });
|
|
}
|
|
|
|
export async function getMetaDict(ufids: ObjectID[]) {
|
|
const r = {};
|
|
ufids = Array.from(new Set(ufids));
|
|
const ufdocs = await coll.find({ _id: { $in: ufids } }).toArray();
|
|
for (const ufdoc of ufdocs) {
|
|
r[ufdoc._id.toHexString()] = ufdoc;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
export function getMulti(query: any) {
|
|
return coll.find(query);
|
|
}
|
|
|
|
export 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}`;
|
|
}
|
|
|
|
global.Hydro.model.file = {
|
|
add, get, del, inc, dec, getMeta, getWithSecret, getMulti, getMetaDict, url,
|
|
};
|