Fixes/cleanings and added the frame viewer linker stuff...

This commit is contained in:
Sagi Dayan 2018-06-23 14:19:14 +03:00
parent ac65f4372c
commit 1406ecb5d0
15 changed files with 538 additions and 156 deletions

1
.gitignore vendored
View file

@ -47,6 +47,7 @@ Temporary Items
### Node ### ### Node ###
# Logs # Logs
logs logs
Logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*

View file

@ -1,12 +1,14 @@
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const Logger = require('../Utils/Logger');
//Routers //Routers
const UpdateRouter = require('./Routers/UpdateRouter'); const UpdateRouter = require('./Routers/UpdateRouter');
const AccountRouter = require('./Routers/AccountRouter'); const AccountRouter = require('./Routers/AccountRouter');
const FrameRouter = require('./Routers/FrameRouter'); const FrameRouter = require('./Routers/FrameRouter');
const UserRouter = require('./Routers/UserRouter'); const UserRouter = require('./Routers/UserRouter');
const FrameViewerRouter = require('./Routers/FrameViewerRouter');
// parse application/x-www-form-urlencoded // parse application/x-www-form-urlencoded
@ -23,5 +25,6 @@ router.use(bodyParser.json())
router.use('/update', UpdateRouter); router.use('/update', UpdateRouter);
router.use('/account', AccountRouter); router.use('/account', AccountRouter);
router.use('/frame', FrameRouter); router.use('/frame', FrameRouter);
router.use('/frameviewer', FrameViewerRouter);
router.use('/user', UserRouter); router.use('/user', UserRouter);
module.exports = router; module.exports = router;

View file

