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 ###
# Logs
logs
Logs
*.log
npm-debug.log*
yarn-debug.log*

View file

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

View file

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

View file

@ -2,180 +2,238 @@ const express = require("express");
const DBUtils = require('../../Utils/DBUtil')
const AuthUtil = require('../../Utils/AuthUtil')
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();
router.use(bodyParser.raw({
uploadDir: '/tmp/uploads',
keepExtensions: true,
limit: '5mb',
type: 'image/*'
}))
uploadDir: '/tmp/uploads',
keepExtensions: true,
limit: '5mb',
type: 'image/*'
}))
router.get('/:frameId', (req, res) => { // by Frame Id
router.use((req, res, next) => {
const token = req.get('token');
const frameId = req.params.frameId;
Logger.debug(TAG, 'Auth middleware check');
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
});
}
.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
Logger.debug(TAG, 'GET: /:frameId');
const token = req.get('token');
const frameId = req.params.frameId;
const account = req.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();
delete frame.viewerKeys
/// 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);
})
});
} else {
res.status(403).json({
message: 'Account has no access to frame with id of: ' + frameId
});
}
})
router.post('/create', (req, res) => {
Logger.debug(TAG, 'GET: /create');
const token = req.get('token');
const body = req.body;
if(!body.name) {
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
const account = req.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);
})
})
});
router.post('/:frameId/upload/photo', (req, res) => {
Logger.debug(TAG, 'POST: /:frameId/upload/photo');
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 account = req.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
});
AuthUtil.getAccountByToken(token)
.then((account) => {
if(account.frames.indexOf(frameId) >= 0) {
DBUtils.Models.Photo.findOne({_id: photoId}, (err, doc) => {
if(err) {
// Save photo
photo.save((err) => {
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'
});
res.status(201).json(photo)
});
} else {
res.status(403).json({
message: 'Account has no access to frame with id of: ' + photoId
})
.catch(reason => {
res.status(500).json({
message: 'Unexpected error: ' + reason
});
}
})
.catch(reason => {
res.status(401).json({
message: reason
})
} else {
res.status(403).json({
message: 'Account has no access to frame with id of: ' + frameId
});
}
});
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) => {
Logger.debug(TAG, 'GET: /:frameId/download/photo/:photoId');
const token = req.get('token');
const photoId = req.params.photoId;
const frameId = req.params.frameId;
const account = req.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
});
}
})

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 DBUtils = require('../../Utils/DBUtil');
const AuthUtil = require('../../Utils/AuthUtil');
const Logger = require('../../Utils/Logger');
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) => {
const token = req.get('token');

View file

@ -7,7 +7,7 @@ const DBAuth = require('../../private/DBAuth');
module.exports = {
server: {
enableSSL: false,
ssl:{
ssl: {
cert: '',
key: '',
passphrase: ''
@ -30,6 +30,12 @@ module.exports = {
}
},
logging: {
logsPath: 'Logs',
fileLogLevel: 'silly',
consoleLogLevel: 'silly'
},
// Password hash
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 } },
password: { type: String, required: 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({
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: []},
admin: {type: Schema.Types.ObjectId, ref: 'Account'},
members: {type: [Schema.Types.ObjectId], default: [], ref: 'Account'},
viewerKeys: {type: [String], default: []},
});
module.exports = mongoose.model('Frame', Frame);

View file

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

View file

@ -3,10 +3,9 @@ const Config = require('../Config/Config')
const Schema = mongoose.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 } },
avatar: { type: String, default: Config.defaults.user.avatar },
auth_token: {type: String, default: null}
avatar: { type: String, default: Config.defaults.user.avatar }
});
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
const Config = require('./Server/Config/Config');
// Logger
const Logger = require('./Server/Utils/Logger');
// Routers
const APIRouter = require('./Server/API/API');
// Soket.io
const io = require('socket.io');
// TV linker listener
const TVListener = require('./Server/FrameLinker/FrameLinker');
// App
const app = express()
app.use('/api', APIRouter);
const TAG = '[FramezServer]'
if(Config.server.enableSSL){
// HTTPS server
@ -21,6 +25,13 @@ if(Config.server.enableSSL){
passphrase: fs.readFileSync(Config.server.ssl.passphrase)
}, app);
}
app.listen(Config.server.port,function(){
console.log(`Running an ${(Config.server.enableSSL) ? 'HTTPS' : 'HTTP'}, and listening on port ${Config.server.port}`);
const server = app.listen(Config.server.port,function(){
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"
}