adding Admin server

This commit is contained in:
Luke Berndt
2021-01-10 22:23:09 -05:00
parent c8b4f02d06
commit 86eb854848
156 changed files with 53167 additions and 15978 deletions

View File

@@ -0,0 +1,86 @@
const path = require("path");
const express = require("express");
const session = require("express-session");
const bodyParser = require("body-parser");
const connectMongo = require("connect-mongo");
const secrets = require("./secrets");
const MongoStore = connectMongo(session)
var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.env['REACT_APP_BACKEND_SERVER'] : 'https://api.openmhz.com';
var frontend_server = process.env['REACT_APP_FRONTEND_SERVER'] != null ? process.env['REACT_APP_FRONTEND_SERVER'] : 'https://openmhz.com';
var socket_server = process.env['REACT_APP_SOCKET_SERVER'] != null ? process.env['REACT_APP_SOCKET_SERVER'] : 'wss://socket.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
var account_server = process.env['REACT_APP_ACCOUNT_SERVER'] != null ? process.env['REACT_APP_ACCOUNT_SERVER'] : 'https://account.openmhz.com'; //'https://s3.amazonaws.com/robotastic';
module.exports = function(app, passport) {
app.set("port", 3008)
// X-Powered-By header has no functional value.
// Keeping it makes it easier for an attacker to build the site's profile
// It can be removed safely
app.disable("x-powered-by")
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static(path.join(process.cwd(), 'public')));
const sess = {
resave: true,
saveUninitialized: true,
secret: secrets.sessionSecret,
proxy: true,
name: "sessionId",
cookie: {
httpOnly: true,
secure: false,
domain: cookie_domain
},
store: new MongoStore({
url: secrets.db,
autoReconnect: true
})
}
var node_env = process.env.NODE_ENV;
console.log('--------------------------');
console.log('===> 😊 Starting Server . . .');
console.log('===> Environment: ' + node_env);
/*
if(node_env === 'production') {
console.log('===> 🚦 Note: In order for authentication to work in production');
console.log('===> you will need a secure HTTPS connection');
sess.cookie.secure = true; // Serve secure cookies
}*/
app.use(session(sess))
app.use(passport.initialize())
app.use(passport.session())
app.use('/*', function(req, res, next) {
var allowedOrigins = [ account_server];
allowedOrigins.push(frontend_server);
allowedOrigins.push(backend_server);
allowedOrigins.push(socket_server);
var origin = req.headers.origin;
if (allowedOrigins.indexOf(origin) > -1) {
res.setHeader('Access-Control-Allow-Origin', origin);
} else if (req.headers["user-agent"] == 'TrunkRecorder1.0') {
res.setHeader('Access-Control-Allow-Origin', "*");
} else {
res.setHeader('Access-Control-Allow-Origin', "*");
if (origin) {
console.warn("forcing CORS for: " + origin + " referer: " + req.headers.referer);
}
}
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS,WEBSOCKET');
res.header('Access-Control-Allow-Credentials', 'true');
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Access-Control-Allow-Credentials, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Max-Age");
res.header('Access-Control-Max-Age', '600');
next();
});
}

View File

@@ -0,0 +1,36 @@
/*
Configuring local strategy to authenticate strategies
Code modified from : https://github.com/madhums/node-express-mongoose-demo/blob/master/config/passport/local.js
*/
const mongoose = require("mongoose");
const LocalStrategy = require("passport-local").Strategy;
const User = require("../../models/user");
/*
By default, LocalStrategy expects to find credentials in parameters named username and password.
If your site prefers to name these fields differently, options are available to change the defaults.
*/
module.exports = new LocalStrategy({
usernameField: "email"
}, (email, password, done) => {
User.findOne({
$or: [{
email
}, {
local: {
email
}
}]
}, (err, user) => {
if(!user) return done(null, false, { message: `Email ${email} not found` })
user.comparePassword(password, (err, isMatch) => {
if (isMatch) {
return done(null, user)
} else {
return done(null, false, { message: "Invalid email or password" })
}
})
})
})

View File

@@ -0,0 +1,26 @@
/* Initializing PassportJS */
const User = require("../models/user");
const local = require("./passport-strategies/local");
module.exports = function (app, passport) {
// Configure Passport authenticated session persistence.
//
// In order to restore authentication state across HTTP requests, Passport needs
// to serialize users into and deserialize users out of the session. The
// typical implementation of this is as simple as supplying the user ID when
// serializing, and querying the user record by ID from the database when
// deserializing.
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user)
})
})
// use the following strategies
passport.use(local)
}

View File

