All basic Auth and upload/download functions. No editing nor deleting
This commit is contained in:
parent
37853cc8b4
commit
bae570232c
13 changed files with 446 additions and 14 deletions
|
@ -1,8 +1,25 @@
|
|||
var express = require("express");
|
||||
var router = express.Router();
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
//Routers
|
||||
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;
|
||||
|
|
91
Server/API/Routers/AccountRouter.js
Normal file
91
Server/API/Routers/AccountRouter.js
Normal 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;
|
182
Server/API/Routers/FrameRouter.js
Normal file
182
Server/API/Routers/FrameRouter.js
Normal 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;
|
|
@ -17,4 +17,19 @@ module.exports = {
|
|||
|
||||
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
50
Server/Schemas/Account.js
Normal 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);
|
|
@ -9,4 +9,4 @@ const AppVersion = new Schema({
|
|||
uploaded: Date
|
||||
});
|
||||
|
||||
module.exports = AppVersion
|
||||
module.exports = mongoose.model('AppVersion', AppVersion);
|
||||
|
|
13
Server/Schemas/Frame.js
Normal file
13
Server/Schemas/Frame.js
Normal 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
14
Server/Schemas/Photo.js
Normal 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
12
Server/Schemas/User.js
Normal 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
34
Server/Utils/AuthUtil.js
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,20 +5,19 @@ const mongoose = require('mongoose');
|
|||
|
||||
mongoose.connect(Config.mongoURL);
|
||||
|
||||
const Schemas = {}
|
||||
const Models = {}
|
||||
|
||||
var normalizedPath = require("path").join(__dirname, "..", "Schemas");
|
||||
|
||||
// Load All Schemas and put them in Shemas Obj
|
||||
require("fs").readdirSync(normalizedPath).forEach(function(file) {
|
||||
Schemas[file.split('.')[0]] = require(normalizedPath + '/' + file);
|
||||
});
|
||||
require("fs")
|
||||
.readdirSync(normalizedPath)
|
||||
.forEach((file) => {
|
||||
Models[file.split('.')[0]] = require(normalizedPath + '/' + file);
|
||||
});
|
||||
|
||||
const util = {
|
||||
getSomething: () => {
|
||||
|
||||
},
|
||||
Schemas: Schemas
|
||||
Models: Models
|
||||
}
|
||||
|
||||
module.exports = util;
|
||||
|
|
5
Server/Utils/TokenGenerator.js
Normal file
5
Server/Utils/TokenGenerator.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
const uuidv4 = require('uuid/v4');
|
||||
|
||||
module.exports = {
|
||||
generate: ()=>uuidv4()
|
||||
}
|
Loading…
Reference in a new issue