Merge file/url creating form and manager

This commit is contained in:
Alice Gaudon 2021-03-29 12:55:05 +02:00
parent a2a9d20994
commit a5729af3c1
8 changed files with 98 additions and 152 deletions

View File

@ -18,7 +18,7 @@ export default class AuthTokenController extends Controller {
}); });
await authToken.save(); await authToken.save();
req.flash('success', 'Successfully created auth token.'); req.flash('success', 'Successfully created auth token.');
res.redirect(req.getPreviousUrl() || Controller.route('file-upload')); res.redirect(req.getPreviousUrl() || Controller.route('file-uploader'));
} }
protected async postRevokeAuthToken(req: Request, res: Response): Promise<void> { protected async postRevokeAuthToken(req: Request, res: Response): Promise<void> {
@ -34,6 +34,6 @@ export default class AuthTokenController extends Controller {
await authToken.delete(); await authToken.delete();
req.flash('success', 'Successfully deleted auth token.'); req.flash('success', 'Successfully deleted auth token.');
res.redirect(req.getPreviousUrl() || Controller.route('file-upload')); res.redirect(req.getPreviousUrl() || Controller.route('file-uploader'));
} }
} }

View File

@ -5,8 +5,7 @@ import {BadRequestError, ForbiddenHttpError, ServerError} from "swaf/HttpError";
import FileModel from "../models/FileModel"; import FileModel from "../models/FileModel";
import config from "config"; import config from "config";
import * as fs from "fs"; import * as fs from "fs";
import AuthToken from "../models/AuthToken"; import Formidable from "formidable";
import {IncomingForm} from "formidable";
import generateSlug from "../SlugGenerator"; import generateSlug from "../SlugGenerator";
import {logger} from "swaf/Logger"; import {logger} from "swaf/Logger";
import FileUploadMiddleware from "swaf/FileUploadMiddleware"; import FileUploadMiddleware from "swaf/FileUploadMiddleware";
@ -14,21 +13,20 @@ import FileUploadMiddleware from "swaf/FileUploadMiddleware";
export default class FileController extends Controller { export default class FileController extends Controller {
public routes(): void { public routes(): void {
this.get('/files/upload', this.getFileUploader, 'file-upload', RequireAuthMiddleware); this.get('/files/:page([0-9]+)?', this.getFileUploader, 'file-uploader', RequireAuthMiddleware);
this.get('/files/upload/script', this.downloadLinuxScript, 'file-linux-script'); this.get('/files/upload-script', this.downloadLinuxScript, 'file-linux-script');
this.post('/files/post', this.postFileFrontend, 'post-file-frontend', RequireAuthMiddleware, FileUploadFormMiddleware); this.post('/files/upload', 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); this.post('/files/delete/:slug', FileController.deleteFileRoute, 'delete-file-frontend', RequireAuthMiddleware);
} }
protected async getFileUploader(req: Request, res: Response): Promise<void> { protected async getFileUploader(req: Request, res: Response): Promise<void> {
const allowedDomains = config.get<string[]>('allowed_url_domains'); const allowedDomains = config.get<string[]>('allowed_url_domains');
const user = req.as(RequireAuthMiddleware).getUser(); const user = req.as(RequireAuthMiddleware).getUser();
res.render('file-upload', { res.render('file-uploader', {
max_upload_size: config.get<string>('max_upload_size'), max_upload_size: config.get<string>('max_upload_size'),
auth_tokens: await AuthToken.select().where('user_id', user.id).get(),
allowed_domains: allowedDomains, allowed_domains: allowedDomains,
default_domain: allowedDomains[config.get<number>('default_url_domain_for_files')], default_domain: allowedDomains[config.get<number>('default_url_domain_for_files')],
files: await FileModel.paginateForUser(req, 100, user.getOrFail('id')),
}); });
} }
@ -36,13 +34,6 @@ export default class FileController extends Controller {
res.download('assets/files/upload_file.sh', 'upload_file.sh'); 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> { protected async postFileFrontend(req: Request, res: Response): Promise<void> {
req.body.type = 'file'; req.body.type = 'file';
await FileController.handleFileUpload( await FileController.handleFileUpload(
@ -100,7 +91,7 @@ export default class FileController extends Controller {
html: () => { html: () => {
req.flash('success', 'Upload success!'); req.flash('success', 'Upload success!');
req.flash('url', file.getURL(domain)); req.flash('url', file.getURL(domain));
res.redirect(Controller.route('file-manager')); res.redirect(Controller.route('file-uploader'));
}, },
}); });
} }
@ -135,7 +126,7 @@ export default class FileController extends Controller {
text: () => res.send('success'), text: () => res.send('success'),
html: () => { html: () => {
req.flash('success', 'Successfully deleted file.'); req.flash('success', 'Successfully deleted file.');
res.redirect(Controller.route('file-manager')); res.redirect(Controller.route('file-uploader'));
}, },
}); });
} }
@ -152,11 +143,11 @@ export class FileUploadFormMiddleware extends FileUploadMiddleware {
return 'upload'; return 'upload';
} }
protected makeForm(): IncomingForm { protected makeForm(): Formidable {
const form = new IncomingForm(); return new Formidable({
form.uploadDir = 'storage/tmp'; uploadDir: 'storage/tmp',
form.maxFileSize = config.get<number>('max_upload_size') * 1024 * 1024; maxFileSize: config.get<number>('max_upload_size') * 1024 * 1024,
return form; });
} }
} }