@@ -0,0 +1,14 @@
/** Important **/
/** You should not be committing this file to GitHub **/
/** Repeat: DO! NOT! COMMIT! THIS! FILE! TO! YOUR! REPO! **/
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017;
var mongoUrl = 'mongodb://' + host + ':' + port + '/scanner';
module.exports = secrets = {
db: mongoUrl,
sessionSecret: "letthisbeyoursecret"
}

View File

@@ -0,0 +1,296 @@
const Group = require("../models/group");
const System = require("../models/system");
const mongoose = require("mongoose");
//router.get('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
exports.getGroups = function(req, res, next) {
process.nextTick(function() {
System.findOne(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, system) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!system) {
console.error(err);
res.json({
success: false,
message: "That Short Name does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
res.json({
success: false,
message: "You are not the user associated with this system."
});
return;
}
if (req.params.groupId) {
var groupId = req.params.groupId.toLowerCase();
Group.findOne(
{
_id: new mongoose.Types.ObjectId(groupId),
shortName: req.params.shortName.toLowerCase()
},
{ sort: { position: -1 } },
function(err, group) {
if (err) {
res.json({
success: false,
message: err
});
return;
} else {
res.json({
success: true,
group: group
});
return;
}
}
);
} else {
Group.find(
{
shortName: req.params.shortName.toLowerCase()
}).sort("position").exec(
function(err, groups) {
if (err) {
res.json({
success: false,
message: err
});
return;
} else {
res.json({
success: true,
groups: groups
});
return;
}
}
);
}
}
);
});
};
exports.deleteGroup = function(req, res, next) {
//router.get('/system/:shortName/remove_group/:groupId', isLoggedIn, function(req, res) {
System.findOne({
'shortName': req.params.shortName.toLowerCase()
}, function(err, system) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!system) {
console.error(err);
res.json({
success: false,
message: "That Short Name does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
res.json({
success: false,
message: "You are not the user associated with this system."
});
return;
}
Group.findOneAndRemove({
shortName: req.params.shortName.toLowerCase(),
'_id': new mongoose.Types.ObjectId(req.params.groupId)
}, function(err) {
if (err) {
res.json({
success: false,
message: err
});
return;
} else {
res.json({
success: true
});
return;
}
// removed!
});
});
};
exports.reorderGroups = function(req, res, next) {
//router.post('/system/:shortName/group_order', isLoggedIn, function(req, res) {
process.nextTick(function() {
System.findOne(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, system) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!system) {
console.error(err);
res.json({
success: false,
message: "That Short Name does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
res.json({
success: false,
message: "You are not the user associated with this system."
});
return;
}
var groupOrder = JSON.parse(req.body.groupOrder);
for (var i = 0; i < groupOrder.length; i++) {
console.log("in group: " + groupOrder[i] + " position: " + i);
Group.findOneAndUpdate(
{
_id: new mongoose.Types.ObjectId(groupOrder[i]),
shortName: req.params.shortName.toLowerCase()
},
{
position: i
},
{
new: true,
upsert: false
},
function(err, group) {
console.log("Save group: " + group._id + " position: " + group.position);
if (err) {
res.json({
success: false,
message: err
});
console.log(
"Error: Group Order - " +
err +
" Shortname: " +
req.params.shortName.toLowerCase()
);
return;
}
}
);
}
res.json({
success: true
});
}
);
});
};
exports.upsertGroup = function(req, res, next) {
//router.post('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
process.nextTick(function() {
System.findOne(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, system) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!system) {
console.error(err);
res.json({
success: false,
message: "That Short Name does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
res.json({
success: false,
message: "You are not the user associated with this system."
});
return;
}
var message = "";
var groupName = req.body.groupName.replace(/[^\w\s]/g, "");
var talkgroups = JSON.parse(req.body.talkgroups);
console.log(
"Talkgroups: " +
talkgroups +
" for group: " +
groupName +
" array: " +
Array.isArray(talkgroups)
);
if (!talkgroups.length || !Array.isArray(talkgroups)) {
message = "Talkgroups need to be selected, group can not be empty ";
}
if (!groupName.length) {
message = message + "Group Name is required";
}
if (message.length) {
res.json({
success: false,
message: message
});
return;
}
// if we got here, that means that everything is good for updating/inserting
var groupId = req.params.groupId;
Group.findOneAndUpdate(
{
_id: new mongoose.Types.ObjectId(groupId),
shortName: req.params.shortName.toLowerCase()
},
{
groupName: groupName,
talkgroups: talkgroups
},
{
new: true,
upsert: true
},
function(err, group) {
if (err) {
res.json({
success: false,
message: err
});
return;
} else {
res.json({
success: true,
group: group
});
return;
}
}
);
}
);
});
};