@ -1,12 +1,17 @@
const express = require("express"); const express = require("express");
const DBUtils = require('../../Utils/DBUtil'); const DBUtils = require('../../Utils/DBUtil');
const Config = require('../../Config/Config'); const Config = require('../../Config/Config');
const AuthUtil = require('../../Utils/AuthUtil');
// Logger
const Logger = require('../../Utils/Logger');
const TAG = '[AccountRouter]'
const router = express.Router(); const router = express.Router();
router.post('/create/', (req, res) => { router.post('/create/', (req, res) => {
const body = req.body; const body = req.body;
Logger.debug(TAG, 'POST: /create/');
if (!body.username || !body.password) { if (!body.username || !body.password) {
res.status(400).json({ res.status(400).json({
message: 'username and password are required' message: 'username and password are required'
@ -46,7 +51,7 @@ router.post('/create/', (req, res) => {
}); //FIXME - Lies!!! }); //FIXME - Lies!!!
} else { } else {
res.status(201).json({ res.status(201).json({
user: user, user: doc.toObject(),
token: account.auth_token token: account.auth_token
}); });
} }
@ -59,26 +64,51 @@ router.post('/create/', (req, res) => {
}); });
router.post('/login/', (req, res) => { router.post('/login/', (req, res) => {
Logger.debug(TAG, 'POST: /login/');
const body = req.body; const body = req.body;
DBUtils.Models.Account.findOne({ DBUtils.Models.Account.findOne({
username: body.username username: body.username
}, (err, account) => { }, (err, account) => {
if (err) throw err; if (err) {
if (account) { Logger.error(TAG, 'Failed to query DB. ERROR:', err);
res.status(500).json({
message: err.message
});
}
else if (account) {
// test a matching password // test a matching password
account.comparePassword(body.password, account.password, (err, isMatch) => { account.comparePassword(body.password, account.password, (err, isMatch) => {
if (err) throw err; if (err) {
if (!isMatch) { Logger.error(TAG, 'Failed to query DB. ERROR:', err);
res.status(500).json({
message: err.message
});
}
else if (!isMatch) {
Logger.warn(TAG, 'Authentication Fail');
res.status(401).json({ res.status(401).json({
message: 'Authentication Fail' message: 'Authentication Fail'
}); });
return; return;
} }else {
res.json({ AuthUtil.getUserByAccountId(account._id).then(user => {
let responseObj = {
user: user.toObject(),
token: account.auth_token token: account.auth_token
}
Logger.debug(TAG, 'Authentication success', JSON.stringify(responseObj, null, 2));
res.json(responseObj);
})
.catch(reason=>{
res.status(400).json({
message: reason
}); });
})
}
}); });
} else { } else {
Logger.warn(TAG, 'Authentication Fail');
res.status(401).json({ res.status(401).json({
message: 'Authentication Fail' message: 'Authentication Fail'
}); });

View file

@ -2,6 +2,12 @@ const express = require("express");
const DBUtils = require('../../Utils/DBUtil') const DBUtils = require('../../Utils/DBUtil')
const AuthUtil = require('../../Utils/AuthUtil') const AuthUtil = require('../../Utils/AuthUtil')
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const FrameLinker = require('../../FrameLinker/FrameLinker')
const uuidv4 = require('uuid/v4');
// Logger
const Logger = require('../../Utils/Logger');
const TAG = '[AccountRouter]'
const router = express.Router(); const router = express.Router();
@ -12,13 +18,32 @@ router.use(bodyParser.raw({
type: 'image/*' type: 'image/*'
})) }))
router.use((req, res, next) => {
const token = req.get('token');
Logger.debug(TAG, 'Auth middleware check');
AuthUtil.getAccountByToken(token)
.then(account => {
req.account = account
Logger.debug(TAG, 'Auth middleware check - Success');
next()
})
.catch((reason) => {
Logger.debug(TAG, 'Auth middleware check - Fail');
res.status(401).json({
message: reason
});
})
});
router.get('/:frameId', (req, res) => { // by Frame Id router.get('/:frameId', (req, res) => { // by Frame Id
Logger.debug(TAG, 'GET: /:frameId');
const token = req.get('token'); const token = req.get('token');
const frameId = req.params.frameId; const frameId = req.params.frameId;
AuthUtil.getAccountByToken(token) const account = req.account;
.then((account) => {
if (account.frames.indexOf(frameId) >= 0) { if (account.frames.indexOf(frameId) >= 0) {
DBUtils.Models.Frame.findOne({_id: frameId}, (err, doc) => { DBUtils.Models.Frame.findOne({
_id: frameId
}, (err, doc) => {
if (err) { if (err) {
res.status(400).json({ res.status(400).json({
message: err.message message: err.message
@ -31,16 +56,26 @@ router.get('/:frameId', (req, res) => { // by Frame Id
return; return;
} }
const frame = doc.toObject(); const frame = doc.toObject();
delete frame.viewerKeys
/// lets get all images ids... /// lets get all images ids...
DBUtils.Models.Photo.find({frame_id: frameId}, (err, docs)=>{ DBUtils.Models.Photo.find({
frame_id: frameId
})
.populate('user')
.exec((err, docs) => {
if (err) { if (err) {
res.status(400).json({ res.status(400).json({
message: err.message message: err.message
}); });
return; return;
} }
console.log(docs); frame.photos = docs.map((p) => {
frame.photos = docs.map(p => p._id); return {
photo_id: p._id,
user: p.user,
timestamp: p.timestamp
};
})
res.json(frame); res.json(frame);
}) })
}); });
@ -50,14 +85,9 @@ router.get('/:frameId', (req, res) => { // by Frame Id
}); });
} }
}) })
.catch((reason) => {
res.status(401).json({
message: reason
});
})
})
router.post('/create', (req, res) => { router.post('/create', (req, res) => {
Logger.debug(TAG, 'GET: /create');
const token = req.get('token'); const token = req.get('token');
const body = req.body; const body = req.body;
if (!body.name) { if (!body.name) {
@ -66,8 +96,7 @@ router.post('/create', (req, res) => {
}); });
return; return;
} }
AuthUtil.getAccountByToken(token) const account = req.account;
.then((account)=>{
// If account valid - create new frame // If account valid - create new frame
const frame = new DBUtils.Models.Frame({ const frame = new DBUtils.Models.Frame({
name: body.name, name: body.name,
@ -89,20 +118,13 @@ router.post('/create', (req, res) => {
res.status(201).json(frame); res.status(201).json(frame);
}) })
}) })
})
.catch((reason)=>{
res.status(401).json({
message: reason
});
})
}); });
router.post('/:frameId/upload/photo', (req, res) => { router.post('/:frameId/upload/photo', (req, res) => {
Logger.debug(TAG, 'POST: /:frameId/upload/photo');
const token = req.get('token'); const token = req.get('token');
const account = req.account;
AuthUtil.getAccountByToken(token)
.then(account => {
const frameId = req.params.frameId; const frameId = req.params.frameId;
if (account.frames.indexOf(frameId) >= 0) { if (account.frames.indexOf(frameId) >= 0) {
// User can upload image to the frame // User can upload image to the frame
@ -114,7 +136,8 @@ router.post('/:frameId/upload/photo', (req, res) => {
photo: req.body, photo: req.body,
timestamp: Date.now(), timestamp: Date.now(),
contentType: req.get('Content-Type'), contentType: req.get('Content-Type'),
user:user.id}); user: user._id
});
// Save photo // Save photo
photo.save((err) => { photo.save((err) => {
@ -137,23 +160,64 @@ router.post('/:frameId/upload/photo', (req, res) => {
message: 'Account has no access to frame with id of: ' + frameId message: 'Account has no access to frame with id of: ' + frameId
}); });
} }
})
.catch(reason => {
res.status(401).json({
message: reason
}); });
}) router.post('/:frameId/link', (req, res) => {
Logger.debug(TAG, 'POST: /:frameId/link');
const token = req.get('token');
const frameId = req.params.frameId;
const account = req.account;
const frameViewerKey = req.body.key;
if(FrameLinker.isKeyValid(frameViewerKey)){
if(account.frames.indexOf(frameId) == -1){
res.status(403).json({
message: 'Account has no access to this frame'
}); });
return;
}
// lets create an access token;
const accessToken = uuidv4();
DBUtils.Models.Frame.findOne({_id: frameId}, (err, frame)=>{
if (err) {
res.status(400).json({
message: err.message
});
return;
}
frame.viewerKeys.push(accessToken);
FrameLinker.linkFrame(frameId, accessToken ,frameViewerKey)
.then(()=>{
frame.save((err)=>{
if(err){
res.status(400).json({
message: 'Something went wrong...'
});
return;
}
res.status(201).json({message: 'Success'});
})
})
.catch((reason)=>{
res.status(400).json({message:reason});
})
})
}else{
res.status(404).json({
message: 'Unable to find a frame viewer with key: ' + frameViewerKey
});
}
});
router.get('/:frameId/download/photo/:photoId', (req, res) => { router.get('/:frameId/download/photo/:photoId', (req, res) => {
Logger.debug(TAG, 'GET: /:frameId/download/photo/:photoId');
const token = req.get('token'); const token = req.get('token');
const photoId = req.params.photoId; const photoId = req.params.photoId;
const frameId = req.params.frameId; const frameId = req.params.frameId;
const account = req.account;
AuthUtil.getAccountByToken(token)
.then((account) => {
if (account.frames.indexOf(frameId) >= 0) { if (account.frames.indexOf(frameId) >= 0) {
DBUtils.Models.Photo.findOne({_id: photoId}, (err, doc) => { DBUtils.Models.Photo.findOne({
_id: photoId
}, (err, doc) => {
if (err) { if (err) {
res.status(400).json({ res.status(400).json({
message: err.message message: err.message
@ -171,12 +235,6 @@ router.get('/:frameId/download/photo/:photoId', (req, res) => {
}); });
} }
}) })
.catch(reason => {
res.status(401).json({
message: reason
});
})
})
module.exports = router; module.exports = router;

View file

@ -0,0 +1,119 @@
const express = require("express");
const DBUtils = require('../../Utils/DBUtil');
const Config = require('../../Config/Config');
const AuthUtil = require('../../Utils/AuthUtil');
// Logger
const Logger = require('../../Utils/Logger');
const TAG = '[FrameViewerRouter]'
const router = express.Router();
router.use((req, res, next) => {
Logger.debug(TAG, 'Auth middleware');
const token = req.get('token');
Logger.debug(TAG, `Params ${JSON.stringify(req.params)}`)
const frameId = req.params.frameId;
if (!token) {
res.status(403).json({
message: 'Invalid access token'
});
} else {
next();
}
});
router.get('/frame/:frameId', (req, res) => {
Logger.debug(TAG, 'GET: /frame/:frameId');
const token = req.get('token');
const frameId = req.params.frameId;
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;
}
if(doc.viewerKeys.indexOf(token) == -1){
res.status(403).json({
message: 'Frame Viewer has no access to frame with id of: ' + frameId
});
return;
}
const frame = doc.toObject();
/// lets get all images ids...
DBUtils.Models.Photo.find({
frame_id: frameId
})
.populate('user')
.exec((err, docs) => {
if (err) {
res.status(400).json({
message: err.message
});
return;
}
frame.photos = docs.map((p) => {
return {
photo_id: p._id,
user: p.user,
timestamp: p.timestamp
};
})
res.json(frame);
})
});
});
router.get('/frame/:frameId/photo/:photoId', (req, res) => {
Logger.debug(TAG, 'GET: /frame/:frameId/photo/:photoId');
const token = req.get('token');
const photoId = req.params.photoId;
const frameId = req.params.frameId;
DBUtils.Models.Frame.findOne({
_id: frameId
}, (err, frame) => {
if (err) {
res.status(500).json({
message: 'DB error'
})
return;
}
if (frame) {
if (frame.viewerKeys.indexOf(token) != -1) {
req.frame = frame;
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: 'Frame Viewer has no access to frame with id of: ' + frameId
});
}
}else{
res.status(404).json({
message: 'Unable to find frame with id of ' + frameId
});
}
});
});
module.exports = router;

View file

@ -1,8 +1,41 @@
const express = require("express"); const express = require("express");
const DBUtils = require('../../Utils/DBUtil'); const DBUtils = require('../../Utils/DBUtil');
const AuthUtil = require('../../Utils/AuthUtil'); const AuthUtil = require('../../Utils/AuthUtil');
const Logger = require('../../Utils/Logger');
const router = express.Router(); const router = express.Router();
const TAG = '[UserRouter]'
router.get('/framez', (req, res) => {
const token = req.get('token');
Logger.debug(TAG, 'GET: /framez');
AuthUtil.getAccountByToken(token)
.then(account => {
DBUtils.Models.Frame.find({admin:account._id}, (err, docs) => {
if (err) {
Logger.error(TAG, 'Failed to query DB. ERROR:', err);
res.status(500).json({
message: err.message
});
}
else{
Logger.debug(TAG, 'responding with frames');
let frames = docs.map(f=>{
let frame = f.toObject()
delete frame.viewerKeys
return frame
})
res.json({frames:frames});
}
});
})
.catch(reason => {
Logger.warn(TAG, 'Invalid token');
res.status(401).json({
message: reason
});
})
})
router.get('/:accountId', (req, res) => { router.get('/:accountId', (req, res) => {
const token = req.get('token'); const token = req.get('token');

View file

@ -30,6 +30,12 @@ module.exports = {
} }
}, },
logging: {
logsPath: 'Logs',
fileLogLevel: 'silly',
consoleLogLevel: 'silly'
},
// Password hash // Password hash
salt_work_factor: 10 salt_work_factor: 10
}; };

View file

@ -0,0 +1,45 @@
const Logger = require('../Utils/Logger')
const frameViewers = {}
const TAG = '[FrameLinker]';
module.exports = {
init: (io) => {
io.sockets.on('connection', (socket) => {
let key = null
Logger.log(TAG, 'New connection from client: ', socket.id)
socket.on('disconnect', () => {
Logger.log(TAG, 'Frame Viewer disconnected disconnected')
if(key){
delete frameViewers[key];
}
});
socket.on('register_key', (data) => {
if(frameViewers[key]){
Logger.warn(TAG, 'Key in use - sending register_fail')
socket.emit('register_fail')
return;
}
Logger.debug(TAG, 'Registering key ' + data.key)
key = data.key
frameViewers[key] = socket
Logger.debug(TAG, 'is key', key, 'valid:', frameViewers.hasOwnProperty(key))
})
});
},
isKeyValid: (key)=>{
Logger.debug(TAG, 'is key', key, 'valid:', frameViewers.hasOwnProperty(key))
if(frameViewers.hasOwnProperty(key)) return true;
return false;
},
linkFrame: (frameId, accessToken, key)=>{
return new Promise((resolve, reject) => {
if(!frameViewers[key]) reject('No Frame Viewer Found')
else{
frameViewers[key].emit('consume_frame', {frame_id: frameId, accessToken: accessToken})
resolve()
}
});
}
};

View file

@ -9,7 +9,7 @@ const Account = new Schema({
username: { type: String, required: true, index: { unique: true } }, username: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true }, password: { type: String, required: true },
auth_token: {type: String, require: false, index: { unique: true }}, auth_token: {type: String, require: false, index: { unique: true }},
frames: {type: [Schema.ObjectId], default: []} frames: {type: [Schema.Types.ObjectId], default: [], ref: 'Frame'}
}); });

View file

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

View file

@ -3,9 +3,9 @@ const mongoose = require('mongoose');
const Schema = mongoose.Schema; const Schema = mongoose.Schema;
const Photo = new Schema({ const Photo = new Schema({
frame_id: Schema.ObjectId, frame_id: {type: Schema.Types.ObjectId, ref: 'Frame'},
contentType: String, contentType: String,
user: Schema.ObjectId, user: {type: Schema.Types.ObjectId, ref: 'User'},
photo: Buffer, photo: Buffer,
// thumbnail: Buffer, // thumbnail: Buffer,
timestamp: Number timestamp: Number

View file

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

55
Server/Utils/Logger.js Normal file
View file

@ -0,0 +1,55 @@
/**
* NPM Modules
*/
const fs = require('fs');
const path = require('path');
const winston = require('winston');
/**
* Internal Modules
*/
const Config = require('../Config/Config');
/**
* Module Variables
*/
const tsFormat = () => (new Date()).toISOString().replace(/T/, ' ');
let logger;
/**
* Module Functions
*/
let _init = ()=>{
if (!fs.existsSync(Config.logging.logsPath)) {
fs.mkdirSync(Config.logging.logsPath);
}
// { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }
logger = new (winston.Logger)({
transports: [
//Console Logging
new (winston.transports.Console)({
timestamp: tsFormat,
colorize: true,
json: false,
level: Config.logging.consoleLogLevel
}),
// File Logging
new (require('winston-daily-rotate-file'))({
filename: `${Config.logging.logsPath}/framez-server-%DATE%.log`,
timestamp: tsFormat,
datePattern: 'YYYY-MM-DD',
prepend: true,
json: false,
level: Config.logging.fileLogLevel
})
]
});
};
_init();
/**
* Module exported Interface
*/
module.exports = logger;

View file

@ -3,15 +3,19 @@ const https = require('https');
// //
// Config // Config
const Config = require('./Server/Config/Config'); const Config = require('./Server/Config/Config');
// Logger
const Logger = require('./Server/Utils/Logger');
// Routers // Routers
const APIRouter = require('./Server/API/API'); const APIRouter = require('./Server/API/API');
// Soket.io
const io = require('socket.io');
// TV linker listener
const TVListener = require('./Server/FrameLinker/FrameLinker');
// App // App
const app = express() const app = express()
const TAG = '[FramezServer]'
app.use('/api', APIRouter);
if(Config.server.enableSSL){ if(Config.server.enableSSL){
// HTTPS server // HTTPS server
@ -21,6 +25,13 @@ if(Config.server.enableSSL){
passphrase: fs.readFileSync(Config.server.ssl.passphrase) passphrase: fs.readFileSync(Config.server.ssl.passphrase)
}, app); }, app);
} }
app.listen(Config.server.port,function(){ const server = app.listen(Config.server.port,function(){
console.log(`Running an ${(Config.server.enableSSL) ? 'HTTPS' : 'HTTP'}, and listening on port ${Config.server.port}`); Logger.info(TAG, `Running an ${(Config.server.enableSSL) ? 'HTTPS' : 'HTTP'}, and listening on port ${Config.server.port}`);
}); });
TVListener.init(io(server));
app.use('/api', APIRouter);

22
package.json Normal file
View file

@ -0,0 +1,22 @@
{
"name": "framez-back",
"version": "1.0.0",
"description": "",
"main": "framez-server.js",
"dependencies": {
"bcrypt": "^2.0.1",
"body-parser": "^1.18.2",
"express": "^4.16.3",
"mongoose": "^5.0.16",
"socket.io": "^2.1.0",
"uuid": "^3.2.1",
"winston": "^2.4.2",
"winston-daily-rotate-file": "^3.1.3"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}