Implemented NeDB based repository

This commit is contained in:
Mark van Renswoude 2018-03-20 17:22:56 +01:00
parent 50f83b47d1
commit 1febe41516
6 changed files with 240 additions and 144 deletions

View File

@ -4,9 +4,16 @@ module.exports = {
port: 3000,
nodeModulesPath: path.join(__dirname, 'node_modules'),
userDatabasePath: path.join(__dirname, 'data', 'users'),
fileUploadPath: path.join(__dirname, 'data', 'files'),
fileUploadPublicPath: '/files',
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'
};

View File

@ -1,7 +1,7 @@
'use strict'
const config = require('./config');
const JsonUserDatabase = require('./lib/JsonUserDatabase');
const Repository = require('./lib/repository');
const _ = require('lodash');
const express = require('express');
@ -15,20 +15,6 @@ 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;
}, {});
}
*/
function checkAuthorization(req, res, onVerified)
{
@ -56,35 +42,15 @@ function checkAuthorization(req, res, onVerified)
{
const isDevelopment = process.env.NODE_ENV !== 'production';
const userDatabase = new JsonUserDatabase(config.userDatabasePath);
await userDatabase.load();
const repository = new Repository(config.database);
const tusServer = new tus.Server();
tusServer.datastore = new tus.FileStore({
path: config.fileUploadPublicPath,
directory: config.fileUploadPath
path: config.fileUpload.url,
directory: config.fileUpload.path
});
/*
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);
// TODO save metadata for file and notify people
});
});
*/
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
@ -100,10 +66,12 @@ function checkAuthorization(req, res, onVerified)
return;
}
if (await userDatabase.isValidCode(req.body.code))
var userId = await repository.findCodeUserId(req.body.code);
if (userId !== null)
{
jwt.sign({
code: req.body.code
code: req.body.code,
userId: userId
}, config.jwtSecret, (err, token) =>
{
if (err)
@ -126,10 +94,12 @@ function checkAuthorization(req, res, onVerified)
return;
}
checkAuthorization(req, res, (decoded) =>
checkAuthorization(req, res, async (decoded) =>
{
console.log(req.body.files);
// TODO save set
console.log('1');
var uploadId = await repository.addUpload(decoded.userId, req.body.files);
console.log(uploadId);
res.send({ id: uploadId });
});
});

View File

@ -1,91 +0,0 @@
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;

208
lib/repository.js Normal file
View File

@ -0,0 +1,208 @@
//const debug = require('debug')('recv:users');
const _ = require('lodash');
const path = require('path');
const mkdirp = require('mkdirp');
const bcrypt = require('bcrypt');
const Datastore = require('nedb');
const shortid = require('shortid');
const retry = require('async-retry')
class Repository
{
constructor(config)
{
var self = this;
const initStore = (filename) =>
{
var store = new Datastore({
filename: path.join(config.path, filename),
autoload: true
});
store.persistence.setAutocompactionInterval(config.autocompactionInterval);
return store;
};
mkdirp(config.path, (err) =>
{
if (err)
{
console.log(err);
return;
}
self.db = {
users: initStore('users.db'),
codes: initStore('codes.db'),
uploads: initStore('uploads.db')
};
// Initialize database if empty
self.db.users.count({}, (err, count) =>
{
if (err)
{
console.log(err);
return;
}
if (count == 0)
self.addUser('admin', null, 'test');
});
});
}
async isValidUser(username, password)
{
var self = this;
return new Promise((resolve, reject) =>
{
self.db.users.findOne({ username: username }, (err, doc) =>
{
if (err)
{
reject(err);
return;
}
if (doc == null)
{
resolve(false);
return;
}
bcrypt.compare(password, doc.password, (err, res) =>
{
if (err)
reject(err)
else
resolve(res);
});
});
});
}
async addUser(username, email, password)
{
var self = this;
return new Promise((resolve, reject) =>
{
bcrypt.hash(password, 10, function(err, hash)
{
if (err)
{
reject(err);
return;
}
var user = {
username: username,
email: email,
password: hash
};
self.db.users.insert(user, (err, dbUser) =>
{
if (err)
{
reject(err);
return;
}
resolve(dbUser._id);
});
});
});
}
async findCodeUserId(code)
{
var self = this;
return new Promise((resolve, reject) =>
{
self.db.codes.findOne({ _id: code }, (err, doc) =>
{
if (err)
{
reject(err);
return;
}
resolve(doc !== null ? doc.userId : null);
});
});
}
async addCode(userId, expiration)
{
var self = this;
return await retry(async bail =>
{
var code = shortid.generate();
if ((await self.findCodeUserId(code)) !== null)
throw new Error('Code ' + code + ' already exists');
self.db.codes.insert({
_id: code,
userId: userId,
expiration: expiration
})
return code;
}, {
retries: 100,
minTimeout: 0,
maxTimeout: 0
});
}
async addUpload(userId, files)
{
var self = this;
return new Promise((resolve, reject) =>
{
var upload = {
files: _.map(_.filter(files,
(file) => file.hasOwnProperty('id') && file.hasOwnProperty('name')),
(file) => { return { id: file.id, name: file.name } })
};
console.log(upload);
if (upload.files.length)
{
self.db.uploads.insert(upload, (err, dbUpload) =>
{
console.log(dbUpload);
if (err)
{
reject(err);
return;
}
resolve(dbUpload._id);
});
}
else
{
reject();
}
});
}
}
module.exports = Repository;

View File

@ -15,16 +15,18 @@
"author": "Mark van Renswoude <mark@x2software.net>",
"license": "Unlicense",
"dependencies": {
"async-retry": "^1.2.1",
"bcrypt": "^1.0.3",
"body-parser": "^1.18.2",
"debug": "^3.1.0",
"express": "^4.16.2",
"jsonwebtoken": "^8.1.1",
"mkdir-promise": "^1.0.0",
"mz": "^2.7.0",
"lodash": "^4.17.5",
"mkdirp": "^0.5.1",
"nedb": "^1.8.0",
"npm": "^5.7.1",
"tus-node-server": "^0.2.10",
"uuid": "^3.2.1",
"lodash": "^4.17.5"
"shortid": "^2.2.8",
"tus-node-server": "^0.2.10"
},
"devDependencies": {
"axios": "^0.18.0",

View File

@ -50,7 +50,7 @@ export default {
self.code.checking = true;
axios.post('/token/upload', {
code: self.code
code: self.code.value
})
.then((response) =>
{