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");
|
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;
|
||||||
|
|
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`,
|
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
|
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);
|
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;
|
||||||
|
|
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