All basic Auth and upload/download functions. No editing nor deleting

This commit is contained in:
Sagi Dayan 2018-04-28 17:44:33 +03:00
parent 37853cc8b4
commit bae570232c
13 changed files with 446 additions and 14 deletions

4
.gitignore vendored
View file

@ -4,7 +4,7 @@
### Linux ### ### Linux ###
*~ *~
# temporary files which can be created if a process still has a handle open of a # temporary files which can be created if a process still has a handle open of a
deleted file deleted file
.fuse_hidden* .fuse_hidden*
@ -67,7 +67,7 @@ coverage
# nyc test coverage # nyc test coverage
.nyc_output .nyc_output
# Grunt intermediate storage # Grunt intermediate storage
(http://gruntjs.com/creating-plugins#storing-task-files) (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt .grunt

View file

@ -1,8 +1,25 @@
var express = require("express"); const express = require("express");
var router = express.Router(); const router = express.Router();
const bodyParser = require('body-parser');
//Routers //Routers
const UpdateRouter = require('./Routers/UpdateRouter'); const UpdateRouter = require('./Routers/UpdateRouter');
router.use('/update', UpdateRouter); const AccountRouter = require('./Routers/AccountRouter');
const FrameRouter = require('./Routers/FrameRouter');
// parse application/x-www-form-urlencoded
router.use(bodyParser.urlencoded({ extended: true }))
// parse application/json
router.use(bodyParser.json())
// bodyParser.parse('image/*') = function(req, options, next) {
// req.image = req.
// // for your needs it will probably be this:
// next();
// }
router.use('/update', UpdateRouter);
router.use('/account', AccountRouter)
router.use('/frame', FrameRouter)
module.exports = router; module.exports = router;

View file

@ -0,0 +1,91 @@
const express = require("express");
const DBUtils = require('../../Utils/DBUtil');
const Config = require('../../Config/Config');
const router = express.Router();
router.post('/create/', (req, res) => {
const body = req.body;
if (!body.username || !body.password) {
res.status(400).json({
message: 'username and password are required'
});
} else if (body.username.length < Config.validators.account.username_min_length) {
res.status(400).json({
message: `username must be at least ${Config.validators.account.username_min_length} chars long`
});
} else if (body.password.length < Config.validators.account.password_min_length) {
res.status(400).json({
message: `password must be at least ${Config.validators.account.password_min_length} chars long`
});
} else {
// Create a new Account - status 201
// create a user a new user
const account = new DBUtils.Models.Account({
username: body.username,
password: body.password,
});
account.save((err, doc) => {
if (err) {
res.status(400).json({
message: "Failed to save account in DB, username taken"
})
} else {
// create an empty user object
const user = new DBUtils.Models.User({
account_id: account._id,
nickname: account.username
});
user.save((err, doc) => {
if (err) {
//TODO delete the created account....
res.status(400).json({
message: "Failed to save account in DB, username taken"
}); //FIXME - Lies!!!
} else {
res.status(201).json({
user: user,
token: account.auth_token
});
}
})
}
});
}
});
router.post('/login/', (req, res) => {
const body = req.body;
DBUtils.Models.Account.findOne({
username: body.username
}, (err, account) => {
if (err) throw err;
if (account) {
// test a matching password
account.comparePassword(body.password, account.password, (err, isMatch) => {
if (err) throw err;
if (!isMatch) {
res.status(401).json({
message: 'Authentication Fail'
});
return;
}
res.json({
token: account.auth_token
});
});
} else {
res.status(401).json({
message: 'Authentication Fail'
});
}
});
});
module.exports = router;

View file

@ -0,0 +1,182 @@
const express = require("express");
const DBUtils = require('../../Utils/DBUtil')
const AuthUtil = require('../../Utils/AuthUtil')
const bodyParser = require('body-parser');
const router = express.Router();
router.use(bodyParser.raw({
uploadDir: '/tmp/uploads',
keepExtensions: true,
limit: '5mb',
type: 'image/*'
}))
router.get('/:frameId', (req, res) => { // by Frame Id
const token = req.get('token');
const frameId = req.params.frameId;
AuthUtil.getAccountByToken(token)
.then((account) => {
if(account.frames.indexOf(frameId) >= 0) {
DBUtils.Models.Frame.findOne({_id: frameId}, (err, doc) => {
if(err) {
res.status(400).json({
message: err.message
});
return;
} else if(!doc) {
res.status(400).json({
message: 'Unable to find a Frame with id: ' + frameId
});
return;
}
const frame = doc.toObject();
/// lets get all images ids...
DBUtils.Models.Photo.find({frame_id: frameId}, (err, docs)=>{
if(err) {
res.status(400).json({
message: err.message
});
return;
}
console.log(docs);
frame.photos = docs.map(p => p._id);
res.json(frame);
})
});
}else{
res.status(403).json({
message: 'Account has no access to frame with id of: ' + frameId
});
}
})
.catch((reason) => {
res.status(401).json({
message: reason
});
})
})
router.post('/create', (req, res) => {
const token = req.get('token');
const body = req.body;
if(!body.name) {
res.status(400).json({
message: 'Must provide a name for your new frame'
});
return;
}
AuthUtil.getAccountByToken(token)
.then((account)=>{
// If account valid - create new frame
const frame = new DBUtils.Models.Frame({
name: body.name,
admin: account._id,
members: [account._id]
});
// save frame
frame.save((err, doc)=>{
if(err) {
res.status(400).json({
message: err.message
});
return;
}
// frame created - now add its id to the account object
account.frames.push(doc._id);
account.save((err)=>{
if( err ) throw err;
res.status(201).json(frame);
})
})
})
.catch((reason)=>{
res.status(401).json({
message: reason
});
})
});
router.post('/:frameId/upload/photo', (req, res) => {
const token = req.get('token');
AuthUtil.getAccountByToken(token)
.then(account => {
const frameId = req.params.frameId;
if(account.frames.indexOf(frameId) >= 0) {
// User can upload image to the frame
AuthUtil.getUserByAccountId(account._id)
.then((user)=>{
// Upload Photo...
const photo = new DBUtils.Models.Photo({
frame_id: frameId,
photo: req.body,
timestamp: Date.now(),
contentType: req.get('Content-Type'),
user:user.id});
// Save photo
photo.save((err) => {
if(err) {
res.status(400).json({
message: err.message
});
return;
}
res.status(201).json(photo)
});
})
.catch(reason => {
res.status(500).json({
message: 'Unexpected error: ' + reason
});
})
} else {
res.status(403).json({
message: 'Account has no access to frame with id of: ' + frameId
});
}
})
.catch(reason => {
res.status(401).json({
message: reason
});
})
});
router.get('/:frameId/download/photo/:photoId', (req, res) => {
const token = req.get('token');
const photoId = req.params.photoId;
const frameId = req.params.frameId;
AuthUtil.getAccountByToken(token)
.then((account) => {
if(account.frames.indexOf(frameId) >= 0) {
DBUtils.Models.Photo.findOne({_id: photoId}, (err, doc) => {
if(err) {
res.status(400).json({
message: err.message
});
return;
}
if(doc) res.contentType(doc.contentType).send(doc.photo);
else res.status(400).json({
message: 'Photo not found'
});
});
} else {
res.status(403).json({
message: 'Account has no access to frame with id of: ' + photoId
});
}
})
.catch(reason => {
res.status(401).json({
message: reason
});
})
})
module.exports = router;

View file

@ -17,4 +17,19 @@ module.exports = {
mongoURL: `mongodb://${DBAuth.username}:${DBAuth.password}@ds159489.mlab.com:59489/framez_db`, mongoURL: `mongodb://${DBAuth.username}:${DBAuth.password}@ds159489.mlab.com:59489/framez_db`,
defaults: {
user: {
avatar: 'http://www.top-madagascar.com/assets/images/admin/user-admin.png'
}
},
validators: {
account: {
password_min_length: 8,
username_min_length: 3
}
},
// Password hash
salt_work_factor: 10
}; };