View File

@@ -0,0 +1,241 @@
const mongoose = require("mongoose");
const Permission = require("../models/permission");
const System = require("../models/system");
const User = require("../models/user");
exports.fetchPermissions = function(req, res, next) {
Permission.find(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, permissions) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
var userIds = permissions.map(permission => {return permission.userId});
User.find(
{
_id: {
$in: userIds
}
},
function(err, users) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
var returnUsers = users.map(user =>{
var permission = permissions.find(obj => {
if (obj.userId.toString() == user._id.toString()) {
return true;
}
return false;
});
if (!permission) {
console.error("Error - Permission not found for user: " + user._id);
res.json({ success: false, message: "Permission not found" });
return;
}
return {_id: permission._id, userId: user._id, firstName: user.firstName, lastName: user.lastName, email: user.email, role: permission.role };
});
res.json({ success: true, permissions: returnUsers });
});
return;
}
);
};
exports.addPermission = async (req, res, next) => {
//router.post('/system/:shortName/group/:groupId?', isLoggedIn, function(req, res) {
let role = req.body.role;
var system = false;
var user = false;
var existing = false;
var saved = false;
if (!role || (role >= 20) || (role <1)) {
res.json({
success: false,
message: "Invalid Role"
});
return;
}
console.log("role looks good");
try {
system = await System.findOne(
{
shortName: req.params.shortName.toLowerCase()
});
} catch(err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!system) {
res.json({
success: false,
message: "That System does not exist."
});
return;
}
console.log("system looks good")
try {
user = await User.findOne(
{
email: req.body.email
});
} catch (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!user) {
res.json({ success: false, message: "User not found" });
return;
}
console.log("user looks good")
try {
existing = await Permission.findOne({userId: user._id, systemId: system._id});
} catch (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (existing) {
res.json({ success: false, message: "User already has acccess" });
return;
}
console.log("no existing")
var permission = new Permission({
userId: user._id,
systemId: system._id,
shortName: system.shortName,
role: role
});
try {
saved = await permission.save()
} catch(err) {
res.json({
success: false,
message: err
});
return;
}
console.log("saved it")
var newPermission = saved.toObject();
newPermission.firstName = user.firstName;
newPermission.lastName = user.lastName;
newPermission.email = user.email;
console.log("Add Permission to: " + system.shortName + " for: " + newPermission.email + " by: " + req.user.email);
res.json({
success: true,
permission: newPermission
});
};
exports.updatePermission = function(req, res, next) {
process.nextTick(function() {
Permission.findOne(
{
_id: new mongoose.Types.ObjectId(req.params.permissionId),
shortName: req.params.shortName.toLowerCase(req.params.shortName)
},
function(err, permission) {
if (err) {
res.json({
success: false,
message: err
});
return;
}
if (permission.role >= 20) {
res.json({
success: false,
message: "Cannot change permissions of system owner"
});
return;
}
var newRole = req.body.role;
if (!newRole || (newRole >= 20) || (newRole <1)) {
res.json({
success: false,
message: "Invalid Role"
});
return;
}
permission.role = newRole;
permission.save(function(err, saved) {
if (err) {
res.json({
success: false,
message: err
});
return;
}
res.json({
success: true,
permission: saved.toJSON()
});
return;
});
}
);
});
};
exports.deletePermission = function(req, res, next) {
//router.get('/system/:shortName/remove_group/:groupId', isLoggedIn, function(req, res) {
Permission.findOneAndRemove({
shortName: req.params.shortName.toLowerCase(),
'_id': new mongoose.Types.ObjectId(req.params.permissionId),
role: { $lt : 20}
}, function(err, permission) {
if (err) {
res.json({
success: false,
message: err
});
return;
}
if (!permission) {
res.json({
success: false,
message: "Permission not found"
});
return;
} else {
res.json({
success: true
});
return;
}
// removed!
});
};

View File

@@ -0,0 +1,507 @@
const mongoose = require("mongoose");
const System = require("../models/system");
const SystemStat = require("../models/system_stat");
const Talkgroup = require("../models/talkgroup");
const Group = require("../models/group");
const crypto = require("crypto");
exports.isLoggedIn = function (req, res, next) {
if (req.isAuthenticated()) return next();
res.redirect(account_server + "/login");
};
// -------------------------------------------
exports.listSystems = function (req, res, next) {
console.log("Listing Systems for: " + req.user._id);
System.find({
userId: mongoose.Types.ObjectId(req.user._id)
},
function (err, systems) {
if (err) {
console.error("Error - ListSystems: " + err);
res.json({
success: false,
message: err
});
return;
}
var shortNames = new Array();
for (var i = 0; i < systems.length; i++) {
shortNames.push(systems[i].shortName);
}
SystemStat.find({
shortName: {
$in: shortNames
}
},
function (err, stats) {
var returnSys = systems.map(obj => {
var rObj = (({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
userId,
key,
planType,
showScreenName
}) => ({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
userId,
key,
planType,
showScreenName
}))(obj);
if (obj.showScreenName) {
rObj.screenName = req.user.screenName;
} else {
rObj.screenName = null;
}
rObj.id = obj._id;
return rObj;
});
var sys_stats = {}
var MS_PER_MINUTE = 60000;
var now = new Date();
for (var i = 0; i < stats.length; i++) {
sys_stats[stats[i].shortName] = {};
sys_stats[stats[i].shortName].callTotals = stats[i].callTotals;
sys_stats[stats[i].shortName].errorTotals = stats[i].errorTotals;
sys_stats[stats[i].shortName].errorStats = stats[i].errorStats;
sys_stats[stats[i].shortName].usageBytes = stats[i].usageBytes;
sys_stats[stats[i].shortName].usageMb = stats[i].usageMb;
}
res.json({
success: true,
systems: returnSys,
stats: sys_stats
});
return;
}
);
}
);
};
function remove_system(shortName) {
/*
var sysDir = config.mediaDirectory + "/" + shortName + "/"
console.log("Removing DIR: " + sysDir);
fs.remove(sysDir, function(err) {
if (err) return console.error(err)
});
Transmission.remove({
shortName: shortName
}, function(err) {
if (err) return console.error(err);
// removed!
});
*/
System.remove({
shortName: shortName
},
function (err) {
if (err) return console.error(err);
// removed!
}
);
Talkgroup.remove({
shortName: shortName
},
function (err) {
if (err) return console.error(err);
// removed!
}
);
Group.remove({
shortName: shortName
},
function (err) {
if (err) return console.error(err);
// removed!
}
);
}
exports.deleteSystem = function (req, res, next) {
//router.get('/delete_system/:shortName', isLoggedIn, function(req, res) {
System.findOne({
shortName: req.params.shortName.toLowerCase()
},
function (err, system) {
if (err) {
res.json({
success: false,
message: err
});
return;
}
if (!system) {
res.json({
success: false,
message: "That Short Name does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
console.log("Error deleting syste - Wrong User - sys: " + system.userId + " profile: " + mongoose.Types.ObjectId(req.user._id));
res.json({
success: false,
message: "You are not the user associated with this system."
});
return;
}
console.log("Removing: " + system.name);
remove_system(system.shortName);
res.json({
success: true
});
return;
}
);
};
// -------------------------------------------
exports.ownSystem = async function (req, res, next) {
try {
var system = await System.findOne({
shortName: req.body.shortName.toLowerCase()
});
if (!system) {
console.error("ERROR: OwnSystem - system does not exist: " + req.body.shortName.toLowerCase());
res.json({
success: false,
message: "That System does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
console.error("ERROR: OwnSystem - not users system " + req.body.shortName.toLowerCase());
res.json({
success: false,
message: "You are not the user associated with this system."
});
console.error("System: " + system.userId + " User: " + req.user._id);
return;
}
res.locals.test = "Yoho!"
res.locals.system = system;
console.log(system);
console.log(res.locals);
next();
} catch (err) {
console.error("ERROR: Unique ShortName - caught: " + err);
res.json({
success: false,
message: err
});
return;
}
}
// -------------------------------------------
exports.uniqueShortName = async function (req, res, next) {
console.error("locals: " + res.locals.shortName + " body: " + req.body.shortName )
try {
var system = await System.findOne({
shortName: res.locals.shortName.toLowerCase()
});
if (system) {
console.error("ERROR: Unique Shortname already in use: " + req.body.shortName.toLowerCase());
res.json({
success: false,
message: "Short Name already in use"
});
return;
} else {
next();
}
} catch (err) {
console.error("ERROR: Unique ShortName - caught: " + err);
res.json({
success: false,
message: err
});
return;
}
}
// -------------------------------------------
exports.updateSystem = async function (req, res, next) {
console.log(res.locals)
res.locals.system.name = res.locals.name;
res.locals.system.city = res.locals.city;
res.locals.system.state = res.locals.state;
res.locals.system.county = res.locals.county;
res.locals.system.country = res.locals.country;
res.locals.system.description = res.locals.description;
res.locals.system.systemType = res.locals.systemType;
res.locals.system.showScreenName = res.locals.showScreenName;
res.locals.system.save(function (err) {
if (err) {
res.json({
success: false,
message: err
});
return;
} else {
var returnSys = (({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
userId,
key,
showScreenName
}) => ({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
userId,
key,
showScreenName
}))(res.locals.system);
returnSys.id = res.locals.system._id;
res.json({
success: true,
system: returnSys
});
return;
}
});
}
// -------------------------------------------
exports.validateSystem = async function (req, res, next) {
try {
res.locals.showScreenName = req.body.showScreenName;
if (!req.body.name || (req.body.name.length < 2)) {
console.error("ERROR: Validate System - req.body.name");
res.json({
success: false,
message: "System Name is Required"
});
return;
}
res.locals.name = req.body.name.replace(/[^\w\s\.\,\-\_]/gi, '');
if (!req.body.description || (req.body.description.length < 2)) {
console.error("ERROR: Validate System - req.body.description");
res.json({
success: false,
message: "System Description is Required"
});
return;
}
res.locals.description = req.body.description.replace(/[^\w\s\.\,\-\_]/gi, '');
if (!req.body.shortName || (req.body.shortName.length < 2)) {
console.error("ERROR: Validate System - req.body.shortName");
res.json({
success: false,
message: "System ShortName is Required"
});
return;
}
var tempShortName = req.body.shortName.replace(/[^\w]/gi, '');
res.locals.shortName = tempShortName.toLowerCase();
// -------------------- Handle all the different SystemTypes of Locations
// make sure it is one of the expected types
if ((req.body.systemType != "state") && (req.body.systemType != "city") && (req.body.systemType != "county") && (req.body.systemType != "international")) {
console.error("ERROR: Validate System - req.body.systemType: " + req.body.systemType);
res.json({
success: false,
message: "Select system location"
});
return;
}
// copy it over
res.locals.systemType = req.body.systemType;
// now check to make sure the correct things for location is filled out
if (req.body.systemType === "state" ||
req.body.systemType === "city" ||
req.body.systemType === "county") {
if (!req.body.state || (req.body.state === "") || (req.body.state.length < 2)) {
console.error("ERROR: Validate System - req.body.state: " + req.body.state);
res.json({
success: false,
message: "Select the State for the System"
});
return;
}
res.locals.state = req.body.state;
}
if (req.body.systemType === "city") {
if (!req.body.city || (req.body.city === "") || (req.body.city.length < 2)) {
console.error("ERROR: Validate System - req.body.systemType: " + req.body.systemType);
res.json({
success: false,
message: "Enter the city for the System"
});
return;
}
res.locals.city = req.body.city.replace(/[^\w\s\.\,\-\_]/gi, '');
}
if (req.body.systemType === "county") {
if (!req.body.county || (req.body.county === "") || (req.body.county.length < 2)) {
console.error("ERROR: Validate System - req.body.county: " + req.body.county);
res.json({
success: false,
message: "Enter the county for the System"
});
return;
}
res.locals.county = req.body.county.replace(/[^\w\s\.\,\-\_]/gi, '');
}
if (req.body.systemType === "international") {
if (!req.body.country || (req.body.country === "") || (req.body.country.length < 2)) {
console.error("ERROR: Validate System - req.body.country: " + req.body.country);
res.json({
success: false,
message: "Enter the country for the System"
});
return;
}
res.locals.country = req.body.country.replace(/[^\w\s\.\,\-\_]/gi, '');
}
} catch (err) {
console.error("ERROR: Validate System - caught: " + err);
res.json({
success: false,
message: err
});
return;
}
next();
}
// -------------------------------------------
exports.createSystem = async function (req, res, next) {
var key = uuid.v4();
key = crypto
.createHash("sha256")
.update(key)
.update("salt")
.digest("hex");
const system = (({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
showScreenName
}) => ({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
showScreenName
}))(res.locals);
system.key = key;
system.userId = mongoose.Types.ObjectId(req.user._id);
System.create(system, function (err, newSys) {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
var returnSys = (({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
userId,
showScreenName,
key
}) => ({
name,
shortName,
description,
systemType,
city,
state,
county,
country,
userId,
showScreenName,
key
}))(newSys);
returnSys.id = newSys._id;
res.json({
success: true,
system: returnSys
});
return;
});
}

View File

@@ -0,0 +1,250 @@
const Talkgroup = require("../models/talkgroup");
const System = require("../models/system");
const fs = require('fs-extra');
const crypto = require("crypto");
var parse = require('csv-parse');
exports.isLoggedIn = function(req, res, next) {
if (req.isAuthenticated()) return next();
res.redirect(account_server + "/login");
};
//router.get("/tg_export/:shortName", isLoggedIn, function(req, res) {
exports.exportTalkgroups = function(req, res, next) {
process.nextTick(function() {
System.findOne(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, system) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!system) {
console.error(err);
res.json({
success: false,
message: "That Short Name does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
res.json({
success: false,
message: "You are not the user associated with this system."
});
return;
}
Talkgroup.find(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, talkgroups) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
if (!talkgroups || !Array.isArray(talkgroups)) {
console.error(err);
res.json({
success: false,
message: "Talkgroups were not found."
});
return;
}
res.statusCode = 200;
res.setHeader("Content-Type", "text/csv");
talkgroups.forEach(function(item) {
/* res.write('"' + item.num + '", "'
+ item.mode + '", "'
+ item.alpha + '", "'
+ item.description + '", "'
+ item.tag + '", "'
+ item.group + '", "'
+ item.priority+ '"\r\n');*/
if (item) {
var sending = "";
sending = item.num + ", ";
if (item.mode) {
sending = sending + item.mode.toString().replace(/\,/g, "") + ", ";
} else {
sending = sending + " ,";
}
if (item.alpa) {
sending = sending + item.alpha.toString().replace(/\,/g, "") + ", ";
} else {
sending = sending + " ,";
}
if (item.description) {
sending = sending + item.description.toString().replace(/\,/g, "") + ", ";
} else {
sending = sending + " ,";
}
if (item.tag) {
sending = sending + item.tag.toString().replace(/\,/g, "") + ", ";
} else {
sending = sending + " ,";
}
if (item.group) {
sending = sending + item.group.toString().replace(/\,/g, "") + ", ";
} else {
sending = sending + " ,";
}
if (item.priority) {
sending = sending + item.priority.toString().replace(/\,/g, "") + "\r\n";
} else {
sending = sending + "\r\n";
}
res.write(sending);
}
});
res.end();
}
);
}
);
});
};
function csv_import(shortName, filename, callback) {
var talkgroups = [];
Talkgroup.remove(
{
shortName: shortName
},
function(err) {
if (err) {
console.error("Error: csv import: " + err);
callback(err, null);
} else {
var parser = parse(
{
columns: [
"num",
"mode",
"alpha",
"description",
"tag",
"group",
"priority"
]
},
async function(err, data) {
if (err) {
console.error("Error: csv import 2: " + err);
callback(err, null);
} else {
for (var i = 0; i < data.length; i++) {
var row = data[i];
var newTg = new Talkgroup();
newTg.num = parseInt(row.num);
newTg.alpha = row.alpha;
newTg.mode = row.mode;
newTg.description = row.description;
newTg.tag = row.tag.toLowerCase();
newTg.group = row.group.toLowerCase();
newTg.priority = row.priority;
newTg.shortName = shortName;
await newTg.save(function(err) {
if (err) {
console.log("Error: CSV import: " + err);
callback(err, null);
}
});
}
callback(null, data);
}
}
);
}
fs.createReadStream(filename).pipe(parser);
// removed!
}
);
}
//router.post("/tg_import/:shortName", isLoggedIn, upload.single('file'), function(req, res, next) {
exports.importTalkgroups = function(req, res, next) {
process.nextTick(function() {
System.findOne(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, system) {
if (err) {
console.error("Error - importTalkgroups: " + err);
res.json({ success: false, message: err });
return;
}
if (!system) {
console.error("Error - importTalkgroups: System not found - " + req.params.shortName.toLowerCase());
res.json({
success: false,
message: "That Short Name does not exist."
});
return;
}
if (!system.userId.equals(req.user._id)) {
console.error("Error - importTalkgroups: you are not the user for hte system: " + req.user._id);
res.json({
success: false,
message: "You are not the user associated with this system."
});
return;
}
if (!req.file || req.file.size === 0) {
res.json({ success: false, message: "Please select a file." });
return;
}
fs.exists(req.file.path, function(exists) {
if (exists) {
csv_import(system.shortName, req.file.path, function(err, data) {
if (err) {
console.error("Erorr - CSV import, callback: " + err);
res.json({ success: false, message: err });
res.end();
return;
} else {
res.json({ success: true, talkgroups: data });
return;
}
});
} else {
console.error("Error - importTalkgroups: no file");
res.end(
"Well, there is no magic for those who don’t believe in it!"
);
}
});
}
);
});
};
// -------------------------------------------
exports.fetchTalkgroups = function(req, res, next) {
Talkgroup.find(
{
shortName: req.params.shortName.toLowerCase()
},
function(err, talkgroups) {
if (err) {
console.error(err);
res.json({ success: false, message: err });
return;
}
res.json({ success: true, talkgroups: talkgroups });
return;
}
);
};

155
admin/server/index.js Normal file
View File

@@ -0,0 +1,155 @@
const mongoose = require("mongoose");
const express = require("express");
const passport = require("passport");
const path = require("path");
const secrets = require("./config/secrets");
const configurePassport = require("./config/passport");
const configureExpress = require("./config/express");
const systems = require("./controllers/systems");
const groups = require("./controllers/groups");
const talkgroups = require("./controllers/talkgroups");
const Permission = require("./models/permission");
require("./models/user");
const multer = require('multer');
var upload = multer({
dest: 'uploads/'
});
// -------------------------------------------
const app = express()
// -------------------------------------------
const connect = () => {
mongoose.connect(secrets.db, (err, res) => {
if (err) {
console.log(`Error connecting to ${secrets.db}. ${err}`)
} else {
console.log(`Successfully connected to ${secrets.db}.`)
}
})
}
connect()
mongoose.connection.on("error", console.error)
mongoose.connection.on("disconnected", connect)
// -------------------------------------------
const isDev = process.env.NODE_ENV === "development"
/*
async function getRole(req) {
let promise = new Promise((resolve, reject) => {
if (req.params.shortName && req.user) {
var short_name = req.params.shortName.toLowerCase();
var userId = req.user._id;
Permission.findOne({'userId': userId, 'shortName': short_name}, function(err, permission) {
if (err) {
console.error('Error - getRole userId: ' + userId + ' shortName: ' + short_name + ' error: ' + err);
reject(-1);
}
console.log('Found - getRole userId: ' + userId + ' shortName: ' + short_name + ' role: ' + permission.role);
resolve(permission.role);
});
} else {
reject(-1);
}});
return promise;
}*/
function isOwner(req, res, next) {
if (req.params.shortName && req.user) {
var short_name = req.params.shortName.toLowerCase();
var userId = req.user._id;
Permission.findOne({'userId': userId, 'shortName': short_name}, function(err, permission) {
if (err) {
console.error('Error - getRole userId: ' + userId + ' shortName: ' + short_name + ' error: ' + err);
}
console.log('Found - getRole userId: ' + userId + ' shortName: ' + short_name + ' role: ' + permission.role);
if ( permission.role >= 15) {
return next();
} else {
res.status(401).send({
success: false,
message: "Insufficent Permission, role: " + permission.role
});
}
});
}
}
function isAdmin(req, res, next) {
if (req.params.shortName && req.user) {
var short_name = req.params.shortName.toLowerCase();
var userId = req.user._id;
Permission.findOne({'userId': userId, 'shortName': short_name}, function(err, permission) {
if (err) {
console.error('Error - getRole userId: ' + userId + ' shortName: ' + short_name + ' error: ' + err);
}
console.log('Found - getRole userId: ' + userId + ' shortName: ' + short_name + ' role: ' + permission.role);
if (permission.role >= 10) {
return next();
} else {
res.status(401).send({
success: false,
message: "Insufficent Permission, role: " + permission.role
});
}
});
}
}
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) return next();
res.status(401).send({
success: false,
message: "Not Authenticated."
});
};
// -------------------------------------------
configurePassport(app, passport)
configureExpress(app, passport)
// -------------------------------------------
app.use(express.static(path.join(__dirname, "public")));
app.get("")
//app.get("/permissions/:shortname", isOwner, permissions.fetchPermissions)
/*app.get("/permissions/:shortName", permissions.fetchPermissions);
app.delete("/permissions/:shortName/:permissionId", isAdmin, permissions.deletePermission);
app.post("/permissions/:shortName/:permissionId", isAdmin, permissions.updatePermission);
app.post("/permissions/:shortName", isAdmin, permissions.addPermission);*/
app.get("/talkgroups/:shortName", isLoggedIn, talkgroups.fetchTalkgroups)
app.post("/talkgroups/:shortName/import", isLoggedIn, upload.single('file'), talkgroups.importTalkgroups)
app.get("/talkgroups/:shortName/export", isLoggedIn, talkgroups.exportTalkgroups)
app.post("/groups/:shortName/reorder", isLoggedIn, groups.reorderGroups);
app.post("/groups/:shortName/:groupId?", isLoggedIn, groups.upsertGroup);
app.get("/groups/:shortName/:groupId?", isLoggedIn, groups.getGroups);
app.delete("/groups/:shortName/:groupId", isLoggedIn, groups.deleteGroup);
app.get("/systems", isLoggedIn, systems.listSystems)
app.delete("/systems/:shortName", isLoggedIn, systems.deleteSystem)
app.post("/systems/:shortName", [isLoggedIn, systems.ownSystem, systems.validateSystem, systems.updateSystem])
app.post("/systems", [isLoggedIn, systems.uniqueShortName, systems.validateSystem, systems.createSystem])
app.get("*", (req, res, next) => {
res.sendFile('public/index.html');
});
// start listening to incoming requests
app.listen(app.get("port"), app.get("host"), (err) => {
if (err) {
console.err(err.stack)
} else {
console.log(`App listening on port ${app.get("port")} [${process.env.NODE_ENV} mode]`)
}
})

