Added code and upload expiration checks
Added the feature to override email templates Added automatic refresh of disk space indicator
This commit is contained in:
parent
fb746966af
commit
88beaa6f7d
|
@ -1,10 +1,9 @@
|
||||||
node_modules
|
node_modules
|
||||||
data
|
data
|
||||||
custom/*.js
|
custom/*
|
||||||
public/dist/*.js
|
public/dist/*.js
|
||||||
public/dist/*.map
|
public/dist/*.map
|
||||||
public/dist/index.html
|
public/dist/index.html
|
||||||
config.js
|
config.js
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
/custom/images/logo.png
|
|
|
@ -36,21 +36,28 @@ module.exports = {
|
||||||
alphabet: '1234567890abcdef',
|
alphabet: '1234567890abcdef',
|
||||||
length: 8,
|
length: 8,
|
||||||
|
|
||||||
|
defaultExpiration: null,
|
||||||
maxExpiration: null
|
maxExpiration: null
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
To set a maximum expiration of 7 days:
|
To set a maximum expiration of 7 days:
|
||||||
|
|
||||||
maxExpiration: {
|
maxExpiration: {
|
||||||
units: ExpirationUnits.Days,
|
units: ExpirationUnits.Days,
|
||||||
value: 7
|
value: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultExpiration follows the same format
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// How often codes and uploads are checked for expiration and deleted
|
||||||
|
cleanupInterval: 300,
|
||||||
|
|
||||||
notifications: {
|
notifications: {
|
||||||
|
// How often the notification queue is flushed, in seconds
|
||||||
interval: 10,
|
interval: 10,
|
||||||
|
|
||||||
maxAttempt: 12,
|
maxAttempt: 12,
|
||||||
adminUrl: 'http://localhost:3001/admin/',
|
adminUrl: 'http://localhost:3001/admin/',
|
||||||
|
|
||||||
|
|
|
@ -29,5 +29,8 @@ function humanFileSize(bytes, si)
|
||||||
<li><%= file.name %> (<%= humanFileSize(file.size, true) %>)</li>
|
<li><%= file.name %> (<%= humanFileSize(file.size, true) %>)</li>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</ul>
|
</ul>
|
||||||
|
<% if (upload.expirationDate !== null) { %>
|
||||||
|
These files will be automatically deleted after <%=upload.expirationDate.toLocaleString('en-US')%>
|
||||||
|
<% } %>
|
||||||
<p>You can download these files by logging in to <a href="<%= adminUrl %>"><%= adminUrl %></a></p>
|
<p>You can download these files by logging in to <a href="<%= adminUrl %>"><%= adminUrl %></a></p>
|
||||||
<p>Cheers,<br />Recv</p>
|
<p>Cheers,<br />Recv</p>
|
3
index.js
3
index.js
|
@ -17,6 +17,7 @@ else
|
||||||
|
|
||||||
const Repository = require('./lib/repository');
|
const Repository = require('./lib/repository');
|
||||||
const NotificationWorker = require('./lib/workers/notification');
|
const NotificationWorker = require('./lib/workers/notification');
|
||||||
|
const ExpirationWorker = require('./lib/workers/expiration');
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
|
@ -116,6 +117,8 @@ const webpackConfigFactory = require('./webpack.config.js');
|
||||||
var notificationWorker = new NotificationWorker(repository);
|
var notificationWorker = new NotificationWorker(repository);
|
||||||
notificationWorker.start(config.notifications.interval * 1000);
|
notificationWorker.start(config.notifications.interval * 1000);
|
||||||
|
|
||||||
|
var expirationWorker = new ExpirationWorker(repository);
|
||||||
|
expirationWorker.start(config.cleanupInterval * 1000);
|
||||||
|
|
||||||
var server = app.listen(config.port, () => console.log('Recv running on port ' + server.address().port));
|
var server = app.listen(config.port, () => console.log('Recv running on port ' + server.address().port));
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,13 +214,14 @@ module.exports = (repository) =>
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
router.get('/maxExpiration', asyncHandler(async (req, res) =>
|
router.get('/expiration', asyncHandler(async (req, res) =>
|
||||||
{
|
{
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
await checkAuthorization(req, res, repository, async (user) =>
|
||||||
{
|
{
|
||||||
res.send(config.code.maxExpiration !== null
|
res.send({
|
||||||
? config.code.maxExpiration
|
max: config.code.maxExpiration,
|
||||||
: { units: '', value: 1 });
|
default: config.code.defaultExpiration
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ module.exports = (repository) =>
|
||||||
jwt.sign({
|
jwt.sign({
|
||||||
code: req.body.code,
|
code: req.body.code,
|
||||||
codeUserId: code.userId,
|
codeUserId: code.userId,
|
||||||
codeExpirationDate: code.expirationDate
|
codeExpirationTime: code.expirationDate !== null ? code.expirationDate.getTime() : null
|
||||||
}, config.jwtSecret, (err, token) =>
|
}, config.jwtSecret, (err, token) =>
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
@ -45,7 +45,7 @@ module.exports = (repository, tusServer) =>
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
|
|
||||||
// Upload API
|
// Upload API
|
||||||
router.get('/message/:code', asyncHandler(async (req, res) =>
|
router.get('/info/:code', asyncHandler(async (req, res) =>
|
||||||
{
|
{
|
||||||
var code = await repository.codes.get(req.params.code);
|
var code = await repository.codes.get(req.params.code);
|
||||||
if (code === null)
|
if (code === null)
|
||||||
|
@ -54,19 +54,23 @@ module.exports = (repository, tusServer) =>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!code.messageHTML)
|
let info = {
|
||||||
|
message: null,
|
||||||
|
expirationDate: code.expirationDate !== null ? code.expirationDate.getTime() : null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (code.messageHTML)
|
||||||
{
|
{
|
||||||
res.sendStatus(204);
|
var user = await repository.users.get(code.userId);
|
||||||
return;
|
var name = user !== null ? user.name : null;
|
||||||
|
|
||||||
|
info.message = {
|
||||||
|
name: name,
|
||||||
|
message: code.messageHTML
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await repository.users.get(code.userId);
|
res.send(info);
|
||||||
var name = user !== null ? user.name : null;
|
|
||||||
|
|
||||||
res.send({
|
|
||||||
name: name,
|
|
||||||
message: code.messageHTML
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
router.post('/complete', asyncHandler(async (req, res) =>
|
router.post('/complete', asyncHandler(async (req, res) =>
|
||||||
|
@ -102,7 +106,13 @@ module.exports = (repository, tusServer) =>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uploadId = await repository.uploads.insert(decoded.codeUserId, decoded.code, req.body.files, decoded.codeExpirationDate);
|
var uploadId = await repository.uploads.insert(
|
||||||
|
decoded.codeUserId,
|
||||||
|
decoded.code,
|
||||||
|
req.body.files,
|
||||||
|
decoded.codeExpirationTime !== null ? new Date(decoded.codeExpirationTime) : null
|
||||||
|
);
|
||||||
|
|
||||||
await repository.notifications.insert({
|
await repository.notifications.insert({
|
||||||
userId: decoded.codeUserId,
|
userId: decoded.codeUserId,
|
||||||
uploadId: uploadId
|
uploadId: uploadId
|
||||||
|
|
|
@ -2,6 +2,7 @@ const map = require('lodash/map');
|
||||||
const retry = require('async-retry');
|
const retry = require('async-retry');
|
||||||
const generate = require('nanoid/generate');
|
const generate = require('nanoid/generate');
|
||||||
const markdown = require('markdown').markdown;
|
const markdown = require('markdown').markdown;
|
||||||
|
const async = require('async');
|
||||||
const ExpirationUnits = require('../expirationunits');
|
const ExpirationUnits = require('../expirationunits');
|
||||||
|
|
||||||
|
|
||||||
|
@ -197,6 +198,40 @@ class CodeRepository
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deleteExpired()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) =>
|
||||||
|
{
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
|
self.store.find({ $where: function() { return this.expirationDate !== null && this.expirationDate < now }}, (err, docs) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachOfSeries(docs,
|
||||||
|
async (doc) =>
|
||||||
|
{
|
||||||
|
console.log('Expired code: ' + doc._id);
|
||||||
|
await self.delete(doc._id);
|
||||||
|
},
|
||||||
|
(err) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
reject(err);
|
||||||
|
else
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ const map = require('lodash/map');
|
||||||
const filter = require('lodash/filter');
|
const filter = require('lodash/filter');
|
||||||
const resolvePath = require('resolve-path');
|
const resolvePath = require('resolve-path');
|
||||||
const fs = require('mz/fs');
|
const fs = require('mz/fs');
|
||||||
|
const async = require('async');
|
||||||
|
|
||||||
|
|
||||||
class Upload
|
class Upload
|
||||||
|
@ -205,6 +206,40 @@ class UploadRepository
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deleteExpired()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) =>
|
||||||
|
{
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
|
self.store.find({ $where: function() { return this.expirationDate !== null && this.expirationDate < now }}, (err, docs) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachOfSeries(docs,
|
||||||
|
async (doc) =>
|
||||||
|
{
|
||||||
|
console.log('Expired upload: ' + doc._id);
|
||||||
|
await self.delete(doc._id);
|
||||||
|
},
|
||||||
|
(err) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
reject(err);
|
||||||
|
else
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
class AbstractIntervalWorker
|
||||||
|
{
|
||||||
|
start(interval)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.stop();
|
||||||
|
self.timer = setInterval(async () =>
|
||||||
|
{
|
||||||
|
if (self.ticking)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self.ticking = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await self.tick();
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
self.ticking = false;
|
||||||
|
}, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stop()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
if (self.timer)
|
||||||
|
{
|
||||||
|
clearInterval(self.timer);
|
||||||
|
self.timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Implement this:
|
||||||
|
async tick()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AbstractIntervalWorker;
|
|
@ -0,0 +1,23 @@
|
||||||
|
const async = require('async');
|
||||||
|
const AbstractIntervalWorker = require('./abstractintervalworker');
|
||||||
|
|
||||||
|
|
||||||
|
class ExpirationWorker extends AbstractIntervalWorker
|
||||||
|
{
|
||||||
|
constructor(repository)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async tick()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
await self.repository.uploads.deleteExpired();
|
||||||
|
await self.repository.codes.deleteExpired();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ExpirationWorker;
|
|
@ -1,45 +1,21 @@
|
||||||
const config = require('../../config');
|
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const nodemailer = require('nodemailer');
|
const nodemailer = require('nodemailer');
|
||||||
const Email = require('email-templates');
|
const Email = require('email-templates');
|
||||||
|
const fs = require('mz/fs');
|
||||||
|
const path = require('path');
|
||||||
|
const getPaths = require('get-paths');
|
||||||
|
const AbstractIntervalWorker = require('./abstractintervalworker');
|
||||||
|
|
||||||
|
|
||||||
class NotificationWorker
|
class NotificationWorker extends AbstractIntervalWorker
|
||||||
{
|
{
|
||||||
constructor(repository)
|
constructor(repository)
|
||||||
{
|
{
|
||||||
|
super();
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
start(interval)
|
|
||||||
{
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
self.stop();
|
|
||||||
self.timer = setInterval(async () =>
|
|
||||||
{
|
|
||||||
if (self.ticking)
|
|
||||||
return;
|
|
||||||
|
|
||||||
self.ticking = true;
|
|
||||||
await self.tick();
|
|
||||||
self.ticking = false;
|
|
||||||
}, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
stop()
|
|
||||||
{
|
|
||||||
var self = this;
|
|
||||||
if (self.timer)
|
|
||||||
{
|
|
||||||
clearInterval(self.timer);
|
|
||||||
self.timer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async tick()
|
async tick()
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -88,6 +64,39 @@ class NotificationWorker
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Override the default template locator with one that checks
|
||||||
|
// the custom folder first, then falls back to the configured folder
|
||||||
|
let getTemplatePathFromRoot = function(root, view, ext)
|
||||||
|
{
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const paths = await getPaths(
|
||||||
|
root,
|
||||||
|
view,
|
||||||
|
ext
|
||||||
|
);
|
||||||
|
const filePath = path.resolve(root, paths.rel);
|
||||||
|
resolve({ filePath, paths });
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
email.getTemplatePath = async function(view)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await getTemplatePathFromRoot(path.resolve('custom/emails'), view, this.config.views.options.extension);
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
return await getTemplatePathFromRoot(this.config.views.root, view, this.config.views.options.extension);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
email
|
email
|
||||||
.send({
|
.send({
|
||||||
template: 'uploadnotification',
|
template: 'uploadnotification',
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"description": "Recv - self-hosted web file transfer",
|
"description": "Recv - self-hosted web file transfer",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "supervisor -w index.js,lib index.js",
|
"dev": "supervisor -w index.js,lib,config.js,config.defaults.js index.js",
|
||||||
"devbuild": "webpack-cli --mode development",
|
"devbuild": "webpack-cli --mode development",
|
||||||
"build": "webpack-cli --mode production",
|
"build": "webpack-cli --mode production",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
|
|
@ -53,6 +53,21 @@ Vue.filter('formatDateTime', (value) =>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Vue.filter('formatDate', (value) =>
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
return moment(String(value)).format(i18n.t('dateFormat'))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Vue.filter('formatTime', (value) =>
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
return moment(String(value)).format(i18n.t('timeFormat'))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// All credit goes to: https://stackoverflow.com/a/14919494
|
// All credit goes to: https://stackoverflow.com/a/14919494
|
||||||
function humanFileSize(bytes, si)
|
function humanFileSize(bytes, si)
|
||||||
|
|
|
@ -2,6 +2,8 @@ export default {
|
||||||
title: 'File upload - Recv',
|
title: 'File upload - Recv',
|
||||||
disclaimer: '',
|
disclaimer: '',
|
||||||
dateTimeFormat: 'MM/DD/YYYY hh:mm a',
|
dateTimeFormat: 'MM/DD/YYYY hh:mm a',
|
||||||
|
dateFormat: 'MM/DD/YYYY',
|
||||||
|
timeFormat: 'hh:mm a',
|
||||||
|
|
||||||
landing: {
|
landing: {
|
||||||
invitePlaceholder: 'Code',
|
invitePlaceholder: 'Code',
|
||||||
|
@ -16,7 +18,20 @@ export default {
|
||||||
invalidLogin: 'The specified username or password is incorrect'
|
invalidLogin: 'The specified username or password is incorrect'
|
||||||
},
|
},
|
||||||
|
|
||||||
messageFrom: 'Message from ',
|
messageFrom: 'Message from {owner}',
|
||||||
|
uploadDisclaimer: '',
|
||||||
|
expirationNotice: {
|
||||||
|
never: '',
|
||||||
|
ever: 'Files uploaded for this invite code will be automatically deleted on {date} at {time}'
|
||||||
|
},
|
||||||
|
|
||||||
|
expirationValues: {
|
||||||
|
h: '0 hours | 1 hour | {count} hours',
|
||||||
|
d: '0 days | 1 day | {count} days',
|
||||||
|
w: '0 weeks | 1 week | {count} weeks',
|
||||||
|
M: '0 months | 1 month | {count} months',
|
||||||
|
y: '0 years | 1 year | {count} years'
|
||||||
|
},
|
||||||
|
|
||||||
uppyDashboard: {
|
uppyDashboard: {
|
||||||
done: 'Done',
|
done: 'Done',
|
||||||
|
@ -33,7 +48,7 @@ export default {
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
|
|
||||||
diskspace: '%{available} disk space available of %{total} total',
|
diskspace: '{available} disk space available of {total} total',
|
||||||
|
|
||||||
login: {
|
login: {
|
||||||
usernamePlaceholder: 'Username or e-mail address',
|
usernamePlaceholder: 'Username or e-mail address',
|
||||||
|
@ -85,14 +100,7 @@ export default {
|
||||||
y: 'Years:'
|
y: 'Years:'
|
||||||
},
|
},
|
||||||
|
|
||||||
expirationHint: {
|
expirationHint: 'The maximum allowed expiration is {valueUnits}'
|
||||||
format: 'The maximum allowed expiration is %{valueUnits}',
|
|
||||||
h: '0 hours | 1 hour | {count} hours',
|
|
||||||
d: '0 days | 1 day | {count} days',
|
|
||||||
w: '0 weeks | 1 week | {count} weeks',
|
|
||||||
M: '0 months | 1 month | {count} months',
|
|
||||||
y: '0 years | 1 year | {count} years'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ export default {
|
||||||
title: 'Bestandsoverdracht - Recv',
|
title: 'Bestandsoverdracht - Recv',
|
||||||
disclaimer: '',
|
disclaimer: '',
|
||||||
dateTimeFormat: 'DD-MM-YYYY HH:mm',
|
dateTimeFormat: 'DD-MM-YYYY HH:mm',
|
||||||
|
dateFormat: 'DD-MM-YYYY',
|
||||||
|
timeFormat: 'HH:mm',
|
||||||
|
|
||||||
landing: {
|
landing: {
|
||||||
invitePlaceholder: 'Code',
|
invitePlaceholder: 'Code',
|
||||||
|
@ -16,7 +18,20 @@ export default {
|
||||||
invalidLogin: 'De ingevoerde gebruikersnaam of wachtwoord is incorrect'
|
invalidLogin: 'De ingevoerde gebruikersnaam of wachtwoord is incorrect'
|
||||||
},
|
},
|
||||||
|
|
||||||
messageFrom: 'Bericht van ',
|
messageFrom: 'Bericht van {owner}',
|
||||||
|
uploadDisclaimer: '',
|
||||||
|
expirationNotice: {
|
||||||
|
never: '',
|
||||||
|
ever: 'Bestanden die worden geupload voor deze code worden automatisch verwijderd op {date} om {time}'
|
||||||
|
},
|
||||||
|
|
||||||
|
expirationValues: {
|
||||||
|
h: '0 uren | 1 uur | {count} uren',
|
||||||
|
d: '0 dagen | 1 dag | {count} dagen',
|
||||||
|
w: '0 weken | 1 week | {count} weken',
|
||||||
|
M: '0 maanden | 1 maand | {count} maanden',
|
||||||
|
y: '0 jaren | 1 jaar | {count} jaren'
|
||||||
|
},
|
||||||
|
|
||||||
uppyDashboard: {
|
uppyDashboard: {
|
||||||
done: 'Gereed',
|
done: 'Gereed',
|
||||||
|
@ -33,7 +48,7 @@ export default {
|
||||||
save: 'Opslaan',
|
save: 'Opslaan',
|
||||||
delete: 'Verwijderen',
|
delete: 'Verwijderen',
|
||||||
|
|
||||||
diskspace: '%{available} schijfruimte beschikbaar van %{total} totaal',
|
diskspace: '{available} schijfruimte beschikbaar van {total} totaal',
|
||||||
|
|
||||||
login: {
|
login: {
|
||||||
usernamePlaceholder: 'Gebruikersnaam of e-mail adres',
|
usernamePlaceholder: 'Gebruikersnaam of e-mail adres',
|
||||||
|
@ -85,14 +100,7 @@ export default {
|
||||||
y: 'Jaren:'
|
y: 'Jaren:'
|
||||||
},
|
},
|
||||||
|
|
||||||
expirationHint: {
|
expirationHint: 'De maximaal toegestane verlooptermijn is {valueUnits}'
|
||||||
format: 'De maximaal toegestane verlooptermijn is %{valueUnits}',
|
|
||||||
h: '0 uren | 1 uur | {value} uren',
|
|
||||||
d: '0 dagen | 1 dag | {value} dagen',
|
|
||||||
w: '0 weken | 1 week | {value} weken',
|
|
||||||
M: '0 maanden | 1 maand | {value} maanden',
|
|
||||||
y: '0 jaren | 1 jaar | {value} jaren'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,15 @@
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="disclaimer">
|
<div class="disclaimer" v-if="$t('disclaimer')">
|
||||||
{{ $t('disclaimer') }}
|
{{ $t('disclaimer') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<style lang="scss">
|
||||||
export default {
|
.disclaimer
|
||||||
|
{
|
||||||
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
</script>
|
</style>
|
|
@ -1,10 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="upload">
|
<div id="upload">
|
||||||
<div class="message" v-if="message !== null">
|
<div class="message" v-if="message !== null">
|
||||||
<div class="from" v-if="message.name !== null">{{ $t('messageFrom') + message.name }}</div>
|
<div class="from" v-if="message.name !== null">{{ $t('messageFrom', { owner: message.name }) }}</div>
|
||||||
<div v-html="message.message"></div>
|
<div v-html="message.message"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="uploadDisclaimer" v-if="$t('uploadDisclaimer')">
|
||||||
|
{{ $t('uploadDisclaimer') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="expirationDate" v-if="showExpirationNotice()">
|
||||||
|
{{ expirationDate === null ? $t('expirationNotice.never') : $t('expirationNotice.ever', getExpirationNoticeDateTime()) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="uploadTarget"></div>
|
<div class="uploadTarget"></div>
|
||||||
|
|
||||||
<div class="navigation">
|
<div class="navigation">
|
||||||
|
@ -30,7 +39,8 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
uploadToken: shared.uploadToken,
|
uploadToken: shared.uploadToken,
|
||||||
message: null
|
message: null,
|
||||||
|
expirationDate: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -49,10 +59,11 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
axios.get('/message/' + encodeURIComponent(self.codeParam))
|
axios.get('/info/' + encodeURIComponent(self.codeParam))
|
||||||
.then((response) =>
|
.then((response) =>
|
||||||
{
|
{
|
||||||
self.message = response.status !== 204 ? response.data : null;
|
self.message = response.data.message;
|
||||||
|
self.expirationDate = response.data.expirationDate !== null ? new Date(response.data.expirationDate) : null;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -119,6 +130,30 @@ export default {
|
||||||
uploadURL: false
|
uploadURL: false
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
showExpirationNotice()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (self.expirationDate === null)
|
||||||
|
return self.$i18n.t('expirationNotice.never') != '';
|
||||||
|
else
|
||||||
|
return self.$i18n.t('expirationNotice.ever') != '';
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
getExpirationNoticeDateTime()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return {
|
||||||
|
date: self.$options.filters.formatDate(self.expirationDate),
|
||||||
|
time: self.$options.filters.formatTime(self.expirationDate)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -131,11 +166,23 @@ export default {
|
||||||
color: #808080;
|
color: #808080;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
border-bottom: solid 1px #e0e0e0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.navigation
|
.navigation
|
||||||
{
|
{
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.uploadDisclaimer, .expirationDate
|
||||||
|
{
|
||||||
|
color: #808080;
|
||||||
|
font-size: 75%;
|
||||||
|
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -47,7 +47,7 @@
|
||||||
<input v-if="code.id" id="expiration" type="text" readonly :value="code.expirationDate | formatDateTime" class="pure-input-2-3">
|
<input v-if="code.id" id="expiration" type="text" readonly :value="code.expirationDate | formatDateTime" class="pure-input-2-3">
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-form-description" v-if="!code.id && maxExpiration !== null">
|
<div class="pure-form-description" v-if="!code.id && maxExpiration !== null">
|
||||||
{{ $t('admin.codes.detail.expirationHint.format', { valueUnits: getExpirationValueUnits() }) }}
|
{{ $t('admin.codes.detail.expirationHint', { valueUnits: getExpirationValueUnits() }) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
|
@ -123,20 +123,26 @@ export default {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
axios.get('/admin/maxExpiration', {
|
axios.get('/admin/expiration', {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + shared.adminToken
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((response) =>
|
.then((response) =>
|
||||||
{
|
{
|
||||||
self.maxExpiration = response.data.units ? {
|
self.maxExpiration = response.data.max !== null ? {
|
||||||
units: response.data.units,
|
units: response.data.max.units,
|
||||||
value: response.data.value
|
value: response.data.max.value
|
||||||
} : null;
|
} : null;
|
||||||
|
|
||||||
self.code = {
|
self.code = {
|
||||||
expiration: response.data,
|
expiration: response.data.default !== null ? {
|
||||||
|
units: response.data.default.units,
|
||||||
|
value: response.data.default.value
|
||||||
|
} : {
|
||||||
|
units: '',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
message: null
|
message: null
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -197,7 +203,7 @@ export default {
|
||||||
return self.maxExpiration === null;
|
return self.maxExpiration === null;
|
||||||
|
|
||||||
if (self.maxExpiration === null)
|
if (self.maxExpiration === null)
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
let validUnits = ['h', 'd', 'w', 'M', 'y'];
|
let validUnits = ['h', 'd', 'w', 'M', 'y'];
|
||||||
return validUnits.indexOf(units) <= validUnits.indexOf(self.maxExpiration.units);
|
return validUnits.indexOf(units) <= validUnits.indexOf(self.maxExpiration.units);
|
||||||
|
@ -211,7 +217,7 @@ export default {
|
||||||
if (self.maxExpiration === null)
|
if (self.maxExpiration === null)
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
return self.$tc('admin.codes.detail.expirationHint.' + self.maxExpiration.units,
|
return self.$tc('expirationValues.' + self.maxExpiration.units,
|
||||||
self.maxExpiration.value,
|
self.maxExpiration.value,
|
||||||
{ count: self.maxExpiration.value });
|
{ count: self.maxExpiration.value });
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,11 +75,13 @@ export default {
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
self.unwatch = self.$watch(self.updateDiskSpace, () => self.updateDiskSpace());
|
self.unwatch = self.$watch(self.updateDiskSpace, () => self.updateDiskSpace());
|
||||||
|
self.refreshTimer = setInterval(() => self.updateDiskSpace(), 10000);
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy()
|
beforeDestroy()
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
clearInterval(self.refreshTimer);
|
||||||
self.unwatch();
|
self.unwatch();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -87,8 +89,10 @@ export default {
|
||||||
updateDiskSpace()
|
updateDiskSpace()
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
if (shared.adminToken !== null)
|
if (shared.adminToken !== null && !self.updatingDiskSpace)
|
||||||
{
|
{
|
||||||
|
self.updatingDiskSpace = true;
|
||||||
|
|
||||||
axios.get('/admin/diskspace', {
|
axios.get('/admin/diskspace', {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + shared.adminToken
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
|
@ -96,6 +100,13 @@ export default {
|
||||||
.then((response) =>
|
.then((response) =>
|
||||||
{
|
{
|
||||||
self.diskspace = response.data;
|
self.diskspace = response.data;
|
||||||
|
})
|
||||||
|
.catch((err) =>
|
||||||
|
{
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
self.updatingDiskSpace = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue