Skip to content

Commit

Permalink
Merge pull request #14 from versx/develop
Browse files Browse the repository at this point in the history
Release 1.3
  • Loading branch information
versx authored Apr 23, 2020
2 parents cf4fa52 + 111f701 commit 76664e8
Show file tree
Hide file tree
Showing 24 changed files with 935 additions and 61 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,7 @@ config.json
logs

# No screenshots
screenshots
screenshots

# No schedules
schedules.json
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ Central repository for macless client configurations without having to keep trac
You can also pre-create devices and assign configs yourself if needed.

## Features
- Custom config assignments
- Screenshot preview
- Device logging
- Device endpoint tooling
- and more...
- Custom config assignments
- Screenshot preview
- Device logging
- Device endpoint tooling
- and more...

## Installation
1.) Clone repository `git clone https://github.com/versx/DeviceConfigManager`
2.) Install dependencies `npm install`
3.) Copy config `cp src/config.example.json src/config.json`
4.) Fill out config `vi src/config.json`
5.) Run `npm run start`
6.) Access via http://machineip:port/ using username: `root` and password `pass123!`

## Notes
If you use HAProxy, make sure to set `option forwardfor` in your haproxy.cfg if you are not passing the x-forward-for header so the correct IP addresses are saved.

## PM2 (recommended)
Once everything is setup and running appropriately, you can add this to PM2 ecosystem.config.js file so it is automatically started:
Expand Down
6 changes: 6 additions & 0 deletions migrations/3.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS `users` (
`username` varchar(50) NOT NULL PRIMARY KEY,
`password` varchar(255) NOT NULL
);

INSERT INTO `users` (`username`, `password`) VALUES ('root', SHA1('pass123!'));
58 changes: 58 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"moment-timezone": "^0.5.28",
"express-session": "^1.17.1",
"multer": "^1.4.2",
"mustache": "^4.0.1",
"mustache-express": "^1.3.0",
Expand Down
1 change: 1 addition & 0 deletions src/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"locale": "en",
"style": "dark",
"logging": true,
"secret": "-/!sup3rr4nd0m70p53cr3t70k3ny0u5h0u1dch4ng3!/-",
"db": {
"host": "127.0.0.1",
"port": 3306,
Expand Down
4 changes: 3 additions & 1 deletion src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ function query(sql, args) {
// The Promise constructor should catch any errors thrown on
// this tick. Alternately, try/catch and reject(err) on catch.
var conn = getConnection();
/* eslint-disable no-unused-vars */
conn.query(sql, args, function(err, rows, fields) {
/* eslint-enable no-unused-vars */
// Call reject on error states,
// call resolve with results
if (err) {
Expand All @@ -40,7 +42,7 @@ function query(sql, args) {
resolve(rows);
conn.end(function(err, args) {
if (err) {
console.error('Failed to close mysql connection.');
console.error('Failed to close mysql connection:', args);
return;
}
});
Expand Down
133 changes: 119 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@

const path = require('path');
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const app = express();
const mustacheExpress = require('mustache-express');

const config = require('./config.json');
const Device = require('./models/device.js');
const Config = require('./models/config.js');
const Migrator = require('./migrator.js');
const ScheduleManager = require('./models/schedule-manager.js');
const apiRoutes = require('./routes/api.js');

const timezones = require('../static/data/timezones.json');

// TODO: Create route classes
// TODO: Error checking/handling
// TODO: Security / token auth / users (maybe?) or basic authentication

const defaultData = {
title: config.title,
locale: config.locale,
style: config.style == 'dark' ? 'dark' : '',
logging: config.logging
};

// Start database migrator
var dbMigrator = new Migrator();
Expand All @@ -24,29 +35,69 @@ app.set('view engine', 'mustache');
app.set('views', path.resolve(__dirname, 'views'));
app.engine('mustache', mustacheExpress());
app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' })); // for parsing application/x-www-form-urlencoded
//app.use(bodyParser.raw({ type: 'application/x-www-form-urlencoded' }));
app.use(express.static(path.resolve(__dirname, '../static')));
app.use('/screenshots', express.static(path.resolve(__dirname, '../screenshots')));

const defaultData = {
title: config.title,
locale: config.locale,
style: config.style == 'dark' ? 'dark' : '',
logging: config.logging
};
// Sessions middleware
app.use(session({
secret: config.secret, // REVIEW: Randomize?
resave: true,
saveUninitialized: true
}));

// Login middleware
app.use(function(req, res, next) {
if (req.path === '/api/login' || req.path === '/login') {
return next();
}
if (req.session.loggedin) {
defaultData.logged_in = true;
next();
return;
}
res.redirect('/login');
});

// API Route
app.use('/api', apiRoutes);

// UI Routes
app.get(['/', '/index'], async function(req, res) {
var devices = await Device.getAll();
var configs = await Config.getAll();
var metadata = await Migrator.getEntries();
if (req.session.loggedin) {
var username = req.session.username;
var devices = await Device.getAll();
var configs = await Config.getAll();
var schedules = ScheduleManager.getAll();
var metadata = await Migrator.getEntries();
var data = defaultData;
data.metadata = metadata;
data.devices = devices.length;
data.configs = configs.length;
data.schedules = Object.keys(schedules).length;
data.username = username;
res.render('index', data);
}
});

app.get('/login', function(req, res) {
var data = defaultData;
data.logged_in = false;
data.username = null;
res.render('login', data);
});

app.get('/logout', function(req, res) {
req.session.destroy(function(err) {
if (err) throw err;
res.redirect('/login');
});
});

app.get('/account', function(req, res) {
var data = defaultData;
data.metadata = metadata;
data.devices = devices.length;
data.configs = configs.length;
res.render('index', data);
data.username = req.session.username;
res.render('account', data);
});

// Device UI Routes
Expand Down Expand Up @@ -163,6 +214,60 @@ app.get('/config/delete/:name', function(req, res) {
res.render('config-delete', data);
});

// Schedule UI Routes
app.get('/schedules', function(req, res) {
res.render('schedules', defaultData);
});

app.get('/schedule/new', async function(req, res) {
var data = defaultData;
var configs = await Config.getAll();
var devices = await Device.getAll();
data.configs = configs;
data.devices = devices;
data.timezones = timezones;
res.render('schedule-new', data);
});

app.get('/schedule/edit/:name', async function(req, res) {
var name = req.params.name;
var data = defaultData;
var configs = await Config.getAll();
var devices = await Device.getAll();
var schedule = ScheduleManager.getByName(name);
if (configs) {
configs.forEach(function(cfg) {
cfg.selected = cfg.name === schedule.config;
cfg.next_config_selected = cfg.name === schedule.next_config;
});
}
if (devices) {
devices.forEach(function(device) {
device.selected = schedule.uuids.includes(device.uuid);
});
}
data.old_name = name;
data.name = schedule.name;
data.configs = configs;
data.devices = devices;
data.start_time = schedule.start_time;
data.end_time = schedule.end_time;
data.timezone = schedule.timezone;
data.next_config = schedule.next_config;
data.enabled = schedule.enabled === 1 ? 'checked' : '';
timezones.forEach(function(timezone) {
timezone.selected = timezone.value === schedule.timezone;
});
data.timezones = timezones;
res.render('schedule-edit', data);
});

app.get('/schedule/delete/:name', function(req, res) {
var data = defaultData;
data.name = req.params.name;
res.render('schedule-delete', data);
});

// Settings UI Routes
app.get('/settings', function(req, res) {
res.render('settings', defaultData);
Expand Down
12 changes: 7 additions & 5 deletions src/migrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@ class Migrator {
var done = false;
while (!done) {
if (query === undefined || query === null) {
var message = `Failed to connect to database (as root) while initializing. Try: ${count}/10`;
console.error(`[DBController] Failed to connect to database (as root) while initializing. Try: ${count}/10`);
if (count === 10) {
console.error('[DBController]', message);
process.exit(-1);
} else {
console.error('[DBController]', message);
}
count++;
await utils.snooze(2500);
Expand Down Expand Up @@ -87,7 +84,8 @@ class Migrator {
sqlSplit.forEach(async sql => {
let msql = sql.replace('&semi', ';').trim();
if (msql !== '') {
let results = await query(msql)
console.log('[DBController] Executing:', msql);
var result = await query(msql)
.then(x => x)
.catch(async err => {
console.error('[DBController] Migration failed:', err, '\r\nExecuting SQL statement:', msql);
Expand All @@ -108,6 +106,7 @@ class Migrator {
return null;
*/
});
console.log('[DBController] Migration execution result:', result);
}
});

Expand All @@ -123,6 +122,9 @@ class Migrator {
process.exit(-1);
});
console.log('[DBController] Migration successful');
if (newVersion === toVersion) {
console.log('[DBController] Migration done');
}
this.migrate(newVersion, toVersion);
}
}
Expand Down
Loading

0 comments on commit 76664e8

Please sign in to comment.