Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ You can also pre-create devices and assign configs yourself if needed.
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!`

## 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!'));
45 changes: 45 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"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
73 changes: 59 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

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');
Expand All @@ -13,7 +15,13 @@ const apiRoutes = require('./routes/api.js');

// 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 @@ -27,26 +35,63 @@ app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' })); // for parsi
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 metadata = await Migrator.getEntries();
var data = defaultData;
data.metadata = metadata;
data.devices = devices.length;
data.configs = configs.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
1 change: 1 addition & 0 deletions src/migrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class Migrator {
sqlSplit.forEach(async sql => {
let msql = sql.replace('&semi', ';').trim();
if (msql !== '') {
console.log('[DBController] Executing:', msql);
let results = await query(msql)
.then(x => x)
.catch(async err => {
Expand Down
29 changes: 29 additions & 0 deletions src/models/account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

const query = require('../db.js');


class Account {
constructor() {
}
static async getAccount(username, password) {
var sql = `
SELECT username
FROM users
WHERE username = ? AND password = SHA1(?)`;
var args = [username, password];
var results = await query(sql, args);
return results && results.length > 0;
}
static async changePassword(username, password, newPassword) {
var sql = `
UPDATE users
SET password = SHA1(?)
WHERE username = ? AND password = SHA1(?)`;
var args = [newPassword, username, password];
var result = await query(sql, args);
return result.affectedRows === 1;
}
}

module.exports = Account;
52 changes: 52 additions & 0 deletions src/routes/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,63 @@ const query = require('../db.js');
const utils = require('../utils.js');
const upload = multer({ dest: '../../screenshots' });

const Account = require('../models/account.js');
const Config = require('../models/config.js');
const Device = require('../models/device.js');
const Log = require('../models/log.js');


// Authentication API Route
router.post('/login', async function(req, res) {
var username = req.body.username;
var password = req.body.password;
if (username && password) {
var result = await Account.getAccount(username, password);
if (result) {
req.session.loggedin = true;
req.session.username = username;
res.redirect('/');
return;
} else {
console.log('Incorrect Username and/or Password!');
}
} else {
console.log('Username or password is empty!');
}
res.redirect('/login');
});

router.post('/account/change_password/:username', async function(req, res) {
var username = req.params.username;
var oldPassword = req.body.old_password;
var password = req.body.password;
var password2 = req.body.password2;
//console.log('Username:', username, 'Old Password:', oldPassword, 'New Password:', password, 'Confirm Password:', password2);
// TODO: show error
if (password !== password2) {
console.error('Passwords do not match');
res.redirect('/account');
return;
}
var exists = await Account.getAccount(username, oldPassword);
if (exists) {
// TODO: Update account in database
var result = await Account.changePassword(username, oldPassword, password);
if (result) {
// Success
console.log(`Successfully changed password for user ${username} from ${oldPassword} to ${password}.`);
} else {
// Failed
console.log(`Unexpected error occurred trying to change password for user ${username}`);
}
} else {
// Failed
console.log(`Account with username ${username} does not exist`);
}
res.redirect('/account');
});


// Device API Routes
router.get('/devices', async function(req, res) {
try {
Expand Down
36 changes: 36 additions & 0 deletions src/views/account.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html="{{locale}}">
{{> header}}
<body>
{{> navbar}}
<br>
<h1 align="center" id="header">{{username}} Account</h1>
<br>
<div class="w-75" style="float: none; margin: 0 auto;">
<form action="/api/account/change_password/{{username}}" method="post">
<div class="form-group">
Old Password
<input type="password" class="form-control" name="old_password" value="" placeholder="" required>
</div>
<div class="form-group">
Password
<input type="password" class="form-control" name="password" value="" placeholder="" required>
</div>
<div class="form-group">
Confirm Password
<input type="password" class="form-control" name="password2" value="" placeholder="" required>
</div>
<br>
<button type="submit" class="btn btn-primary">Change</button>
<br>
</form>
</div>
</body>
</html>

<script>
if ("{{style}}" === 'dark') {
$('body').css('background-color', 'rgb(33, 37, 41)');
$('body').css('color', 'rgb(255, 255, 255)');
//$('#header').css('color', 'white');
}
</script>
32 changes: 32 additions & 0 deletions src/views/login.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html="{{locale}}">
{{> header}}
<body>
{{> navbar}}
<br>
<h1 align="center" id="header">Login</h1>
<br>
<div class="w-75" style="float: none; margin: 0 auto;">
<form action="/api/login" method="post">
<div class="form-group">
Username
<input type="text" class="form-control" name="username" value="" placeholder="" required>
</div>
<div class="form-group">
Password
<input type="password" class="form-control" name="password" value="" placeholder="" required>
</div>
<br>
<button type="submit" class="btn btn-primary">Login</button>
<br>
</form>
</div>
</body>
</html>

<script>
if ("{{style}}" === 'dark') {
$('body').css('background-color', 'rgb(33, 37, 41)');
$('body').css('color', 'rgb(255, 255, 255)');
//$('#header').css('color', 'white');
}
</script>
Loading