Implemented NeDB based repository
This commit is contained in:
parent
50f83b47d1
commit
1febe41516
|
@ -4,9 +4,16 @@ module.exports = {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
|
|
||||||
nodeModulesPath: path.join(__dirname, 'node_modules'),
|
nodeModulesPath: path.join(__dirname, 'node_modules'),
|
||||||
userDatabasePath: path.join(__dirname, 'data', 'users'),
|
|
||||||
fileUploadPath: path.join(__dirname, 'data', 'files'),
|
database: {
|
||||||
fileUploadPublicPath: '/files',
|
path: path.join(__dirname, 'data'),
|
||||||
|
autocompactionInterval: 60000
|
||||||
|
},
|
||||||
|
|
||||||
|
fileUpload: {
|
||||||
|
path: path.join(__dirname, 'data', 'files'),
|
||||||
|
url: '/files'
|
||||||
|
},
|
||||||
|
|
||||||
jwtSecret: 'change me to a random generated string'
|
jwtSecret: 'change me to a random generated string'
|
||||||
};
|
};
|
58
index.js
58
index.js
|
@ -1,7 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
const JsonUserDatabase = require('./lib/JsonUserDatabase');
|
const Repository = require('./lib/repository');
|
||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
@ -15,20 +15,6 @@ const webpackDevMiddleware = require('webpack-dev-middleware');
|
||||||
const webpackHotMiddleware = require('webpack-hot-middleware');
|
const webpackHotMiddleware = require('webpack-hot-middleware');
|
||||||
const webpackConfig = require('./webpack.config.js');
|
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)
|
function checkAuthorization(req, res, onVerified)
|
||||||
{
|
{
|
||||||
|
@ -56,35 +42,15 @@ function checkAuthorization(req, res, onVerified)
|
||||||
{
|
{
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
const userDatabase = new JsonUserDatabase(config.userDatabasePath);
|
const repository = new Repository(config.database);
|
||||||
await userDatabase.load();
|
|
||||||
|
|
||||||
const tusServer = new tus.Server();
|
const tusServer = new tus.Server();
|
||||||
tusServer.datastore = new tus.FileStore({
|
tusServer.datastore = new tus.FileStore({
|
||||||
path: config.fileUploadPublicPath,
|
path: config.fileUpload.url,
|
||||||
directory: config.fileUploadPath
|
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();
|
const app = express();
|
||||||
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
@ -100,10 +66,12 @@ function checkAuthorization(req, res, onVerified)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await userDatabase.isValidCode(req.body.code))
|
var userId = await repository.findCodeUserId(req.body.code);
|
||||||
|
if (userId !== null)
|
||||||
{
|
{
|
||||||
jwt.sign({
|
jwt.sign({
|
||||||
code: req.body.code
|
code: req.body.code,
|
||||||
|
userId: userId
|
||||||
}, config.jwtSecret, (err, token) =>
|
}, config.jwtSecret, (err, token) =>
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -126,10 +94,12 @@ function checkAuthorization(req, res, onVerified)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAuthorization(req, res, (decoded) =>
|
checkAuthorization(req, res, async (decoded) =>
|
||||||
{
|
{
|
||||||
console.log(req.body.files);
|
console.log('1');
|
||||||
// TODO save set
|
var uploadId = await repository.addUpload(decoded.userId, req.body.files);
|
||||||
|
console.log(uploadId);
|
||||||
|
res.send({ id: uploadId });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
|
@ -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;
|
12
package.json
12
package.json
|
@ -15,16 +15,18 @@
|
||||||
"author": "Mark van Renswoude <mark@x2software.net>",
|
"author": "Mark van Renswoude <mark@x2software.net>",
|
||||||
"license": "Unlicense",
|
"license": "Unlicense",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"async-retry": "^1.2.1",
|
||||||
|
"bcrypt": "^1.0.3",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"jsonwebtoken": "^8.1.1",
|
"jsonwebtoken": "^8.1.1",
|
||||||
"mkdir-promise": "^1.0.0",
|
"lodash": "^4.17.5",
|
||||||
"mz": "^2.7.0",
|
"mkdirp": "^0.5.1",
|
||||||
|
"nedb": "^1.8.0",
|
||||||
"npm": "^5.7.1",
|
"npm": "^5.7.1",
|
||||||
"tus-node-server": "^0.2.10",
|
"shortid": "^2.2.8",
|
||||||
"uuid": "^3.2.1",
|
"tus-node-server": "^0.2.10"
|
||||||
"lodash": "^4.17.5"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
||||||
self.code.checking = true;
|
self.code.checking = true;
|
||||||
|
|
||||||
axios.post('/token/upload', {
|
axios.post('/token/upload', {
|
||||||
code: self.code
|
code: self.code.value
|
||||||
})
|
})
|
||||||
.then((response) =>
|
.then((response) =>
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue