...
 
Commits (35)
{
"plugins": ["syntax-dynamic-import"]
"plugins": [
"syntax-dynamic-import",
"transform-es2015-arrow-functions",
"transform-es2015-shorthand-properties",
"transform-es2015-block-scoping"
]
}
\ No newline at end of file
node_modules
data
custom/*.js
public/dist
custom/*
public/dist/*.js
public/dist/*.map
public/dist/index.html
config.js
*.sublime-workspace
\ No newline at end of file
*.sublime-workspace
npm-debug.log
\ No newline at end of file
### Installation
Copy ```config.defaults.js``` to ```config.js``` and change as required. Any settings left out will use the value from ```config.defaults.js```.
To build the production version:
```bash
npm run build
```
#### Service on Linux (systemd)
/etc/systemd/system/recv.service:
```ini
[Unit]
Description=Recv
[Service]
ExecStart=/usr/bin/node /srv/recv/index.js
WorkingDirectory=/srv/recv
Restart=always
RestartSec=10 # Restart service after 10 seconds if node service crashes
StandardOutput=syslog # Output to syslog
StandardError=syslog # Output to syslog
SyslogIdentifier=recv
User=recv
[Install]
WantedBy=multi-user.target
```
```bash
systemctl enable recv.service
systemctl start recv.service
```
\ No newline at end of file
const path = require('path');
const ExpirationUnits = require('./lib/expirationunits');
/*
Copy this file and rename it to 'config.js' to customize the configuration.
This will ensure your changes are not overwritten during updates.
Any settings not configured in your own 'config.js' will use the default
defined in 'config.defaults.js'.
*/
module.exports = {
port: 3000,
nodeModulesPath: path.join(__dirname, 'node_modules'),
database: {
path: path.join(__dirname, 'data'),
autocompactionInterval: 60000
},
fileUpload: {
path: path.join(__dirname, 'data', 'files'),
url: '/files'
},
jwtSecret: 'change me to a random generated string',
code: {
// Use https://alex7kom.github.io/nano-nanoid-cc/ to check for the chance of collisions.
// If a collision occurs, a new code will be generated and tested again for up to 100 times.
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/',
mail: {
from: '"Recv" <recv@localhost>',
transport: {
// passed directly to NodeMailer's createTransport. See nodemailer.com for more information.
host: 'smtp.ethereal.email',
port: 587,
secure: false,
auth: {
user: 'recv',
pass: 'test'
}
}
}
}
};
\ No newline at end of file
const path = require('path');
module.exports = {
port: 3000,
nodeModulesPath: path.join(__dirname, 'node_modules'),
userDatabasePath: path.join(__dirname, 'data', 'users'),
fileUploadPath: path.join(__dirname, 'data', 'files'),
fileUploadPublicPath: '/files',
jwtSecret: 'change me to a random generated string'
};
\ No newline at end of file
<% 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>
\ No newline at end of file
📄 <%=fileCount%> file<%=fileCount != 1 ? 's have' : ' has'%> been assigned to you
\ No newline at end of file
<p>Hello <%=user.name%>,</p>
<p>The following files have been uploaded:</p>
<ul>
<% upload.files.forEach((file) => { %>
<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>
\ No newline at end of file
📄 <%=upload.files.count%> file<%=upload.files.count != 1 ? 's' : ''%> uploaded
\ No newline at end of file
'use strict'
const fs = require('fs');
const _ = require('lodash');
const config = require('./config');
const JsonUserDatabase = require('./lib/JsonUserDatabase');
const _ = require('lodash');
let configDefaults = require('./config.defaults');
if (fs.existsSync('./config.js'))
{
let configChanges = require('./config');
global.config = _.merge(configDefaults, configChanges);
}
else
global.config = configDefaults;
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');
const tus = require('tus-node-server');
const jwt = require('jsonwebtoken');
const path = require('path');
const resolvePath = require('resolve-path');
const cookieParser = require('cookie-parser');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('./webpack.config.js');
/*
function metadataToObject(stringValue)
{
const keyValuePairList = stringValue.split(',');
return _.reduce(keyValuePairList , (metadata, keyValuePair) => {
let [key, base64Value] = keyValuePair.split(' ');
metadata[key] = new Buffer(base64Value, "base64").toString("ascii");
return metadata;
}, {});
}
*/
const webpackConfigFactory = require('./webpack.config.js');
function checkAuthorization(req, res, onVerified)
(async () =>
{
if (!req.headers.authorization || req.headers.authorization.split(' ')[0] !== 'Bearer')
{
res.sendStatus(400);
return;
}
var token = req.headers.authorization.split(' ')[1];
jwt.verify(token, config.jwtSecret, (err, decoded) =>
try
{
if (err)
{
res.sendStatus(403);
return;
}
const isDevelopment = process.env.NODE_ENV !== 'production';
console.log('Running in ' + (isDevelopment ? 'development' : 'production') + ' mode');
onVerified(decoded);
});
}
const repository = new Repository(config.database);
await repository.load();
const tusServer = new tus.Server();
tusServer.datastore = new tus.FileStore({
path: config.fileUpload.url,
directory: config.fileUpload.path
});
(async function()
{
const isDevelopment = process.env.NODE_ENV !== 'production';
const app = express();
const userDatabase = new JsonUserDatabase(config.userDatabasePath);
await userDatabase.load();
app.disable('x-powered-by');
const tusServer = new tus.Server();
tusServer.datastore = new tus.FileStore({
path: config.fileUploadPublicPath,
directory: config.fileUploadPath
});
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
/*
tusServer.on(tus.EVENTS.EVENT_UPLOAD_COMPLETE, (event) =>
{
console.log(event);
const metadata = metadataToObject(event.file.upload_metadata);
jwt.verify(metadata.token, config.jwtSecret, (err, decoded) =>
{
if (err)
return;
const filePath = path.join(config.fileUploadPath, event.file.id);
console.log(filePath);
app.use('/', require('./lib/api/upload')(repository, tusServer));
app.use('/token', require('./lib/api/token')(repository));
app.use('/admin', require('./lib/api/admin')(repository));
// TODO save metadata for file and notify people
});
});
*/
// Frontend
if (isDevelopment)
{
const webpackConfig = webpackConfigFactory(process.env, { mode: 'development' });
const compiler = webpack(webpackConfig);
const app = express();
app.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath
}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(webpackHotMiddleware(compiler));
}
// Token API
app.post('/token/upload', async (req, res) =>
{
if (!req.body.code)
// Automatic fallback support for file icons
app.get('/images/fileicons/:format/:filename', (req, res) =>
{
res.sendStatus(400);
return;
}
var basePath;
var filePath;
if (await userDatabase.isValidCode(req.body.code))
{
jwt.sign({
code: req.body.code
}, config.jwtSecret, (err, token) =>
try
{
basePath = resolvePath('./public/dist/images/fileicons/', req.params.format);
filePath = resolvePath(basePath, req.params.filename);
}
catch (err)
{
res.sendStatus(404);
}
fs.stat(filePath, (err, stat) =>
{
if (err)
res.sendStatus(500);
res.sendFile(resolvePath(basePath, '_blank.png'));
else
res.send(token);
res.sendFile(filePath);
});
}
else
res.sendStatus(403);
});
});
// Upload API
app.post('/complete', (req, res) =>
{
if (!req.body.files)
{
res.sendStatus(400);
return;
}
app.use(express.static(path.join(__dirname, 'custom')));
app.use(express.static(path.join(__dirname, 'public', 'dist')));
checkAuthorization(req, res, (decoded) =>
{
console.log(req.body.files);
// TODO save set
});
});
// Redirect to make Vue-router URLs less quirky
app.get('/c/:code', (req, res) => { res.redirect(301, '/#/c/' + req.params.code) });
// Tus upload
const uploadApp = express();
uploadApp.all('*', (req, res) =>
{
checkAuthorization(req, res, (decoded) =>
{
tusServer.handle(req, res);
});
});
app.use('/upload', uploadApp);
// Background workers
var notificationWorker = new NotificationWorker(repository);
notificationWorker.start(config.notifications.interval * 1000);
var expirationWorker = new ExpirationWorker(repository);
expirationWorker.start(config.cleanupInterval * 1000);
// Frontend
if (isDevelopment)
var server = app.listen(config.port, () => console.log('Recv running on port ' + server.address().port));
}
catch (e)
{
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath
}));
app.use(webpackHotMiddleware(compiler));
console.log(e);
process.exit(1);
}
app.use(express.static(path.join(__dirname, 'public', 'dist')));
var server = app.listen(config.port, () => console.log('Recv running on port ' + server.address().port));
})();
\ No newline at end of file
const debug = require('debug')('recv:JsonUserDatabase');
const uuidv4 = require('uuid/v4');
const fs = require('mz/fs');
const mkdir = require('mkdir-promise');
const path = require('path');
class JsonUserDatabase
{
constructor(path)
{
this.path = path;
this.codes = {};
this.users = {};
}
async load()
{
debug('loading database from ' + this.path);
try
{
var files = await fs.readdir(this.path);
}
catch(err)
{
if (err.code == 'ENOENT')
// Path does not exist, no worries
files = null;
else
throw err;
}
if (!files)
return;
for (var i = 0; i < files.length; i++)
{
var fullPath = path.join(this.path, files[i]);
var stats = await fs.lstat(fullPath);
if (stats.isFile())
{
debug('loading ' + fullPath);
try
{
var userInfo = JSON.parse(await fs.readFile(fullPath));
if (userInfo.type !== 'user')
throw new Error('unsupported file type: ' + userInfo.type);
this.users[userInfo.uid] = userInfo;
}
catch (err)
{
console.error('error while loading file ' + fullPath + ', skipped:');
console.error(err);
}
}
}
debug(Object.keys(this.users).length + ' user(s) loaded');
}
addUser(info)
{
var userId = uuidv4();
// TODO add user
// TODO save file
return userId;
}
isValidCode(code)
{
debug('validating code: ' + code);
// TODO check code
var valid = true;
debug('valid = ' + valid);
return valid;
}
}
module.exports = JsonUserDatabase;
\ No newline at end of file
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);
}));
}
\ No newline at end of file
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();
// Redirects to make Vue-router URLs less quirky
router.get('/', (req, res) => { res.redirect(301, '/#/admin/') });
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;
}
\ No newline at end of file
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);
});
});
}
\ No newline at end of file
const asyncHandler = require('express-async-handler');
const AuthTokens = require('../../authtokens');
const resolvePath = require('resolve-path');
const groupBy = require('lodash/groupBy');
const map = require('lodash/map');
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('/fileuploads/', asyncHandler(async (req, res) =>
{
var groupedFiles = groupBy(req.body, (value) => value.uploadId);
for (var uploadId in groupedFiles)
{
await repository.uploads.deleteFiles(uploadId,
map(groupedFiles[uploadId], (file) => { return file.fileId }));
}
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);
}));
}
\ No newline at end of file
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);
}));
}
\ No newline at end of file
const express = require('express');
const asyncHandler = require('express-async-handler');
const jwt = require('jsonwebtoken');
module.exports = (repository) =>
{
var router = express.Router();
router.post('/upload', asyncHandler(async (req, res) =>
{
if (!req.body.code)
{
res.sendStatus(400);
return;
}
var code = await repository.codes.get(req.body.code);
if (code !== null)
{
jwt.sign({
codeId: req.body.code,
codeUserId: code.userId
}, config.jwtSecret, (err, token) =>
{
if (err)
res.sendStatus(500);
else
res.send(token);
});
}
else
res.sendStatus(403);
}));
router.post('/login', asyncHandler(async (req, res) =>
{
if (!req.body.username || !req.body.password)
{
res.sendStatus(400);
return;
}
var user = await repository.users.getLoginUser(req.body.username, req.body.password);
if (user !== null)
{
jwt.sign({
userId: user.id
}, config.jwtSecret, (err, token) =>
{
if (err)
res.sendStatus(500);
else
res.send(token);
});
}
else
res.sendStatus(403);
}));
return router;
}
\ No newline at end of file
const express = require('express');
const asyncHandler = require('express-async-handler');
const jwt = require('jsonwebtoken');
const resolvePath = require('resolve-path');
const fs = require('fs');
const async = require('async');
const NotificationType = require('../repository/notification').NotificationType;
async function checkAuthorization(req, res, onVerified)
{
if (!req.headers.authorization || req.headers.authorization.split(' ')[0] !== 'Bearer')
{
res.sendStatus(400);
return;
}
var token = req.headers.authorization.split(' ')[1];
jwt.verify(token, config.jwtSecret, async (err, decoded) =>
{
try
{
if (err)
{
res.sendStatus(403);
return;
}
if (decoded.codeId)
await onVerified(decoded);
else
res.sendStatus(400);
}
catch (e)
{
console.log(e);
res.sendStatus(500);
}
});
}
module.exports = (repository, tusServer) =>
{
var router = express.Router();
// Upload API
router.get('/info/:code', asyncHandler(async (req, res) =>
{
var code = await repository.codes.get(req.params.code);
if (code === null)
{
res.sendStatus(404);
return;
}
let info = {
message: null,
expirationDate: code.expirationDate,
expiration: code.expiration
};
if (code.messageHTML)
{
var user = await repository.users.get(code.userId);
var name = user !== null ? user.name : null;
info.message = {
name: name,
message: code.messageHTML
};
}
res.send(info);
}));
router.post('/complete', asyncHandler(async (req, res) =>
{
if (!req.body.files)
{
res.sendStatus(400);
return;
}
await checkAuthorization(req, res, async (decoded) =>
{
async.each(req.body.files, (item, callback) =>
{
if (!item.id)
{
callback();
return;
}
var fullpath = resolvePath(config.fileUpload.path, item.id);
fs.stat(fullpath, (err, stats) =>
{
item.size = stats.size;
callback();
});
},
async (err) =>
{
if (err)
{
res.sendStatus(500);
return;
}
let expirationDate = await repository.codes.getUploadExpirationDate(decoded.codeId);
var uploadId = await repository.uploads.insert({
userId: decoded.codeUserId,
codeId: decoded.codeId,
files: req.body.files,
expirationDate: expirationDate
});
await repository.notifications.insert({
userId: decoded.codeUserId,
uploadId: uploadId,