50
Server/Schemas/Account.js Normal file
View file

@ -0,0 +1,50 @@
const Config = require('../Config/Config')
const TokenGen = require('../Utils/TokenGenerator');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const Schema = mongoose.Schema;
const SALT_WORK_FACTOR = Config.salt_work_factor;
const Account = new Schema({
username: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true },
auth_token: {type: String, require: false, index: { unique: true }},
frames: {type: [Schema.ObjectId], default: []}
});
Account.pre('save', function(next) {
var user = this;
// only geerate auth_token if it was modified (or is new)
if(!user.auth_token){
user.auth_token = TokenGen.generate()
}
// only hash the password if it has been modified (or is new)
if (user.isModified('password')){
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
} else {
next()
}
});
Account.methods.comparePassword = (password, hash,cb) => {
bcrypt.compare(password, hash, (err, isMatch) => {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('Account', Account);

View file

@ -9,4 +9,4 @@ const AppVersion = new Schema({
uploaded: Date uploaded: Date
}); });
module.exports = AppVersion module.exports = mongoose.model('AppVersion', AppVersion);

13
Server/Schemas/Frame.js Normal file
View file

@ -0,0 +1,13 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Frame = new Schema({
name: String,
icon: {type: String, default: 'https://image.flaticon.com/icons/png/128/847/847930.png'},
admin: Schema.ObjectId,
members: {type: [Schema.ObjectId], default: []},
tv_clients: {type: [Schema.ObjectId], default: []},
});
module.exports = mongoose.model('Frame', Frame);

14
Server/Schemas/Photo.js Normal file
View file

@ -0,0 +1,14 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Photo = new Schema({
frame_id: Schema.ObjectId,
contentType: String,
user: Schema.ObjectId,
photo: Buffer,
// thumbnail: Buffer,
timestamp: Number
});
module.exports = mongoose.model('Photo', Photo);

12
Server/Schemas/User.js Normal file
View file

@ -0,0 +1,12 @@
const mongoose = require('mongoose');
const Config = require('../Config/Config')
const Schema = mongoose.Schema;
const User = new Schema({
account_id: Schema.ObjectId,
nickname: { type: String, required: true, index: { unique: true } },
avatar: { type: String, default: Config.defaults.user.avatar },
auth_token: {type: String, default: null}
});
module.exports = mongoose.model('User', User);

34
Server/Utils/AuthUtil.js Normal file
View file

@ -0,0 +1,34 @@
const DBUtils = require('./DBUtil');
module.exports = {
getAccountByToken: (token) => {
return new Promise((resolve, reject) => {
if(!token){
reject('Must provide token');
return;
}
DBUtils.Models.Account.findOne({auth_token:token}, (err, account)=>{
if(err) {
reject('Invalid token');
return;
}
resolve(account);
});
})
},
getUserByAccountId: (id) => {
return new Promise((resolve, reject) => {
if(!id) {
reject('No account id provided');
return;
}
DBUtils.Models.User.findOne({account_id: id}, (err, doc) => {
if(err) {
reject('Unable to find user');
return;
}
resolve(doc);
});
});
}
}

View file

@ -5,20 +5,19 @@ const mongoose = require('mongoose');
mongoose.connect(Config.mongoURL); mongoose.connect(Config.mongoURL);
const Schemas = {} const Models = {}
var normalizedPath = require("path").join(__dirname, "..", "Schemas"); var normalizedPath = require("path").join(__dirname, "..", "Schemas");
// Load All Schemas and put them in Shemas Obj // Load All Schemas and put them in Shemas Obj
require("fs").readdirSync(normalizedPath).forEach(function(file) { require("fs")
Schemas[file.split('.')[0]] = require(normalizedPath + '/' + file); .readdirSync(normalizedPath)
}); .forEach((file) => {
Models[file.split('.')[0]] = require(normalizedPath + '/' + file);
});
const util = { const util = {
getSomething: () => { Models: Models
},
Schemas: Schemas
} }
module.exports = util; module.exports = util;

View file

@ -0,0 +1,5 @@
const uuidv4 = require('uuid/v4');
module.exports = {
generate: ()=>uuidv4()
}