View File

@ -4,23 +4,21 @@ import URLRedirect from "../models/URLRedirect";
import {RequireAuthMiddleware, RequireRequestAuthMiddleware} from "swaf/auth/AuthComponent"; import {RequireAuthMiddleware, RequireRequestAuthMiddleware} from "swaf/auth/AuthComponent";
import generateSlug from "../SlugGenerator"; import generateSlug from "../SlugGenerator";
import config from "config"; import config from "config";
import AuthToken from "../models/AuthToken";
export default class URLRedirectController extends Controller { export default class URLRedirectController extends Controller {
public routes(): void { public routes(): void {
this.get('/url/shrink', this.getUrlShrinker, 'url-shrinker', RequireAuthMiddleware); this.get('/url/:page([0-9]+)?', this.getUrlShrinker, 'url-shrinker', RequireAuthMiddleware);
this.get('/url/shrink/script', this.downloadLinuxScript, 'url-linux-script'); this.get('/url/shrink-script', this.downloadLinuxScript, 'url-linux-script');
this.post('/url/shrink', this.addUrlFrontend, 'shrink-url', RequireAuthMiddleware); this.post('/url/shrink', this.addUrlFrontend, 'shrink-url', RequireAuthMiddleware);
this.get('/urls/:page([0-9]+)?', this.getUrlRedirectManager, 'url-manager', RequireAuthMiddleware);
} }
protected async getUrlShrinker(req: Request, res: Response): Promise<void> { protected async getUrlShrinker(req: Request, res: Response): Promise<void> {
const user = req.as(RequireAuthMiddleware).getUser(); const user = req.as(RequireAuthMiddleware).getUser();
const allowedDomains = config.get<string[]>('allowed_url_domains'); const allowedDomains = config.get<string[]>('allowed_url_domains');
res.render('url-shrinker', { res.render('url-shrinker', {
auth_tokens: await AuthToken.select().where('user_id', user.id).get(),
allowed_domains: allowedDomains, allowed_domains: allowedDomains,
default_domain: allowedDomains[config.get<number>('default_url_domain_for_urls')], default_domain: allowedDomains[config.get<number>('default_url_domain_for_urls')],
urls: await URLRedirect.paginateForUser(req, 2, user.getOrFail('id')),
}); });
} }
@ -28,13 +26,6 @@ export default class URLRedirectController extends Controller {
res.download('assets/files/shrink_url.sh', 'shrink_url.sh'); res.download('assets/files/shrink_url.sh', 'shrink_url.sh');
} }
protected async getUrlRedirectManager(req: Request, res: Response): Promise<void> {
const user = req.as(RequireAuthMiddleware).getUser();
res.render('url-manager', {
urls: await URLRedirect.paginateForUser(req, 100, user.getOrFail('id')),
});
}
protected async addUrlFrontend(req: Request, res: Response, next: NextFunction): Promise<void> { protected async addUrlFrontend(req: Request, res: Response, next: NextFunction): Promise<void> {
req.body.type = 'url'; req.body.type = 'url';
await URLRedirectController.addURL( await URLRedirectController.addURL(
@ -75,7 +66,7 @@ export default class URLRedirectController extends Controller {
html: () => { html: () => {
req.flash('success', 'URL shrunk successfully!'); req.flash('success', 'URL shrunk successfully!');
req.flash('url', urlRedirect.getURL(domain)); req.flash('url', urlRedirect.getURL(domain));
res.redirect(Controller.route('url-manager')); res.redirect(Controller.route('url-shrinker'));
}, },
}); });
} }

