parent
88beaa6f7d
commit
d6f553e99f
|
@ -0,0 +1,20 @@
|
||||||
|
<% let expirationDate = null; %>
|
||||||
|
<p>Hello <%=user.name%>,</p>
|
||||||
|
<p>The following files have been assigned from <%=prevUser.name%> to you<%=prevUser.userId !== assignUser.userId ? ' by ' + assignUser.name : ''%>:</p>
|
||||||
|
<ul>
|
||||||
|
<% uploads.forEach((upload) => { %>
|
||||||
|
<% upload.files.forEach((file) => { %>
|
||||||
|
<li><%=file.name%> (<%=humanFileSize(file.size, true)%>)</li>
|
||||||
|
<%
|
||||||
|
});
|
||||||
|
|
||||||
|
if (upload.expirationDate !== null && (expirationDate === null || upload.expirationDate < expirationDate))
|
||||||
|
expirationDate = upload.expirationDate;
|
||||||
|
%>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
<% if (expirationDate !== null) { %>
|
||||||
|
These files will be automatically deleted after <%=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>
|
|
@ -0,0 +1 @@
|
||||||
|
📄 <%=fileCount%> file<%=fileCount != 1 ? 's have' : ' has'%> been assigned to you
|
|
@ -1,36 +1,12 @@
|
||||||
<%
|
<p>Hello <%=user.name%>,</p>
|
||||||
function humanFileSize(bytes, si)
|
<p>The following files have been uploaded:</p>
|
||||||
{
|
|
||||||
var thresh = si ? 1000 : 1024;
|
|
||||||
if(Math.abs(bytes) < thresh)
|
|
||||||
{
|
|
||||||
return bytes + ' B';
|
|
||||||
}
|
|
||||||
|
|
||||||
var units = si
|
|
||||||
? ['kB','MB','GB','TB','PB','EB','ZB','YB']
|
|
||||||
: ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
|
|
||||||
var u = -1;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
bytes /= thresh;
|
|
||||||
++u;
|
|
||||||
}
|
|
||||||
while(Math.abs(bytes) >= thresh && u < units.length - 1);
|
|
||||||
|
|
||||||
return bytes.toFixed(1) + ' ' + units[u];
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
<p>Hello <%= user.name %>,</p>
|
|
||||||
<p>The following files have just been uploaded:</p>
|
|
||||||
<ul>
|
<ul>
|
||||||
<% upload.files.forEach((file) => { %>
|
<% upload.files.forEach((file) => { %>
|
||||||
<li><%= file.name %> (<%= humanFileSize(file.size, true) %>)</li>
|
<li><%=file.name%> (<%=humanFileSize(file.size, true)%>)</li>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
</ul>
|
</ul>
|
||||||
<% if (upload.expirationDate !== null) { %>
|
<% if (upload.expirationDate !== null) { %>
|
||||||
These files will be automatically deleted after <%=upload.expirationDate.toLocaleString('en-US')%>
|
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>
|
|
@ -1 +1 @@
|
||||||
📄 File upload notification
|
📄 <%=upload.files.count%> file<%=upload.files.count != 1 ? 's' : ''%> uploaded
|
4
index.js
4
index.js
|
@ -1,13 +1,13 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const merge = require('deepmerge');
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
|
||||||
let configDefaults = require('./config.defaults');
|
let configDefaults = require('./config.defaults');
|
||||||
if (fs.existsSync('./config.js'))
|
if (fs.existsSync('./config.js'))
|
||||||
{
|
{
|
||||||
let configChanges = require('./config');
|
let configChanges = require('./config');
|
||||||
global.config = merge(configDefaults, configChanges);
|
global.config = _.merge(configDefaults, configChanges);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
global.config = configDefaults;
|
global.config = configDefaults;
|
||||||
|
|
387
lib/api/admin.js
387
lib/api/admin.js
|
@ -1,387 +0,0 @@
|
||||||
const express = require('express');
|
|
||||||
const asyncHandler = require('express-async-handler');
|
|
||||||
const jwt = require('jsonwebtoken');
|
|
||||||
const path = require('path');
|
|
||||||
const resolvePath = require('resolve-path');
|
|
||||||
const AuthTokens = require('../authtokens');
|
|
||||||
const ExpirationUnits = require('../expirationunits');
|
|
||||||
const disk = require('diskusage');
|
|
||||||
const fs = require('mz/fs');
|
|
||||||
|
|
||||||
|
|
||||||
async function checkAuthorization(req, res, repository, onVerified)
|
|
||||||
{
|
|
||||||
var token;
|
|
||||||
|
|
||||||
if (req.headers.authorization)
|
|
||||||
{
|
|
||||||
if (req.headers.authorization.split(' ')[0] !== 'Bearer')
|
|
||||||
{
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
token = req.headers.authorization.split(' ')[1];
|
|
||||||
}
|
|
||||||
else if (req.cookies && req.cookies.adminToken)
|
|
||||||
{
|
|
||||||
token = req.cookies.adminToken;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jwt.verify(token, config.jwtSecret, async (err, decoded) =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decoded.userId)
|
|
||||||
{
|
|
||||||
var user = await repository.users.get(decoded.userId);
|
|
||||||
if (user === null || !user.active)
|
|
||||||
{
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
await onVerified(user);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
res.sendStatus(400);
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
console.log(e);
|
|
||||||
res.sendStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = (repository) =>
|
|
||||||
{
|
|
||||||
var router = express.Router();
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/whoami', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
res.send({
|
|
||||||
userId: user.id,
|
|
||||||
username: user.username,
|
|
||||||
auth: user.auth
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/diskspace', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
disk.check(config.fileUpload.path, (err, info) =>
|
|
||||||
{
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
res.sendStatus(500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send(info);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Codes
|
|
||||||
*/
|
|
||||||
router.get('/codes', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
var codes = await repository.codes.list(user.hasAuth(AuthTokens.ViewAllCodes) ? null : user.id);
|
|
||||||
var usernames = await repository.users.getNames();
|
|
||||||
|
|
||||||
codes.forEach((item) =>
|
|
||||||
{
|
|
||||||
item.username = usernames[item.userId];
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send(codes);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/codes/:id', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
var code = await repository.codes.get(req.params.id);
|
|
||||||
if (code === null || (code.userId !== user.id && !user.hasAuth(AuthTokens.ViewAllCodes)))
|
|
||||||
{
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = await repository.users.get(code.userId);
|
|
||||||
if (user !== null)
|
|
||||||
code.username = user.name;
|
|
||||||
|
|
||||||
res.send(code);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.post('/codes', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
var postedCode = req.body;
|
|
||||||
|
|
||||||
|
|
||||||
if (config.code.maxExpiration !== null)
|
|
||||||
{
|
|
||||||
let now = new Date();
|
|
||||||
|
|
||||||
if (ExpirationUnits.apply(postedCode.expiration) > ExpirationUnits.apply(config.code.maxExpiration))
|
|
||||||
{
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (postedCode.id)
|
|
||||||
{
|
|
||||||
var code = await repository.codes.get(postedCode.id);
|
|
||||||
if (code === null || (code.userId !== user.id && !user.hasAuth(AuthTokens.ViewAllCodes)))
|
|
||||||
{
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await repository.codes.update({
|
|
||||||
id: postedCode.id,
|
|
||||||
expiration: postedCode.expiration,
|
|
||||||
description: postedCode.description,
|
|
||||||
message: postedCode.message
|
|
||||||
});
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var codeId = await repository.codes.insert({
|
|
||||||
userId: user.id,
|
|
||||||
created: postedCode.created || new Date(),
|
|
||||||
expiration: postedCode.expiration,
|
|
||||||
description: postedCode.description,
|
|
||||||
message: postedCode.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send(codeId);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.delete('/codes/:id', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
var code = await repository.codes.get(req.params.id);
|
|
||||||
if (code == null || (code.userId !== user.id && !user.hasAuth(AuthTokens.ViewAllCodes)))
|
|
||||||
{
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
repository.codes.delete(code.id);
|
|
||||||
res.sendStatus(200);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/expiration', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
res.send({
|
|
||||||
max: config.code.maxExpiration,
|
|
||||||
default: config.code.defaultExpiration
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
/*
|
|
||||||
Uploads
|
|
||||||
*/
|
|
||||||
router.get('/uploads', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
var files = await repository.uploads.list(user.hasAuth(AuthTokens.ViewAllUploads) ? null : user.id);
|
|
||||||
var usernames = await repository.users.getNames();
|
|
||||||
var codedescriptions = await repository.codes.getDescriptions();
|
|
||||||
|
|
||||||
files.forEach((item) =>
|
|
||||||
{
|
|
||||||
item.username = item.userId !== null ? usernames[item.userId] : null;
|
|
||||||
item.codedescription = item.code !== null ? codedescriptions[item.code] : null;
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send(files);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.delete('/uploads/:id', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
var upload = await repository.uploads.get(req.params.id);
|
|
||||||
if (upload == null || (upload.userId !== user.id && !user.hasAuth(AuthTokens.ViewAllUploads)))
|
|
||||||
{
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await repository.uploads.delete(upload.id);
|
|
||||||
res.sendStatus(200);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/download/:fileid/:displayname', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
// TODO should we check if the user has access to the file?
|
|
||||||
// for now not that important, if you know the file's UID and are logged in
|
|
||||||
|
|
||||||
var fullpath = resolvePath(config.fileUpload.path, req.params.fileid);
|
|
||||||
res.download(fullpath, req.params.displayname);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Users
|
|
||||||
*/
|
|
||||||
router.get('/users', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
if (!user.hasAuth(AuthTokens.ManageUsers))
|
|
||||||
{
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var users = await repository.users.list();
|
|
||||||
res.send(users);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/users/:id', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
if (req.params.id !== user.id && !user.hasAuth(AuthTokens.ManageUsers))
|
|
||||||
{
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = await repository.users.get(req.params.id);
|
|
||||||
if (user === null)
|
|
||||||
{
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send(user);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.post('/users', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
var postedUser = req.body;
|
|
||||||
|
|
||||||
if (postedUser.id)
|
|
||||||
{
|
|
||||||
if (postedUser.id !== user.id && !user.hasAuth(AuthTokens.ManageUsers))
|
|
||||||
{
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await repository.users.update({
|
|
||||||
id: postedUser.id,
|
|
||||||
username: postedUser.username,
|
|
||||||
name: postedUser.name,
|
|
||||||
password: postedUser.password,
|
|
||||||
email: postedUser.email,
|
|
||||||
auth: postedUser.auth,
|
|
||||||
active: postedUser.active
|
|
||||||
});
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!user.hasAuth(AuthTokens.ManageUsers))
|
|
||||||
{
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var userId = await repository.users.insert({
|
|
||||||
username: postedUser.username,
|
|
||||||
name: postedUser.name,
|
|
||||||
password: postedUser.password,
|
|
||||||
email: postedUser.email,
|
|
||||||
auth: postedUser.auth,
|
|
||||||
active: postedUser.active,
|
|
||||||
createdByUserId: postedUser.createdByUserId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send(userId);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
router.delete('/users/:id', asyncHandler(async (req, res) =>
|
|
||||||
{
|
|
||||||
await checkAuthorization(req, res, repository, async (user) =>
|
|
||||||
{
|
|
||||||
if (!user.hasAuth(AuthTokens.ManageUsers))
|
|
||||||
{
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
repository.users.delete(req.params.id);
|
|
||||||
res.sendStatus(200);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
return router;
|
|
||||||
}
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
const asyncHandler = require('express-async-handler');
|
||||||
|
const AuthTokens = require('../../authtokens');
|
||||||
|
const ExpirationUnits = require('../../expirationunits');
|
||||||
|
const NotificationType = require('../../repository/notification').NotificationType;
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = (repository, router) =>
|
||||||
|
{
|
||||||
|
router.get('/codes', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var codes = await repository.codes.list(req.user.hasAuth(AuthTokens.ViewAllCodes) ? null : req.user.id);
|
||||||
|
var usernames = await repository.users.getNames();
|
||||||
|
|
||||||
|
codes.forEach((item) =>
|
||||||
|
{
|
||||||
|
item.username = usernames[item.userId];
|
||||||
|
});
|
||||||
|
|
||||||
|
res.send(codes);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.get('/codes/:id', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var code = await repository.codes.get(req.params.id);
|
||||||
|
if (code === null || (code.userId !== req.user.id && !req.user.hasAuth(AuthTokens.ViewAllCodes)))
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await repository.users.get(code.userId);
|
||||||
|
if (user !== null)
|
||||||
|
code.username = user.name;
|
||||||
|
|
||||||
|
res.send(code);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.post('/codes', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var postedCode = req.body;
|
||||||
|
|
||||||
|
|
||||||
|
if (config.code.maxExpiration !== null)
|
||||||
|
{
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
|
if (ExpirationUnits.apply(postedCode.expiration) > ExpirationUnits.apply(config.code.maxExpiration))
|
||||||
|
{
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (postedCode.id)
|
||||||
|
{
|
||||||
|
var code = await repository.codes.get(postedCode.id);
|
||||||
|
if (code === null || (code.userId !== req.user.id && !req.user.hasAuth(AuthTokens.ViewAllCodes)))
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await repository.codes.update({
|
||||||
|
id: postedCode.id,
|
||||||
|
expiration: postedCode.expiration,
|
||||||
|
description: postedCode.description,
|
||||||
|
message: postedCode.message
|
||||||
|
});
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var codeId = await repository.codes.insert({
|
||||||
|
userId: req.user.id,
|
||||||
|
created: postedCode.created || new Date(),
|
||||||
|
expiration: postedCode.expiration,
|
||||||
|
description: postedCode.description,
|
||||||
|
message: postedCode.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(codeId);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.delete('/codes/:id', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var code = await repository.codes.get(req.params.id);
|
||||||
|
if (code == null || (code.userId !== req.user.id && !req.user.hasAuth(AuthTokens.ViewAllCodes)))
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.codes.delete(code.id);
|
||||||
|
res.sendStatus(200);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.get('/expiration', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
res.send({
|
||||||
|
max: config.code.maxExpiration,
|
||||||
|
default: config.code.defaultExpiration
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.post('/assign/code', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var postedCode = req.body;
|
||||||
|
|
||||||
|
var code = await repository.codes.get(postedCode.id);
|
||||||
|
if (code === null || (code.userId !== req.user.id && !req.user.hasAuth(AuthTokens.ViewAllCodes)))
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code.userId !== postedCode.userId)
|
||||||
|
{
|
||||||
|
var target = await repository.users.get(postedCode.userId);
|
||||||
|
if (target === null)
|
||||||
|
{
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await repository.codes.move(postedCode.id, postedCode.userId);
|
||||||
|
await repository.uploads.move(postedCode.id, postedCode.userId);
|
||||||
|
|
||||||
|
await repository.notifications.insert({
|
||||||
|
userId: postedCode.userId,
|
||||||
|
codeId: postedCode.id,
|
||||||
|
type: NotificationType.CodeMoved,
|
||||||
|
metadata: {
|
||||||
|
prevUserId: code.userId,
|
||||||
|
assignUserId: req.user.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.get('/assign/users', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var users = await repository.users.list();
|
||||||
|
if (users === null)
|
||||||
|
{
|
||||||
|
res.send([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let assignableUsers = _.map(_.filter(users,
|
||||||
|
(user) => { return user.active }),
|
||||||
|
(user) => { return {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
name: user.name
|
||||||
|
}} );
|
||||||
|
|
||||||
|
res.send(assignableUsers);
|
||||||
|
}));
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
const express = require('express');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
|
||||||
|
async function checkAuthorization(req, res, repository, onVerified)
|
||||||
|
{
|
||||||
|
var token;
|
||||||
|
|
||||||
|
if (req.headers.authorization)
|
||||||
|
{
|
||||||
|
if (req.headers.authorization.split(' ')[0] !== 'Bearer')
|
||||||
|
{
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = req.headers.authorization.split(' ')[1];
|
||||||
|
}
|
||||||
|
else if (req.cookies && req.cookies.adminToken)
|
||||||
|
{
|
||||||
|
token = req.cookies.adminToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jwt.verify(token, config.jwtSecret, async (err, decoded) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded.userId)
|
||||||
|
{
|
||||||
|
var user = await repository.users.get(decoded.userId);
|
||||||
|
if (user === null || !user.active)
|
||||||
|
{
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
await onVerified(user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
res.sendStatus(400);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
console.log(e);
|
||||||
|
res.sendStatus(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = (repository) =>
|
||||||
|
{
|
||||||
|
var router = express.Router();
|
||||||
|
router.use(async (req, res, next) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await checkAuthorization(req, res, repository, (user) =>
|
||||||
|
{
|
||||||
|
req.user = user;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
require('./status')(repository, router);
|
||||||
|
require('./codes')(repository, router);
|
||||||
|
require('./uploads')(repository, router);
|
||||||
|
require('./users')(repository, router);
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
const asyncHandler = require('express-async-handler');
|
||||||
|
const disk = require('diskusage');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = (repository, router) =>
|
||||||
|
{
|
||||||
|
router.get('/whoami', (req, res) =>
|
||||||
|
{
|
||||||
|
res.send({
|
||||||
|
userId: req.user.id,
|
||||||
|
username: req.user.username,
|
||||||
|
auth: req.user.auth
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.get('/diskspace', (req, res) =>
|
||||||
|
{
|
||||||
|
disk.check(config.fileUpload.path, (err, info) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
res.sendStatus(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(info);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
const asyncHandler = require('express-async-handler');
|
||||||
|
const AuthTokens = require('../../authtokens');
|
||||||
|
const resolvePath = require('resolve-path');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = (repository, router) =>
|
||||||
|
{
|
||||||
|
router.get('/uploads', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var files = await repository.uploads.list(req.user.hasAuth(AuthTokens.ViewAllUploads) ? null : req.user.id);
|
||||||
|
var usernames = await repository.users.getNames();
|
||||||
|
var codedescriptions = await repository.codes.getDescriptions();
|
||||||
|
|
||||||
|
files.forEach((item) =>
|
||||||
|
{
|
||||||
|
item.username = item.userId !== null ? usernames[item.userId] : null;
|
||||||
|
item.codedescription = item.codeId !== null ? codedescriptions[item.codeId] : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.send(files);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.delete('/uploads/:id', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var upload = await repository.uploads.get(req.params.id);
|
||||||
|
if (upload == null || (upload.userId !== req.user.id && !req.user.hasAuth(AuthTokens.ViewAllUploads)))
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await repository.uploads.delete(upload.id);
|
||||||
|
res.sendStatus(200);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.delete('/codeuploads/:code', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var uploads = await repository.uploads.listForCode(req.params.code);
|
||||||
|
if (uploads === null)
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.user.hasAuth(AuthTokens.ViewAllUploads))
|
||||||
|
{
|
||||||
|
for (let i = 0; i < uploads.length; i++)
|
||||||
|
{
|
||||||
|
if (uploads[i].userId !== req.user.id)
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < uploads.length; i++)
|
||||||
|
await repository.uploads.delete(uploads[i].id);
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.get('/download/:fileid/:displayname', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
// TODO should we check if the user has access to the file?
|
||||||
|
// for now not that important, if you know the file's UID and are logged in
|
||||||
|
|
||||||
|
var fullpath = resolvePath(config.fileUpload.path, req.params.fileid);
|
||||||
|
res.download(fullpath, req.params.displayname);
|
||||||
|
}));
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
const asyncHandler = require('express-async-handler');
|
||||||
|
const AuthTokens = require('../../authtokens');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = (repository, router) =>
|
||||||
|
{
|
||||||
|
router.get('/users', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
if (!req.user.hasAuth(AuthTokens.ManageUsers))
|
||||||
|
{
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = await repository.users.list();
|
||||||
|
res.send(users);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.get('/users/:id', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
if (req.params.id !== req.user.id && !req.user.hasAuth(AuthTokens.ManageUsers))
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await repository.users.get(req.params.id);
|
||||||
|
if (user === null)
|
||||||
|
{
|
||||||
|
res.sendStatus(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(user);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.post('/users', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
var postedUser = req.body;
|
||||||
|
|
||||||
|
if (postedUser.id)
|
||||||
|
{
|
||||||
|
if (postedUser.id !== req.user.id && !req.user.hasAuth(AuthTokens.ManageUsers))
|
||||||
|
{
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await repository.users.update({
|
||||||
|
id: postedUser.id,
|
||||||
|
username: postedUser.username,
|
||||||
|
name: postedUser.name,
|
||||||
|
password: postedUser.password,
|
||||||
|
email: postedUser.email,
|
||||||
|
auth: postedUser.auth,
|
||||||
|
active: postedUser.active
|
||||||
|
});
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!req.user.hasAuth(AuthTokens.ManageUsers))
|
||||||
|
{
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = await repository.users.insert({
|
||||||
|
username: postedUser.username,
|
||||||
|
name: postedUser.name,
|
||||||
|
password: postedUser.password,
|
||||||
|
email: postedUser.email,
|
||||||
|
auth: postedUser.auth,
|
||||||
|
active: postedUser.active,
|
||||||
|
createdByUserId: postedUser.createdByUserId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(userId);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
router.delete('/users/:id', asyncHandler(async (req, res) =>
|
||||||
|
{
|
||||||
|
if (!req.user.hasAuth(AuthTokens.ManageUsers))
|
||||||
|
{
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.users.delete(req.params.id);
|
||||||
|
res.sendStatus(200);
|
||||||
|
}));
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ module.exports = (repository) =>
|
||||||
if (code !== null)
|
if (code !== null)
|
||||||
{
|
{
|
||||||
jwt.sign({
|
jwt.sign({
|
||||||
code: req.body.code,
|
codeId: req.body.code,
|
||||||
codeUserId: code.userId,
|
codeUserId: code.userId,
|
||||||
codeExpirationTime: code.expirationDate !== null ? code.expirationDate.getTime() : null
|
codeExpirationTime: code.expirationDate !== null ? code.expirationDate.getTime() : null
|
||||||
}, config.jwtSecret, (err, token) =>
|
}, config.jwtSecret, (err, token) =>
|
||||||
|
|
|
@ -4,6 +4,7 @@ const jwt = require('jsonwebtoken');
|
||||||
const resolvePath = require('resolve-path');
|
const resolvePath = require('resolve-path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
|
const NotificationType = require('../repository/notification').NotificationType;
|
||||||
|
|
||||||
|
|
||||||
async function checkAuthorization(req, res, onVerified)
|
async function checkAuthorization(req, res, onVerified)
|
||||||
|
@ -25,7 +26,7 @@ async function checkAuthorization(req, res, onVerified)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decoded.code)
|
if (decoded.codeId)
|
||||||
await onVerified(decoded);
|
await onVerified(decoded);
|
||||||
else
|
else
|
||||||
res.sendStatus(400);
|
res.sendStatus(400);
|
||||||
|
@ -106,16 +107,17 @@ module.exports = (repository, tusServer) =>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uploadId = await repository.uploads.insert(
|
var uploadId = await repository.uploads.insert({
|
||||||
decoded.codeUserId,
|
userId: decoded.codeUserId,
|
||||||
decoded.code,
|
codeId: decoded.codeId,
|
||||||
req.body.files,
|
files: req.body.files,
|
||||||
decoded.codeExpirationTime !== null ? new Date(decoded.codeExpirationTime) : null
|
expirationDate: 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,
|
||||||
|
type: NotificationType.UploadComplete
|
||||||
});
|
});
|
||||||
|
|
||||||
res.send({ id: uploadId });
|
res.send({ id: uploadId });
|
||||||
|
|
|
@ -20,6 +20,10 @@ class Code
|
||||||
self.description = values.description || null;
|
self.description = values.description || null;
|
||||||
self.message = values.message || null;
|
self.message = values.message || null;
|
||||||
self.messageHTML = values.messageHTML || null;
|
self.messageHTML = values.messageHTML || null;
|
||||||
|
self.history = values.history || [{
|
||||||
|
userId: self.userId,
|
||||||
|
date: self.created
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,15 +59,23 @@ class CodeRepository
|
||||||
if ((await self.get(codeId)) !== null)
|
if ((await self.get(codeId)) !== null)
|
||||||
throw new Error('Code ' + codeId + ' already exists');
|
throw new Error('Code ' + codeId + ' already exists');
|
||||||
|
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
self.store.insert({
|
self.store.insert({
|
||||||
_id: codeId,
|
_id: codeId,
|
||||||
userId: code.userId,
|
userId: code.userId,
|
||||||
created: code.created || new Date(),
|
created: code.created || now,
|
||||||
expiration: code.expiration,
|
expiration: code.expiration,
|
||||||
expirationDate: ExpirationUnits.apply(code.expiration, code.created),
|
expirationDate: ExpirationUnits.apply(code.expiration, code.created),
|
||||||
description: code.description,
|
description: code.description,
|
||||||
message: code.message,
|
message: code.message,
|
||||||
messageHTML: self.getMessageHTML(code.message)
|
messageHTML: self.getMessageHTML(code.message),
|
||||||
|
history: [
|
||||||
|
{
|
||||||
|
userId: code.userId,
|
||||||
|
date: code.created || now
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
return codeId;
|
return codeId;
|
||||||
|
@ -107,6 +119,42 @@ class CodeRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
move(codeId, userId)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) =>
|
||||||
|
{
|
||||||
|
self.store.update({ _id: codeId }, {
|
||||||
|
$set: {
|
||||||
|
userId: userId
|
||||||
|
},
|
||||||
|
$push: {
|
||||||
|
history: {
|
||||||
|
userId: userId,
|
||||||
|
date: new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err, numAffected) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numAffected == 0)
|
||||||
|
{
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
list(userId)
|
list(userId)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -180,13 +228,13 @@ class CodeRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
delete(code)
|
delete(codeId)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
{
|
{
|
||||||
self.store.remove({ _id: code }, (err, numRemoved) =>
|
self.store.remove({ _id: codeId }, (err, numRemoved) =>
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
const map = require('lodash/map');
|
const map = require('lodash/map');
|
||||||
|
|
||||||
|
|
||||||
|
const NotificationType = {
|
||||||
|
UploadComplete: 'uploadComplete',
|
||||||
|
CodeMoved: 'codeMoved'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Notification
|
class Notification
|
||||||
{
|
{
|
||||||
constructor(values)
|
constructor(values)
|
||||||
|
@ -8,9 +14,12 @@ class Notification
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.id = values.id || values._id || null;
|
self.id = values.id || values._id || null;
|
||||||
self.userId = values.userId || null;
|
|
||||||
self.uploadId = values.uploadId || null;
|
self.uploadId = values.uploadId || null;
|
||||||
|
self.codeId = values.codeId || null;
|
||||||
|
self.userId = values.userId || null;
|
||||||
|
self.type = values.type || NotificationType.UploadComplete;
|
||||||
self.attempt = values.attempt || 0;
|
self.attempt = values.attempt || 0;
|
||||||
|
self.metadata = values.metadata || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +61,11 @@ class NotificationRepository
|
||||||
{
|
{
|
||||||
self.store.insert({
|
self.store.insert({
|
||||||
userId: notification.userId,
|
userId: notification.userId,
|
||||||
|
codeId: notification.codeId,
|
||||||
uploadId: notification.uploadId,
|
uploadId: notification.uploadId,
|
||||||
attempt: notification.attempt
|
attempt: notification.attempt,
|
||||||
|
type: notification.type,
|
||||||
|
metadata: notification.metadata
|
||||||
}, (err, dbNotification) =>
|
}, (err, dbNotification) =>
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -118,6 +130,7 @@ class NotificationRepository
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
NotificationType,
|
||||||
Notification,
|
Notification,
|
||||||
NotificationRepository
|
NotificationRepository
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@ class Upload
|
||||||
|
|
||||||
self.id = values.id || values._id || null;
|
self.id = values.id || values._id || null;
|
||||||
self.userId = values.userId || null;
|
self.userId = values.userId || null;
|
||||||
self.code = values.code || null;
|
self.codeId = values.codeId || null;
|
||||||
self.created = values.created || new Date();
|
self.created = values.created || new Date();
|
||||||
self.expirationDate = values.expirationDate || null;
|
self.expirationDate = values.expirationDate || null;
|
||||||
self.files = values.files || [];
|
self.files = values.files || [];
|
||||||
|
@ -54,25 +54,25 @@ class UploadRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
insert(userId, code, files, expirationDate)
|
insert(upload)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
{
|
{
|
||||||
var upload = {
|
let insertUpload = {
|
||||||
created: new Date(),
|
created: upload.created || new Date(),
|
||||||
userId: userId,
|
userId: upload.userId,
|
||||||
code: code,
|
codeId: upload.codeId,
|
||||||
expirationDate: expirationDate,
|
expirationDate: upload.expirationDate,
|
||||||
files: map(filter(files,
|
files: map(filter(upload.files,
|
||||||
(file) => file.hasOwnProperty('id') && file.hasOwnProperty('name')),
|
(file) => file.hasOwnProperty('id') && file.hasOwnProperty('name')),
|
||||||
(file) => { return { id: file.id, name: file.name, size: file.size } })
|
(file) => { return { id: file.id, name: file.name, size: file.size } })
|
||||||
};
|
};
|
||||||
|
|
||||||
if (upload.files.length)
|
if (insertUpload.files.length)
|
||||||
{
|
{
|
||||||
self.store.insert(upload, (err, dbUpload) =>
|
self.store.insert(insertUpload, (err, dbUpload) =>
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
|
@ -114,6 +114,29 @@ class UploadRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
listForCode(codeId)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) =>
|
||||||
|
{
|
||||||
|
self.store.find({ codeId: codeId }, (err, docs) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(docs.map((dbUpload) =>
|
||||||
|
{
|
||||||
|
return new Upload(dbUpload);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
get(uploadId)
|
get(uploadId)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -134,6 +157,36 @@ class UploadRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
move(codeId, userId)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) =>
|
||||||
|
{
|
||||||
|
self.store.update({ codeId: codeId }, {
|
||||||
|
$set: {
|
||||||
|
userId: userId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err, numAffected) =>
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numAffected == 0)
|
||||||
|
{
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
delete(uploadId)
|
delete(uploadId)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
|
@ -5,6 +5,32 @@ const fs = require('mz/fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const getPaths = require('get-paths');
|
const getPaths = require('get-paths');
|
||||||
const AbstractIntervalWorker = require('./abstractintervalworker');
|
const AbstractIntervalWorker = require('./abstractintervalworker');
|
||||||
|
const NotificationType = require('../repository/notification').NotificationType;
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
|
||||||
|
function humanFileSize(bytes, si)
|
||||||
|
{
|
||||||
|
var thresh = si ? 1000 : 1024;
|
||||||
|
if(Math.abs(bytes) < thresh)
|
||||||
|
{
|
||||||
|
return bytes + ' B';
|
||||||
|
}
|
||||||
|
|
||||||
|
var units = si
|
||||||
|
? ['kB','MB','GB','TB','PB','EB','ZB','YB']
|
||||||
|
: ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
|
||||||
|
var u = -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bytes /= thresh;
|
||||||
|
++u;
|
||||||
|
}
|
||||||
|
while(Math.abs(bytes) >= thresh && u < units.length - 1);
|
||||||
|
|
||||||
|
return bytes.toFixed(1) + ' ' + units[u];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class NotificationWorker extends AbstractIntervalWorker
|
class NotificationWorker extends AbstractIntervalWorker
|
||||||
|
@ -24,7 +50,7 @@ class NotificationWorker extends AbstractIntervalWorker
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
{
|
{
|
||||||
async.eachOfSeries(notifications,
|
async.eachOfSeries(notifications,
|
||||||
async (item) => { await self.sendNotification(item) },
|
async (item) => { await self.handleNotification(item) },
|
||||||
(err) =>
|
(err) =>
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -36,17 +62,104 @@ class NotificationWorker extends AbstractIntervalWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async sendNotification(notification)
|
async handleNotification(notification)
|
||||||
{
|
{
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
let user = await self.repository.users.get(notification.userId);
|
let user = await self.repository.users.get(notification.userId);
|
||||||
if (user === null || !user.email)
|
if (user === null || !user.email)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let upload = await self.repository.uploads.get(notification.uploadId);
|
switch (notification.type)
|
||||||
if (upload === null)
|
{
|
||||||
|
case NotificationType.UploadComplete:
|
||||||
|
await self.sendUploadNotification(notification, user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NotificationType.CodeMoved:
|
||||||
|
await self.sendCodeMovedNotification(notification, user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getDefaultLocals(user)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
// Data
|
||||||
|
user: user,
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
adminUrl: config.notifications.adminUrl,
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
humanFileSize: humanFileSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async sendUploadNotification(notification, user)
|
||||||
|
{
|
||||||
|
let self = this;
|
||||||
|
let locals = self.getDefaultLocals(user);
|
||||||
|
|
||||||
|
locals.upload = await self.repository.uploads.get(notification.uploadId);
|
||||||
|
if (locals.upload === null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
await self.sendNotification(notification, user, locals, 'uploadnotification');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async sendCodeMovedNotification(notification, user)
|
||||||
|
{
|
||||||
|
let self = this;
|
||||||
|
let locals = self.getDefaultLocals(user);
|
||||||
|
|
||||||
|
locals.uploads = await self.repository.uploads.listForCode(notification.codeId);
|
||||||
|
if (locals.uploads === null || locals.uploads.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
locals.fileCount = 0;
|
||||||
|
_.forEach(locals.uploads, (upload) =>
|
||||||
|
{
|
||||||
|
locals.fileCount += upload.files.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
locals.prevUser = {
|
||||||
|
userId: null,
|
||||||
|
name: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
if (notification.metadata !== null)
|
||||||
|
{
|
||||||
|
let resolveUser = async (userId) =>
|
||||||
|
{
|
||||||
|
if (!userId)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let user = await self.repository.users.get(userId);
|
||||||
|
if (user !== null)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
userId: userId,
|
||||||
|
name: user.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
locals.prevUser = await resolveUser(notification.metadata.prevUserId);
|
||||||
|
locals.assignUser = await resolveUser(notification.metadata.assignUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await self.sendNotification(notification, user, locals, 'movednotification');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async sendNotification(notification, user, locals, template)
|
||||||
|
{
|
||||||
|
let self = this;
|
||||||
|
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
{
|
{
|
||||||
const email = new Email({
|
const email = new Email({
|
||||||
|
@ -99,24 +212,21 @@ class NotificationWorker extends AbstractIntervalWorker
|
||||||
|
|
||||||
email
|
email
|
||||||
.send({
|
.send({
|
||||||
template: 'uploadnotification',
|
template: template,
|
||||||
message: {
|
message: {
|
||||||
to: user.email
|
to: user.email
|
||||||
},
|
},
|
||||||
locals: {
|
locals: locals
|
||||||
user: user,
|
|
||||||
upload: upload,
|
|
||||||
adminUrl: config.notifications.adminUrl
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.then(async () =>
|
.then(async () =>
|
||||||
{
|
{
|
||||||
await self.repository.notifications.delete(notification.id);
|
await self.repository.notifications.delete(notification.id);
|
||||||
console.log('Notification sent to: ' + user.email + ' for upload ID: ' + upload.id);
|
console.log('Notification sent to: ' + user.email);
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch(async (error) =>
|
.catch(async (error) =>
|
||||||
{
|
{
|
||||||
|
console.log(error);
|
||||||
notification.attempt++;
|
notification.attempt++;
|
||||||
if (notification.attempt > config.notifications.maxAttempts)
|
if (notification.attempt > config.notifications.maxAttempts)
|
||||||
await self.repository.notifications.delete(notification.id);
|
await self.repository.notifications.delete(notification.id);
|
||||||
|
|
|
@ -3132,11 +3132,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
||||||
"integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
|
"integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
|
||||||
},
|
},
|
||||||
"deepmerge": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-Q89Z26KAfA3lpPGhbF6XMfYAm3jIV3avViy6KOJ2JLzFbeWHOvPQUu5aSJIWXap3gDZC2y1eF5HXEPI2wGqgvw=="
|
|
||||||
},
|
|
||||||
"define-properties": {
|
"define-properties": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"cookie-parser": "^1.4.3",
|
"cookie-parser": "^1.4.3",
|
||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
"deepmerge": "^2.1.0",
|
|
||||||
"diskusage": "^0.2.4",
|
"diskusage": "^0.2.4",
|
||||||
"ejs": "^2.5.9",
|
"ejs": "^2.5.9",
|
||||||
"email-templates": "^3.6.0",
|
"email-templates": "^3.6.0",
|
||||||
|
|
|
@ -191,6 +191,6 @@ a
|
||||||
|
|
||||||
.confirmDelete
|
.confirmDelete
|
||||||
{
|
{
|
||||||
color: red;
|
color: red !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -16,6 +16,8 @@ import merge from 'lodash/merge';
|
||||||
import faSpinner from '@fortawesome/fontawesome-free-solid/faSpinner';
|
import faSpinner from '@fortawesome/fontawesome-free-solid/faSpinner';
|
||||||
import faBan from '@fortawesome/fontawesome-free-solid/faBan';
|
import faBan from '@fortawesome/fontawesome-free-solid/faBan';
|
||||||
import faTrashAlt from '@fortawesome/fontawesome-free-solid/faTrashAlt';
|
import faTrashAlt from '@fortawesome/fontawesome-free-solid/faTrashAlt';
|
||||||
|
import faUser from '@fortawesome/fontawesome-free-solid/faUser';
|
||||||
|
|
||||||
|
|
||||||
if (typeof customMessages !== 'undefined')
|
if (typeof customMessages !== 'undefined')
|
||||||
{
|
{
|
||||||
|
@ -32,7 +34,7 @@ if (typeof customMessages !== 'undefined')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fontawesome.library.add(faSpinner, faBan, faTrashAlt);
|
fontawesome.library.add(faSpinner, faBan, faTrashAlt, faUser);
|
||||||
Vue.component('fa', FontAwesomeIcon);
|
Vue.component('fa', FontAwesomeIcon);
|
||||||
|
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
|
|
|
@ -67,7 +67,10 @@ export default {
|
||||||
created: 'Date',
|
created: 'Date',
|
||||||
code: 'Code',
|
code: 'Code',
|
||||||
owner: 'Owner',
|
owner: 'Owner',
|
||||||
userDeleted: 'deleted'
|
userDeleted: 'deleted',
|
||||||
|
|
||||||
|
assign: 'Change owner',
|
||||||
|
assignApply: 'Apply'
|
||||||
},
|
},
|
||||||
|
|
||||||
codes: {
|
codes: {
|
||||||
|
|
|
@ -67,8 +67,11 @@ export default {
|
||||||
created: 'Datum',
|
created: 'Datum',
|
||||||
code: 'Code',
|
code: 'Code',
|
||||||
owner: 'Eigenaar',
|
owner: 'Eigenaar',
|
||||||
userDeleted: 'verwijderd'
|
userDeleted: 'verwijderd',
|
||||||
},
|
|
||||||
|
assign: 'Verander eigenaar',
|
||||||
|
assignApply: 'Toepassen'
|
||||||
|
},
|
||||||
|
|
||||||
codes: {
|
codes: {
|
||||||
add: 'Genereer code',
|
add: 'Genereer code',
|
||||||
|
|
|
@ -1,34 +1,46 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="uploads">
|
<div id="uploads">
|
||||||
<div v-if="uploads !== null" class="list">
|
<div v-if="uploads !== null" class="list">
|
||||||
<div v-for="upload in uploads">
|
<div v-for="group in groupedUploads">
|
||||||
<div class="properties">
|
<template v-for="(upload, index) in group.uploads">
|
||||||
<div class="pure-g">
|
<div class="properties" v-if="index == 0">
|
||||||
<div class="pure-u-1-1"><span class="text codedescription">{{ upload.codedescription || upload.code }}</span></div>
|
<div class="pure-g">
|
||||||
<div class="pure-u-1-3"><span class="text">{{ upload.code }}</span></div>
|
<div class="pure-u-1-1"><span class="text codedescription">{{ upload.codedescription || upload.codeId }}</span></div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3"><span class="text">{{ upload.codeId }}</span></div>
|
||||||
<span class="text" v-if="hasAuth('viewAllUploads')" :class="{ userDeleted: !upload.username }">
|
<div class="pure-u-1-3">
|
||||||
{{ upload.username || $t('admin.uploads.userDeleted') }}
|
<span class="text" v-if="hasAuth('viewAllUploads')" :class="{ userDeleted: !upload.username }">
|
||||||
</span>
|
{{ upload.username || $t('admin.uploads.userDeleted') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1-3 right"><span class="text">{{ upload.created | formatDateTime }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3 right"><span class="text">{{ upload.created | formatDateTime }}</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pure-menu pure-menu-horizontal">
|
<div class="pure-menu pure-menu-horizontal" v-if="index == 0">
|
||||||
<ul class="pure-menu-list">
|
<ul class="pure-menu-list">
|
||||||
<li class="pure-menu-item" v-if="confirmDelete == upload.id"><a href="#" class="pure-menu-link" @click.prevent="cancelDelete"><fa icon="ban"></fa> {{ $t('admin.cancel') }}</a></li>
|
<li class="pure-menu-item"><a href="#" class="pure-menu-link" @click.prevent="showAssign = !showAssign"><fa icon="user"></fa> {{ $t('admin.uploads.assign') }}</a></li>
|
||||||
<li class="pure-menu-item"><a href="#" class="pure-menu-link" :class="{ confirmDelete: confirmDelete == upload.id }" @click.prevent="deleteClick(upload.id)"><fa icon="trash-alt"></fa> {{ $t('admin.delete') }}</a></li>
|
<li class="pure-menu-item" v-if="confirmDelete == upload.codeId"><a href="#" class="pure-menu-link" @click.prevent="cancelDelete"><fa icon="ban"></fa> {{ $t('admin.cancel') }}</a></li>
|
||||||
</ul>
|
<li class="pure-menu-item"><a href="#" class="pure-menu-link" :class="{ confirmDelete: confirmDelete == upload.codeId }" @click.prevent="deleteClick(upload.codeId)"><fa icon="trash-alt"></fa> {{ $t('admin.delete') }}</a></li>
|
||||||
</div>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="file" v-for="file in upload.files" :title="file.name">
|
<div v-if="showAssign" class="assign pure-form pure-form-horizontal">
|
||||||
<a :href="getDownloadUrl(file)">
|
<select v-model="assignUser">
|
||||||
<img :src="getFileIconUrl(file.name)" class="icon">
|
<option v-for="user in assignUsers" v-if="user.id !== upload.userId" :value="user.id">{{ user.name }}</option>
|
||||||
<span class="filename">{{ file.name }}</span>
|
</select>
|
||||||
<span class="size">{{ file.size | formatSizeSI }}</span>
|
|
||||||
</a>
|
<button class="pure-button pure-button-primary" @click.prevent="assignCode(upload.codeId)">{{ $t('admin.uploads.assignApply') }}</button>
|
||||||
</div>
|
<button class="pure-button" @click.prevent="showAssign = false">{{ $t('admin.cancel') }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="file" v-for="file in upload.files" :title="file.name">
|
||||||
|
<a :href="getDownloadUrl(file)">
|
||||||
|
<img :src="getFileIconUrl(file.name)" class="icon">
|
||||||
|
<span class="filename">{{ file.name }}</span>
|
||||||
|
<span class="size">{{ file.size | formatSizeSI }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="uploads.length == 0" class="nodata">
|
<div v-if="uploads.length == 0" class="nodata">
|
||||||
|
@ -45,6 +57,7 @@
|
||||||
<script>
|
<script>
|
||||||
import orderBy from 'lodash/orderBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import findIndex from 'lodash/findIndex';
|
import findIndex from 'lodash/findIndex';
|
||||||
|
import forEach from 'lodash/forEach';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import shared from '../../shared';
|
import shared from '../../shared';
|
||||||
|
|
||||||
|
@ -53,7 +66,10 @@ export default {
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
uploads: null,
|
uploads: null,
|
||||||
confirmDelete: null
|
confirmDelete: null,
|
||||||
|
showAssign: false,
|
||||||
|
assignUser: null,
|
||||||
|
assignUsers: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -78,8 +94,47 @@ export default {
|
||||||
self.uploads = orderBy(response.data, ['created'], ['desc']);
|
self.uploads = orderBy(response.data, ['created'], ['desc']);
|
||||||
})
|
})
|
||||||
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
||||||
|
|
||||||
|
|
||||||
|
axios.get('/admin/assign/users', {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
{
|
||||||
|
self.assignUsers = orderBy(response.data, ['name'], ['asc']);
|
||||||
|
})
|
||||||
|
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
groupedUploads()
|
||||||
|
{
|
||||||
|
let self = this;
|
||||||
|
let codes = {};
|
||||||
|
|
||||||
|
// Get unique codes and their highest upload time
|
||||||
|
forEach(self.uploads, (upload) =>
|
||||||
|
{
|
||||||
|
if (!codes.hasOwnProperty(upload.codeId))
|
||||||
|
{
|
||||||
|
codes[upload.codeId] = {
|
||||||
|
maxCreated: upload.created,
|
||||||
|
uploads: [upload]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// uploads is sorted by created descending, so no need to update maxCreated
|
||||||
|
codes[upload.codeId].uploads.push(upload);
|
||||||
|
});
|
||||||
|
|
||||||
|
return orderBy(codes, ['maxCreated'], ['desc']);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
hasAuth(token)
|
hasAuth(token)
|
||||||
{
|
{
|
||||||
|
@ -110,29 +165,26 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
deleteClick(uploadId)
|
deleteClick(codeId)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (self.confirmDelete == uploadId)
|
if (self.confirmDelete == codeId)
|
||||||
{
|
{
|
||||||
self.confirmDelete = null;
|
self.confirmDelete = null;
|
||||||
|
|
||||||
axios.delete('/admin/uploads/' + encodeURIComponent(uploadId), {
|
axios.delete('/admin/codeuploads/' + encodeURIComponent(codeId), {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + shared.adminToken
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
}})
|
}})
|
||||||
.then((response) =>
|
.then((response) =>
|
||||||
{
|
{
|
||||||
var index = findIndex(self.uploads, (item) => { return item.id == uploadId; });
|
|
||||||
if (index > -1)
|
|
||||||
self.uploads.splice(index, 1);
|
|
||||||
})
|
})
|
||||||
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
self.confirmDelete = uploadId;
|
self.confirmDelete = codeId;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -140,6 +192,72 @@ export default {
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
self.confirmDelete = null;
|
self.confirmDelete = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
assignCode(codeId)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
let userId = self.assignUser;
|
||||||
|
if (!userId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self.assignUser = null;
|
||||||
|
self.showAssign = false;
|
||||||
|
|
||||||
|
axios.post('/admin/assign/code', {
|
||||||
|
id: codeId,
|
||||||
|
userId: userId
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
{
|
||||||
|
if (self.hasAuth('viewAllUploads'))
|
||||||
|
{
|
||||||
|
let username = null;
|
||||||
|
forEach(self.assignUsers, (user) =>
|
||||||
|
{
|
||||||
|
if (user.id == userId)
|
||||||
|
username = user.name;
|
||||||
|
})
|
||||||
|
|
||||||
|
self.updateCodeUser(codeId, userId, username);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
self.removeCodeUploads(codeId);
|
||||||
|
})
|
||||||
|
.catch((error) => { shared.$emit('apiError', error, this.$router) })
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
removeCodeUploads(codeId)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
for (let i = self.uploads.length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (self.uploads[i].codeId == codeId)
|
||||||
|
self.uploads.splice(i, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
updateCodeUser(codeId, userId, username)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
forEach(self.uploads, (upload) =>
|
||||||
|
{
|
||||||
|
if (upload.codeId == codeId)
|
||||||
|
{
|
||||||
|
upload.userId = userId;
|
||||||
|
upload.username = username;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,4 +325,10 @@ export default {
|
||||||
{
|
{
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.assign
|
||||||
|
{
|
||||||
|
margin-top: .5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
Loading…
Reference in New Issue