ily.li/src/controllers/FileController.ts

150 lines
5.8 KiB
TypeScript

import Controller from "swaf/Controller";
import {RequireAuthMiddleware} from "swaf/auth/AuthComponent";
import {NextFunction, Request, Response} from "express";
import {BadRequestError, ForbiddenHttpError, ServerError} from "swaf/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 generateSlug from "../SlugGenerator";
import {log} from "swaf/Logger";
import FileUploadMiddleware from "swaf/FileUploadMiddleware";
export default class FileController extends Controller {
public routes(): void {
this.get('/files/upload', this.getFileUploader, 'file-upload', RequireAuthMiddleware);
this.get('/files/upload/script', this.downloadLinuxScript, 'file-linux-script');
this.post('/files/post', this.postFileFrontend, 'post-file-frontend', RequireAuthMiddleware, FileUploadFormMiddleware);
this.get('/files/:page([0-9]+)?', this.getFileManager, 'file-manager', RequireAuthMiddleware);
this.post('/files/delete/:slug', FileController.deleteFileRoute, 'delete-file-frontend', RequireAuthMiddleware);
}
protected async getFileUploader(req: Request, res: Response): Promise<void> {
const allowedDomains = config.get<string[]>('allowed_url_domains');
const user = req.as(RequireAuthMiddleware).getUser();
res.render('file-upload', {
max_upload_size: config.get<string>('max_upload_size'),
auth_tokens: await AuthToken.select().where('user_id', user.id).get(),
allowed_domains: allowedDomains,
default_domain: allowedDomains[config.get<number>('default_url_domain_for_files')],
});
}
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> {
const user = req.as(RequireAuthMiddleware).getUser();
res.render('file-manager', {
files: await FileModel.paginateForUser(req, 100, user.getOrFail('id')),
});
}
protected async postFileFrontend(req: Request, res: Response): Promise<void> {
req.body.type = 'file';
await FileController.handleFileUpload(
req.body.autogen_url === undefined && req.body.slug ?
req.body.slug :
await generateSlug(10),
req,
res,
);
}
public static async handleFileUpload(slug: string, req: Request, res: Response): Promise<void> {
// Check for file upload
if (Object.keys(req.files).indexOf('upload') < 0) {
throw new BadRequestError('No file received.', 'You must upload exactly one (1) file.', req.url);
}
const 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 user = req.as(RequireAuthMiddleware).getUser();
const file = FileModel.create({
user_id: 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.getOrFail('storage_path'));
const domain = req.body.url_domain || config.get<string[]>('allowed_url_domains')[config.get<number>('default_url_domain_for_files')];
res.format({
json: () => res.json({
url: file.getURL(domain),
}),
text: () => res.send(file.getURL(domain)),
html: () => {
req.flash('success', 'Upload success!');
req.flash('url', file.getURL(domain));
res.redirectBack('/');
},
});
}
public static async deleteFileRoute(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();
const user = req.as(RequireAuthMiddleware).getUser();
if (!file.canDelete(user.getOrFail('id'))) throw new ForbiddenHttpError('file', req.url);
switch (file.storage_type) {
case 'local':
await FileController.deleteFile(file);
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('/');
},
});
}
public static async deleteFile(file: FileModel): Promise<void> {
fs.unlinkSync(file.getOrFail('storage_path'));
await file.delete();
log.info('Deleted', file.storage_path, `(${file.real_name})`);
}
}
export class FileUploadFormMiddleware extends FileUploadMiddleware {
protected getDefaultField(): string {
return 'upload';
}
protected makeForm(): IncomingForm {
const form = new IncomingForm();
form.uploadDir = 'storage/tmp';
form.maxFileSize = config.get<number>('max_upload_size') * 1024 * 1024;
return form;
}
}