View File

@ -1,58 +0,0 @@
{% extends 'layouts/base.njk' %}
{% set title = app.name + ' - File manager' %}
{% block scripts %}
<script src="/js/fm.js"></script>
{% endblock %}
{% block body %}
<h1>File manager</h1>
<p>You're their manager, please be nice with them.</p>
<div class="container">
<section class="panel">
<h2><i data-feather="folder"></i> File list</h2>
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th class="table-col-grow">URL</th>
<th>Name</th>
<th>Size</th>
<th>Expires at</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for file in files %}
<tr>
<td>{{ file.id }}</td>
<td>
<div class="copyable-text">
<a class="content" href="{{ file.getURL() }}" target="_blank">{{ file.getURL() }}</a>
<button class="copy-button"><i data-feather="copy"></i></button>
</div>
</td>
<td><pre>{{ file.real_name }}</pre></td>
<td>{{ (file.size / (1024 * 1024)).toFixed(2) }}MB</td>
{% set expires_at = file.getExpirationDate() %}
<td>{% if expires_at %}{{ expires_at.toISOString() }}{% else %}Never{% endif %}</td>
<td class="actions">
{% if file.shouldBeDeleted() %}
Pending deletion
{% else %}
<form action="{{ route('delete-file-frontend', file.slug) }}" method="post">
{{ macros.csrf(getCsrfToken) }}
<button class="button danger"><i data-feather="trash"></i> <span class="tip">Delete</span></button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</div>
{% endblock %}

View File

@ -1,6 +1,6 @@
{% extends 'layouts/base.njk' %} {% extends 'layouts/base.njk' %}
{% set title = app.name + ' - File upload' %} {% set title = app.name + ' - Upload file' %}
{% block scripts %} {% block scripts %}
<script src="/js/fm.js"></script> <script src="/js/fm.js"></script>
@ -50,7 +50,50 @@
</div> </div>
{% endif %} {% endif %}
</section> </section>
</div>
{% include 'desktop-utility.njk' %}
<section class="panel">
<h2><i data-feather="folder"></i> File list</h2>
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th class="table-col-grow">URL</th>
<th>Name</th>
<th>Size</th>
<th>Expires at</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for file in files %}
<tr>
<td>{{ file.id }}</td>
<td>
<div class="copyable-text">
<a class="content" href="{{ file.getURL() }}" target="_blank">{{ file.getURL() }}</a>
<button class="copy-button"><i data-feather="copy"></i></button>
</div>
</td>
<td><pre>{{ file.real_name }}</pre></td>
<td>{{ (file.size / (1024 * 1024)).toFixed(2) }}MB</td>
{% set expires_at = file.getExpirationDate() %}
<td>{% if expires_at %}{{ expires_at.toISOString() }}{% else %}Never{% endif %}</td>
<td class="actions">
{% if file.shouldBeDeleted() %}
Pending deletion
{% else %}
<form action="{{ route('delete-file-frontend', file.slug) }}" method="post">
{{ macros.csrf(getCsrfToken) }}
<button class="button danger"><i data-feather="trash"></i> <span class="tip">Delete</span></button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</div>
{% endblock %} {% endblock %}

View File

@ -18,16 +18,8 @@
<ul id="main-menu"> <ul id="main-menu">
<li><a href="{{ route('about') }}"><i data-feather="info"></i> <span class="tip">About</span></a></li> <li><a href="{{ route('about') }}"><i data-feather="info"></i> <span class="tip">About</span></a></li>
{% if user %} {% if user %}
<li><a href="{{ route('file-manager') }}"><i data-feather="folder"></i> <span class="tip">File manager</span></a> <li><a href="{{ route('file-uploader') }}"><i data-feather="upload"></i> <span class="tip">Upload file</span></a></li>
<ul class="dropdown">
<li><a href="{{ route('file-upload') }}"><i data-feather="upload"></i> <span class="tip">Upload file</span></a></li>
</ul>
</li>
<li><a href="{{ route('url-manager') }}"><i data-feather="link"></i> <span class="tip">URL manager</span></a>
<ul class="dropdown">
<li><a href="{{ route('url-shrinker') }}"><i data-feather="crosshair"></i> <span class="tip">Shrink URL</span></a></li> <li><a href="{{ route('url-shrinker') }}"><i data-feather="crosshair"></i> <span class="tip">Shrink URL</span></a></li>
</ul>
</li>
{% if user.is_admin %} {% if user.is_admin %}
<li><a href="{{ route('backend') }}"><i data-feather="settings"></i> <span class="tip">Backend</span></a></li> <li><a href="{{ route('backend') }}"><i data-feather="settings"></i> <span class="tip">Backend</span></a></li>
{% endif %} {% endif %}

View File

@ -1,40 +0,0 @@
{% extends 'layouts/base.njk' %}
{% set title = app.name + ' - URL manager' %}
{% block scripts %}{% endblock %}
{% block body %}
<h1>URL manager</h1>
<p>These are permanent.</p>
<div class="container">
<section class="panel">
<h2><i data-feather="link"></i> URL list</h2>
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th class="table-col-grow">URL</th>
<th>Target</th>
</tr>
</thead>
<tbody>
{% for url in urls %}
<tr>
<td>{{ url.id }}</td>
<td>
<div class="copyable-text">
<a class="content" href="{{ url.getURL() }}" target="_blank">{{ url.getURL() }}</a>
<button class="copy-button"><i data-feather="copy"></i></button>
</div>
</td>
<td><pre>{{ url.target_url }}</pre></td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</div>
{% endblock %}

View File

@ -1,6 +1,6 @@
{% extends 'layouts/base.njk' %} {% extends 'layouts/base.njk' %}
{% set title = app.name + ' - URL shrinker' %} {% set title = app.name + ' - Shrink URL' %}
{% block scripts %} {% block scripts %}
<script src="/js/url-shrinker.js"></script> <script src="/js/url-shrinker.js"></script>
@ -8,7 +8,7 @@
{% block body %} {% block body %}
<h1>Shrink URLs</h1> <h1>Shrink URLs</h1>
<p>(no phishing allowed)</p> <p>For security reasons, shrinked URLs cannot be deleted.</p>
<div class="container"> <div class="container">
<section class="panel"> <section class="panel">
@ -34,7 +34,34 @@
</div> </div>
{% endif %} {% endif %}
</section> </section>
</div>
{% include 'desktop-utility.njk' %}
<section class="panel">
<h2><i data-feather="link"></i> URL list</h2>
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th class="table-col-grow">URL</th>
<th>Target</th>
</tr>
</thead>
<tbody>
{% for url in urls %}
<tr>
<td>{{ url.id }}</td>
<td>
<div class="copyable-text">
<a class="content" href="{{ url.getURL() }}" target="_blank">{{ url.getURL() }}</a>
<button class="copy-button"><i data-feather="copy"></i></button>
</div>
</td>
<td><pre>{{ url.target_url }}</pre></td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</div>
{% endblock %} {% endblock %}