View File

@@ -0,0 +1,14 @@
var mongoose = require('mongoose');
var groupSchema = mongoose.Schema({
shortName: String,
groupName: String,
groupId: String,
position: Number,
talkgroups: [Number]
});
module.exports = mongoose.model('Group', groupSchema);

View File

@@ -0,0 +1,24 @@
// Defining a User Model in mongoose
// Code modified from https://github.com/sahat/hackathon-starter
var mongoose = require("mongoose");
const PermissionSchema = new mongoose.Schema({
userId: mongoose.Schema.Types.ObjectId,
systemId: mongoose.Schema.Types.ObjectId,
shortName: String,
role: {
type: Number,
default: 0
},
ver: {
type: Number,
default: 1.2
}
})
module.exports=mongoose.model("Permission", PermissionSchema)

View File

@@ -0,0 +1,27 @@
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var systemSchema = mongoose.Schema({
name: String,
shortName: String,
systemType: String,
county: String,
country: String,
city: String,
state: String,
description: String,
showScreenName: Boolean,
planType: {type: Number, default: 0},
userId: mongoose.Schema.Types.ObjectId,
key: String
});
systemSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
systemSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
module.exports = mongoose.model('System', systemSchema);

View File

@@ -0,0 +1,12 @@
var mongoose = require('mongoose');
var systemStatSchema = mongoose.Schema({
callTotals: [Number],
errorTotals: [Number],
usageBytes: Number,
usageMb: Number,
shortName: String
}, { collection: 'system_stats' });
module.exports = mongoose.model('SystemStat', systemStatSchema);

