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
|
||||
data
|
||||
custom/*.js
|
||||
custom/*
|
||||
public/dist/*.js
|
||||
public/dist/*.map
|
||||
public/dist/index.html
|
||||
config.js
|
||||
*.sublime-workspace
|
||||
npm-debug.log
|
||||
/custom/images/logo.png
|
||||
npm-debug.log
|
|
@ -36,21 +36,28 @@ module.exports = {
|
|||
alphabet: '1234567890abcdef',
|
||||
length: 8,
|
||||
|
||||
defaultExpiration: null,
|
||||
maxExpiration: null
|
||||
|
||||
/*
|
||||
|
||||
To set a maximum expiration of 7 days:
|
||||
|
||||
maxExpiration: {
|
||||
units: ExpirationUnits.Days,
|
||||
value: 7
|
||||
}
|
||||
|
||||
defaultExpiration follows the same format
|
||||
*/
|
||||
},
|
||||
|
||||
// How often codes and uploads are checked for expiration and deleted
|
||||
cleanupInterval: 300,
|
||||
|
||||
notifications: {
|
||||
// How often the notification queue is flushed, in seconds
|
||||
interval: 10,
|
||||
|
||||
maxAttempt: 12,
|
||||
adminUrl: 'http://localhost:3001/admin/',
|
||||
|
||||
|
|
|
@ -29,5 +29,8 @@ function humanFileSize(bytes, si)
|
|||
<li><%= file.name %> (<%= humanFileSize(file.size, true) %>)</li>
|
||||
<% }) %>
|
||||
</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>Cheers,<br />Recv</p>
|
3
index.js
3
index.js
|
@ -17,6 +17,7 @@ else
|
|||
|
||||
const Repository = require('./lib/repository');
|
||||
const NotificationWorker = require('./lib/workers/notification');
|
||||
const ExpirationWorker = require('./lib/workers/expiration');
|
||||
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
|
@ -116,6 +117,8 @@ const webpackConfigFactory = require('./webpack.config.js');
|
|||
var notificationWorker = new NotificationWorker(repository);
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -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) =>
|
||||
{
|
||||
res.send(config.code.maxExpiration !== null
|
||||
? config.code.maxExpiration
|
||||
: { units: '', value: 1 });
|
||||
res.send({
|
||||
max: config.code.maxExpiration,
|
||||
default: config.code.defaultExpiration
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ module.exports = (repository) =>
|
|||
jwt.sign({
|
||||
code: req.body.code,
|
||||
codeUserId: code.userId,
|
||||
codeExpirationDate: code.expirationDate
|
||||
codeExpirationTime: code.expirationDate !== null ? code.expirationDate.getTime() : null
|
||||
}, config.jwtSecret, (err, token) =>
|
||||
{
|
||||
if (err)
|
||||
|
|
|
@ -45,7 +45,7 @@ module.exports = (repository, tusServer) =>
|
|||
var router = express.Router();
|
||||
|
||||
// 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);
|
||||
if (code === null)
|
||||
|
@ -54,19 +54,23 @@ module.exports = (repository, tusServer) =>
|
|||
return;
|
||||
}
|
||||
|
||||
if (!code.messageHTML)
|
||||
let info = {
|
||||
message: null,
|
||||
expirationDate: code.expirationDate !== null ? code.expirationDate.getTime() : null
|
||||
};
|
||||
|
||||
if (code.messageHTML)
|
||||
{
|
||||
res.sendStatus(204);
|
||||
return;
|
||||
var user = await repository.users.get(code.userId);
|
||||
var name = user !== null ? user.name : null;
|
||||
|
||||
info.message = {
|
||||
name: name,
|
||||
message: code.messageHTML
|
||||
};
|
||||
}
|
||||
|
||||
var user = await repository.users.get(code.userId);
|
||||
var name = user !== null ? user.name : null;
|
||||
|
||||
res.send({
|
||||
name: name,
|
||||
message: code.messageHTML
|
||||
});
|
||||
res.send(info);
|
||||
}));
|
||||
|
||||
router.post('/complete', asyncHandler(async (req, res) =>
|
||||
|
@ -102,7 +106,13 @@ module.exports = (repository, tusServer) =>
|
|||
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({
|
||||
userId: decoded.codeUserId,
|
||||
uploadId: uploadId
|
||||
|
|
|
@ -2,6 +2,7 @@ const map = require('lodash/map');
|
|||
const retry = require('async-retry');
|
||||
const generate = require('nanoid/generate');
|
||||
const markdown = require('markdown').markdown;
|
||||
const async = require('async');
|
||||
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 resolvePath = require('resolve-path');
|
||||
const fs = require('mz/fs');
|
||||
const async = require('async');
|
||||
|
||||
|
||||
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 nodemailer = require('nodemailer');
|
||||
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)
|
||||
{
|
||||
super();
|
||||
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()
|
||||
{
|
||||
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
|
||||
.send({
|
||||
template: 'uploadnotification',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Recv - self-hosted web file transfer",
|
||||
"main": "index.js",
|
||||
"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",
|
||||
"build": "webpack-cli --mode production",
|
||||
"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
|
||||
function humanFileSize(bytes, si)
|
||||
|
|
|
@ -2,6 +2,8 @@ export default {
|
|||
title: 'File upload - Recv',
|
||||
disclaimer: '',
|
||||
dateTimeFormat: 'MM/DD/YYYY hh:mm a',
|
||||
dateFormat: 'MM/DD/YYYY',
|
||||
timeFormat: 'hh:mm a',
|
||||
|
||||
landing: {
|
||||
invitePlaceholder: 'Code',
|
||||
|
@ -16,7 +18,20 @@ export default {
|
|||
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: {
|
||||
done: 'Done',
|
||||
|
@ -33,7 +48,7 @@ export default {
|
|||
save: 'Save',
|
||||
delete: 'Delete',
|
||||
|
||||
diskspace: '%{available} disk space available of %{total} total',
|
||||
diskspace: '{available} disk space available of {total} total',
|
||||
|
||||
login: {
|
||||
usernamePlaceholder: 'Username or e-mail address',
|
||||
|
@ -85,14 +100,7 @@ export default {
|
|||
y: 'Years:'
|
||||
},
|
||||
|
||||
expirationHint: {
|
||||
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'
|
||||
}
|
||||
expirationHint: 'The maximum allowed expiration is {valueUnits}'
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ export default {
|
|||
title: 'Bestandsoverdracht - Recv',
|
||||
disclaimer: '',
|
||||
dateTimeFormat: 'DD-MM-YYYY HH:mm',
|
||||
dateFormat: 'DD-MM-YYYY',
|
||||
timeFormat: 'HH:mm',
|
||||
|
||||
landing: {
|
||||
invitePlaceholder: 'Code',
|
||||
|
@ -16,7 +18,20 @@ export default {
|
|||
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: {
|
||||
done: 'Gereed',
|
||||
|
@ -33,7 +48,7 @@ export default {
|
|||
save: 'Opslaan',
|
||||
delete: 'Verwijderen',
|
||||
|
||||
diskspace: '%{available} schijfruimte beschikbaar van %{total} totaal',
|
||||
diskspace: '{available} schijfruimte beschikbaar van {total} totaal',
|
||||
|
||||
login: {
|
||||
usernamePlaceholder: 'Gebruikersnaam of e-mail adres',
|
||||
|
@ -85,14 +100,7 @@ export default {
|
|||
y: 'Jaren:'
|
||||
},
|
||||
|
||||
expirationHint: {
|
||||
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'
|
||||
}
|
||||
expirationHint: 'De maximaal toegestane verlooptermijn is {valueUnits}'
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -8,13 +8,15 @@
|
|||
<router-view></router-view>
|
||||
</div>
|
||||
|
||||
<div class="disclaimer">
|
||||
<div class="disclaimer" v-if="$t('disclaimer')">
|
||||
{{ $t('disclaimer') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<style lang="scss">
|
||||
.disclaimer
|
||||
{
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
</script>
|
||||
</style>
|
|
@ -1,10 +1,19 @@
|
|||
<template>
|
||||
<div id="upload">
|
||||
<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>
|
||||
|
||||
<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="navigation">
|
||||
|
@ -30,7 +39,8 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
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) =>
|
||||
{
|
||||
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
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
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>
|
||||
|
@ -131,11 +166,23 @@ export default {
|
|||
color: #808080;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
border-bottom: solid 1px #e0e0e0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.navigation
|
||||
{
|
||||
margin-top: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.uploadDisclaimer, .expirationDate
|
||||
{
|
||||
color: #808080;
|
||||
font-size: 75%;
|
||||
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
|
@ -47,7 +47,7 @@
|
|||
<input v-if="code.id" id="expiration" type="text" readonly :value="code.expirationDate | formatDateTime" class="pure-input-2-3">
|
||||
</div>
|
||||
<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 class="pure-control-group">
|
||||
|
@ -123,20 +123,26 @@ export default {
|
|||
}
|
||||
else
|
||||
{
|
||||
axios.get('/admin/maxExpiration', {
|
||||
axios.get('/admin/expiration', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + shared.adminToken
|
||||
}
|
||||
})
|
||||
.then((response) =>
|
||||
{
|
||||
self.maxExpiration = response.data.units ? {
|
||||
units: response.data.units,
|
||||
value: response.data.value
|
||||
self.maxExpiration = response.data.max !== null ? {
|
||||
units: response.data.max.units,
|
||||
value: response.data.max.value
|
||||
} : null;
|
||||
|
||||
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
|
||||
};
|
||||
})
|
||||
|
@ -197,7 +203,7 @@ export default {
|
|||
return self.maxExpiration === null;
|
||||
|
||||
if (self.maxExpiration === null)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
let validUnits = ['h', 'd', 'w', 'M', 'y'];
|
||||
return validUnits.indexOf(units) <= validUnits.indexOf(self.maxExpiration.units);
|
||||
|
@ -211,7 +217,7 @@ export default {
|
|||
if (self.maxExpiration === null)
|
||||
return '';
|
||||
|
||||
return self.$tc('admin.codes.detail.expirationHint.' + self.maxExpiration.units,
|
||||
return self.$tc('expirationValues.' + self.maxExpiration.units,
|
||||
self.maxExpiration.value,
|
||||
{ count: self.maxExpiration.value });
|
||||
}
|
||||
|
|
|
@ -75,11 +75,13 @@ export default {
|
|||
{
|
||||
var self = this;
|
||||
self.unwatch = self.$watch(self.updateDiskSpace, () => self.updateDiskSpace());
|
||||
self.refreshTimer = setInterval(() => self.updateDiskSpace(), 10000);
|
||||
},
|
||||
|
||||
beforeDestroy()
|
||||
{
|
||||
var self = this;
|
||||
clearInterval(self.refreshTimer);
|
||||
self.unwatch();
|
||||
},
|
||||
|
||||
|
@ -87,8 +89,10 @@ export default {
|
|||
updateDiskSpace()
|
||||
{
|
||||
var self = this;
|
||||
if (shared.adminToken !== null)
|
||||
if (shared.adminToken !== null && !self.updatingDiskSpace)
|
||||
{
|
||||
self.updatingDiskSpace = true;
|
||||
|
||||
axios.get('/admin/diskspace', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + shared.adminToken
|
||||
|
@ -96,6 +100,13 @@ export default {
|
|||
.then((response) =>
|
||||
{
|
||||
self.diskspace = response.data;
|
||||
})
|
||||
.catch((err) =>
|
||||
{
|
||||
})
|
||||
.then(() =>
|
||||
{
|
||||
self.updatingDiskSpace = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue