160 lines
6.5 KiB
TypeScript
160 lines
6.5 KiB
TypeScript
import Controller from "wms-core/Controller";
|
|
import {REQUIRE_AUTH_MIDDLEWARE, REQUIRE_REQUEST_AUTH_MIDDLEWARE} from "wms-core/auth/AuthComponent";
|
|
import {NextFunction, Request, Response} from "express";
|
|
import {BadRequestError, ForbiddenHttpError, ServerError} from "wms-core/HttpError";
|
|
import FileModel from "../models/FileModel";
|
|
import config from "config";
|
|
import * as fs from "fs";
|
|
import AuthToken from "../models/AuthToken";
|
|
import {IncomingForm} from "formidable";
|
|
import {FILE_UPLOAD_MIDDLEWARE} from "wms-core/components/ExpressAppComponent";
|
|
import Logger from "wms-core/Logger";
|
|
import generateSlug from "../SlugGenerator";
|
|
|
|
|
|
export default class FileController extends Controller {
|
|
routes(): void {
|
|
this.get('/files/upload', this.getFileUploader, 'file-upload', REQUIRE_AUTH_MIDDLEWARE);
|
|
this.get('/files/upload/script', this.downloadLinuxScript, 'file-linux-script');
|
|
this.post('/files/post', this.postFileFrontend, 'post-file-frontend', REQUIRE_AUTH_MIDDLEWARE, FILE_UPLOAD_FORM_MIDDLEWARE);
|
|
this.get('/files/:page([0-9]+)?', this.getFileManager, 'file-manager', REQUIRE_AUTH_MIDDLEWARE);
|
|
this.post('/files/delete/:slug', this.deleteFile, 'delete-file-frontend', REQUIRE_AUTH_MIDDLEWARE);
|
|
|
|
this.post('/', this.postFile, 'post-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE, FILE_UPLOAD_FORM_MIDDLEWARE);
|
|
this.delete('/:slug', this.deleteFile, 'delete-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE);
|
|
this.get('/:slug', this.downloadFile, 'get-file');
|
|
this.put('/:slug', this.putFile, 'put-file', REQUIRE_REQUEST_AUTH_MIDDLEWARE, FILE_UPLOAD_FORM_MIDDLEWARE);
|
|
}
|
|
|
|
protected async getFileUploader(req: Request, res: Response): Promise<void> {
|
|
res.render('file-upload', {
|
|
max_upload_size: config.get<string>('max_upload_size'),
|
|
auth_tokens: await AuthToken.select().where('user_id', req.models.user!.id!),
|
|
});
|
|
}
|
|
|
|
protected async downloadLinuxScript(req: Request, res: Response): Promise<void> {
|
|
res.download('assets/files/upload_file.sh', 'upload_file.sh');
|
|
}
|
|
|
|
protected async getFileManager(req: Request, res: Response): Promise<void> {
|
|
res.render('file-manager', {
|
|
files: await FileModel.paginateForUser(req, 100, req.models.user!.id!),
|
|
});
|
|
}
|
|
|
|
protected async postFileFrontend(req: Request, res: Response): Promise<void> {
|
|
req.body.type = 'file';
|
|
await this.handleFileUpload(req.body.autogen_url === undefined && req.body.slug ? req.body.slug : await generateSlug(10), req, res);
|
|
}
|
|
|
|
protected async downloadFile(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
const file = await FileModel.getBySlug(req.params.slug);
|
|
if (!file) return next();
|
|
if (file.shouldBeDeleted()) {
|
|
await fs.unlinkSync(file.storage_path);
|
|
await file.delete();
|
|
Logger.info('Deleted', file.storage_path, `(${file.real_name})`);
|
|
return next();
|
|
}
|
|
|
|
switch (file.storage_type) {
|
|
case 'local':
|
|
res.download(file.storage_path, file.real_name);
|
|
break;
|
|
default:
|
|
throw new ServerError(`This file cannot be served. Download protocol for ${file.storage_type} storage type not implemented.`);
|
|
}
|
|
}
|
|
|
|
protected async postFile(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
if (req.body.type !== 'file') return next();
|
|
|
|
await this.handleFileUpload(req.body.slug || await generateSlug(10), req, res);
|
|
}
|
|
|
|
protected async putFile(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
if (req.body.type !== 'file') return next();
|
|
const slug = req.params.slug;
|
|
if (!slug) throw new BadRequestError('Cannot put without a slug.', 'Either provide a slug or use POST method instead.', req.url);
|
|
|
|
await this.handleFileUpload(slug, req, res);
|
|
}
|
|
|
|
protected async handleFileUpload(slug: string, req: Request, res: Response): Promise<void> {
|
|
// Check for file upload
|
|
if (!req.files || !req.files['upload']) {
|
|
throw new BadRequestError('No file received.', 'You must upload exactly one (1) file.', req.url);
|
|
}
|
|
|
|
let upload = req.files['upload'];
|
|
|
|
// TTL
|
|
let ttl = config.get<number>('default_file_ttl');
|
|
if (req.body.never_expire !== undefined) ttl = 0;
|
|
if (req.body.ttl !== undefined) ttl = parseInt(req.body.ttl);
|
|
else if (req.body.expire_after_days !== undefined) ttl = parseInt(req.body.expire_after_days) * 24 * 3600;
|
|
|
|
const file = new FileModel({
|
|
user_id: req.models.user!.id,
|
|
slug: slug,
|
|
real_name: upload.name,
|
|
storage_type: 'local',
|
|
storage_path: 'storage/uploads/' + slug,
|
|
size: upload.size,
|
|
ttl: ttl,
|
|
});
|
|
|
|
await file.save();
|
|
fs.renameSync(upload.path, file.storage_path);
|
|
|
|
res.format({
|
|
json: () => res.json({
|
|
url: file.getURL(),
|
|
}),
|
|
text: () => res.send(file.getURL()),
|
|
html: () => {
|
|
req.flash('success', 'Upload success!');
|
|
res.redirectBack('/');
|
|
},
|
|
});
|
|
}
|
|
|
|
protected async deleteFile(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
const slug = req.params.slug;
|
|
if (!slug) throw new BadRequestError('Cannot delete nothing.', 'Please provide a slug.', req.url);
|
|
|
|
const file = await FileModel.getBySlug(req.params.slug);
|
|
if (!file) return next();
|
|
if (!file.canDelete(req.models.user!.id!)) throw new ForbiddenHttpError('file', req.url);
|
|
|
|
switch (file.storage_type) {
|
|
case 'local':
|
|
await file.delete();
|
|
fs.unlinkSync(file.storage_path);
|
|
break;
|
|
default:
|
|
throw new ServerError(`This file cannot be deleted. Deletion protocol for ${file.storage_type} storage type not implemented.`);
|
|
}
|
|
|
|
res.format({
|
|
json: () => res.json({
|
|
status: 'success',
|
|
}),
|
|
text: () => res.send('success'),
|
|
html: () => {
|
|
req.flash('success', 'Successfully deleted file.');
|
|
res.redirectBack('/');
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
const FILE_UPLOAD_FORM_MIDDLEWARE = FILE_UPLOAD_MIDDLEWARE(() => {
|
|
const form = new IncomingForm();
|
|
form.uploadDir = 'storage/tmp';
|
|
form.maxFileSize = config.get<number>('max_upload_size') * 1024 * 1024;
|
|
return form;
|
|
}, 'upload');
|