View File

@@ -0,0 +1,16 @@
var mongoose = require('mongoose');
var talkgroupSchema = mongoose.Schema({
shortName: String,
userId: String,
num: Number,
mode: String,
alpha: String,
description: String,
tag: String,
group: String,
priority: Number
});
module.exports = mongoose.model('Talkgroup', talkgroupSchema);

View File

@@ -0,0 +1,91 @@
// Defining a User Model in mongoose
// Code modified from https://github.com/sahat/hackathon-starter
const bcrypt = require("bcrypt");
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
local: {
name: String,
email: String,
password: String
},
email: {
type: String,
unique: true,
lowercase: true
},
password: String,
screenName: {
type: String,
unique: true,
lowercase: true
},
firstName: String,
lastName: String,
location: String,
email: String,
resetPasswordToken: String,
resetPasswordTTL: Date,
confirmEmail: {
type: Boolean,
default: false
},
customerId: {
type: String,
default: ""
},
confirmEmailToken: String,
confirmEmailTTL: Date,
admin: {
type: Boolean,
default: false
},
terms: {
type: Number,
default: 0
},
ver: {
type: Number,
default: 1.1
},
sysCount: Number,
lastLogin: { type : Date, default: Date.now }
})
/**
* Password hash middleware.
*/
UserSchema.pre("save", function(next) {
var user = this
if (!user.isModified("password")) return next()
bcrypt.genSalt(8, (err, salt) => {
if (err) return next(err)
bcrypt.hash(user.password, salt, null, (err, hash) => {
if (err) return next(err)
user.password = hash;
user.local.password = hash;
next()
})
})
})
/*
Defining our own custom document instance method
*/
UserSchema.methods = {
comparePassword: function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.local.password, (err, isMatch) => {
if (err) return cb(err)
cb(null, isMatch)
})
}
}
/**
* Statics
*/
UserSchema.statics = {}
module.exports = mongoose.model("User", UserSchema)

View File

@@ -0,0 +1,5 @@
.field {
padding-top: 10px;
padding-left: 5px;
padding-right: 5px;
}