Skip to content

Commit e7ea997

Browse files
authored
Merge pull request versx#18 from versx/develop
Release 1.5
2 parents 4486c92 + fc07d10 commit e7ea997

File tree

11 files changed

+152
-63
lines changed

11 files changed

+152
-63
lines changed

migrations/5.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE `devices` ADD COLUMN `ios_version` VARCHAR(64) DEFAULT NULL;
2+
ALTER TABLE `devices` ADD COLUMN `ipa_version` VARCHAR(64) DEFAULT NULL;

src/index.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ const apiRoutes = require('./routes/api.js');
1717
const timezones = require('../static/data/timezones.json');
1818

1919
// TODO: Create route classes
20-
// TODO: iOS and IPA version
2120
// TODO: Fix devices scroll with DataTables
22-
// TODO: Fix dropdown closes when table freshes
21+
// TODO: Secure /api/config/:uuid endpoint with token
22+
// TODO: Provider option to show/hide config options
23+
// TODO: Accomodate for # in uuid name
2324

2425
const defaultData = {
2526
title: config.title,
@@ -36,6 +37,7 @@ dbMigrator.load();
3637
app.set('view engine', 'mustache');
3738
app.set('views', path.resolve(__dirname, 'views'));
3839
app.engine('mustache', mustacheExpress());
40+
app.use(bodyParser.json());
3941
app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' })); // for parsing application/x-www-form-urlencoded
4042
//app.use(bodyParser.raw({ type: 'application/x-www-form-urlencoded' }));
4143
app.use(express.static(path.resolve(__dirname, '../static')));
@@ -53,7 +55,7 @@ app.use('/api', apiRoutes);
5355

5456
// Login middleware
5557
app.use(function(req, res, next) {
56-
if (req.path === '/api/login' || req.path === '/login' || req.path.includes('/api/config/')) {
58+
if (req.path === '/api/login' || req.path === '/login' || req.path.includes('/api/config')) {
5759
return next();
5860
}
5961
if (req.session.loggedin) {
@@ -256,7 +258,31 @@ app.get('/schedule/delete/:name', function(req, res) {
256258

257259
// Settings UI Routes
258260
app.get('/settings', function(req, res) {
259-
res.render('settings', defaultData);
261+
var data = defaultData;
262+
data.title = config.title;
263+
data.host = config.db.host;
264+
data.port = config.db.port;
265+
data.username = config.db.username;
266+
data.password = config.db.password;
267+
data.database = config.db.database;
268+
data.charset = config.db.charset;
269+
data.styles = [
270+
{ 'name': 'dark' },
271+
{ 'name': 'light' }
272+
];
273+
data.styles.forEach(function(style) {
274+
style.selected = style.name === config.style;
275+
});
276+
data.languages = [
277+
{ 'name': 'en' },
278+
{ 'name': 'es' }
279+
];
280+
data.languages.forEach(function(locale) {
281+
locale.selected = locale.name === config.locale;
282+
});
283+
data.logging = config.logging ? 'checked' : '';
284+
console.log('Settings:', data);
285+
res.render('settings', data);
260286
});
261287

262288
app.listen(config.port, config.interface, () => console.log(`Listening on port ${config.port}...`));

src/models/config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const query = require('../db.js');
44

55
class Config {
66
constructor(name, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout,
7-
accountManager, deployEggs, nearbyTracker, autoLogin, isDefault) {
7+
accountManager, deployEggs, nearbyTracker, autoLogin, isDefault) {
88
this.name = name;
99
this.backendUrl = backendUrl;
1010
this.dataEndpoints = dataEndpoints;
@@ -50,13 +50,13 @@ class Config {
5050
return data;
5151
}
5252
static async create(name, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout,
53-
accountManager, deployEggs, nearbyTracker, autoLogin, isDefault) {
53+
accountManager, deployEggs, nearbyTracker, autoLogin, isDefault) {
5454
var sql = `
5555
INSERT INTO configs (name, backend_url, data_endpoints, token, heartbeat_max_time, min_delay_logout,
5656
account_manager, deploy_eggs, nearby_tracker, auto_login, is_default)
5757
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
5858
var args = [name, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout,
59-
accountManager, deployEggs, nearbyTracker, autoLogin, isDefault];
59+
accountManager, deployEggs, nearbyTracker, autoLogin, isDefault];
6060
var result = await query(sql, args);
6161
return result.affectedRows === 1;
6262
}

src/models/device.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@
33
const query = require('../db.js');
44

55
class Device {
6-
constructor(uuid, config, lastSeen, clientip) {
6+
constructor(uuid, config, lastSeen, clientip, iosVersion, ipaVersion) {
77
this.uuid = uuid;
88
this.config = config;
99
this.lastSeen = lastSeen;
1010
this.clientip = clientip;
11+
this.iosVersion = iosVersion;
12+
this.ipaVersion = ipaVersion;
1113
}
1214
static async getAll() {
13-
var devices = await query('SELECT uuid, config, last_seen, clientip FROM devices');
15+
var devices = await query('SELECT uuid, config, last_seen, clientip, ios_version, ipa_version FROM devices');
1416
return devices;
1517
}
1618
static async getByName(uuid) {
1719
var sql = `
18-
SELECT uuid, config, last_seen, clientip
20+
SELECT uuid, config, last_seen, clientip, ios_version, ipa_version
1921
FROM devices
2022
WHERE uuid = ?`;
2123
var args = [uuid];
@@ -27,21 +29,25 @@ class Device {
2729
result[0].uuid,
2830
result[0].config,
2931
result[0].last_seen,
30-
result[0].clientip
32+
result[0].clientip,
33+
result[0].ios_version,
34+
result[0].ipa_version
3135
);
3236
}
33-
static async create(uuid, config = null, lastSeen = null, clientip = null) {
37+
static async create(uuid, config = null, lastSeen = null, clientip = null, iosVersion = null, ipaVersion = null) {
3438
var sql = `
35-
INSERT INTO devices (uuid, config, last_seen, clientip)
36-
VALUES (?, ?, ?, ?)`;
37-
var args = [uuid, config, lastSeen, clientip];
39+
INSERT INTO devices (uuid, config, last_seen, clientip, ios_version, ipa_version)
40+
VALUES (?, ?, ?, ?, ?, ?)`;
41+
var args = [uuid, config, lastSeen, clientip, iosVersion, ipaVersion];
3842
var result = await query(sql, args);
3943
if (result.affectedRows === 1) {
4044
return new Device(
4145
uuid,
4246
config,
4347
lastSeen,
44-
clientip
48+
clientip,
49+
iosVersion,
50+
ipaVersion
4551
);
4652
}
4753
return null;
@@ -55,9 +61,9 @@ class Device {
5561
async save() {
5662
var sql = `
5763
UPDATE devices
58-
SET config = ?, last_seen = ?, clientip = ?
64+
SET config = ?, last_seen = ?, clientip = ?, ios_version = ?, ipa_version = ?
5965
WHERE uuid = ?`;
60-
var args = [this.config, this.lastSeen, this.clientip, this.uuid];
66+
var args = [this.config, this.lastSeen, this.clientip, this.iosVersion, this.ipaVersion, this.uuid];
6167
var result = await query(sql, args);
6268
return result.affectedRows === 1;
6369
}

src/models/log.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Log {
1515
this.timestamp = timestamp;
1616
this.message = message;
1717
}
18-
static async getByDevice(uuid) {
18+
static getByDevice(uuid) {
1919
var name = uuid + '.log';
2020
var logFile = path.resolve(logsDir, name);
2121
if (!fs.existsSync(logFile)) {
@@ -35,7 +35,7 @@ class Log {
3535
});
3636
return logs;
3737
}
38-
static async create(uuid, message) {
38+
static create(uuid, message) {
3939
var name = uuid + '.log';
4040
var logFile = path.resolve(logsDir, name);
4141
var msg = {
@@ -47,7 +47,7 @@ class Log {
4747
if (err) throw err;
4848
});
4949
}
50-
static async delete(uuid) {
50+
static delete(uuid) {
5151
var name = uuid + '.log';
5252
var logFile = path.resolve(logsDir, name);
5353
if (fs.existsSync(logFile)) {
@@ -56,7 +56,7 @@ class Log {
5656
}
5757
return false;
5858
}
59-
static async deleteAll() {
59+
static deleteAll() {
6060
fs.readdir(logsDir, function(err, files) {
6161
if (err) throw err;
6262
files.forEach(function(file) {

src/routes/api.js

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,43 @@ router.post('/account/change_password/:username', async function(req, res) {
6969
});
7070

7171

72+
// Settings API Routes
73+
router.post('/settings/change_ui', function(req, res) {
74+
var data = req.body;
75+
var newConfig = config;
76+
newConfig.title = data.title;
77+
newConfig.locale = data.locale;
78+
newConfig.style = data.style;
79+
newConfig.logging = data.logging === 'on' ? 1 : 0;
80+
fs.writeFileSync(path.resolve(__dirname, '../config.json'), JSON.stringify(newConfig, null, 2));
81+
res.redirect('/settings');
82+
});
83+
84+
router.post('/settings/change_db', function(req, res) {
85+
var data = req.body;
86+
var newConfig = config;
87+
newConfig.db.host = data.host;
88+
newConfig.db.port = data.port;
89+
newConfig.db.username = data.username;
90+
newConfig.db.password = data.password;
91+
newConfig.db.database = data.database;
92+
newConfig.db.charset = data.charset;
93+
fs.writeFileSync(path.resolve(__dirname, '../config.json'), JSON.stringify(newConfig, null, 2));
94+
res.redirect('/settings');
95+
});
96+
97+
7298
// Device API Routes
7399
router.get('/devices', async function(req, res) {
74100
try {
75101
var devices = await Device.getAll();
76102
devices.forEach(function(device) {
77103
var exists = fs.existsSync(path.join(screenshotsDir, device.uuid + '.png'));
78-
var image = exists ? `/screenshots/${device.uuid}.png` : '/img/offline.png';
79-
device.image = `<a href='${image}' target='_blank'><img src='${image}' width='64' height='96'/></a>`;
104+
// Device received a config last 15 minutes
105+
var delta = 15 * 60;
106+
var isOffline = device.last_seen > (Math.round((new Date()).getTime() / 1000) - delta) ? 0 : 1;
107+
var image = exists ? `/screenshots/${device.uuid}.png` : (isOffline ? '/img/offline.png' : '/img/online.png');
108+
device.image = `<a href='${image}' target='_blank'><img src='${image}' width='72' height='96'/></a>`;
80109
device.last_seen = utils.getDateTime(device.last_seen);
81110
device.buttons = `
82111
<div class='btn-group' role='group'>
@@ -167,12 +196,15 @@ router.get('/configs', async function(req, res) {
167196
}
168197
});
169198

170-
router.get('/config/:uuid', async function(req, res) {
171-
var uuid = req.params.uuid;
199+
router.post('/config', async function(req, res) {
200+
var data = req.body;
201+
var uuid = data.uuid;
202+
var iosVersion = data.ios_version;
203+
var ipaVersion = data.ipa_version;
172204
var device = await Device.getByName(uuid);
173205
var noConfig = false;
174206
var assignDefault = false;
175-
// Check for a proxied IP before the normal IP and set the first one at exists
207+
// Check for a proxied IP before the normal IP and set the first one that exists
176208
var clientip = ((req.headers['x-forwarded-for'] || '').split(', ')[0]) || (req.connection.remoteAddress).match('[0-9]+.[0-9].+[0-9]+.[0-9]+$')[0];
177209
console.log('[' + new Date().toLocaleString() + ']', 'Client', uuid, 'at', clientip, 'is requesting a config.');
178210

@@ -181,6 +213,8 @@ router.get('/config/:uuid', async function(req, res) {
181213
// Device exists
182214
device.lastSeen = new Date() / 1000;
183215
device.clientip = clientip;
216+
device.iosVersion = iosVersion;
217+
device.ipaVersion = ipaVersion;
184218
device.save();
185219
if (device.config) {
186220
// Nothing to do besides respond with config
@@ -192,7 +226,8 @@ router.get('/config/:uuid', async function(req, res) {
192226
} else {
193227
console.log('Device does not exist, creating...');
194228
// Device doesn't exist, create db entry
195-
device = await Device.create(uuid, null, new Date() / 1000, clientip); // REVIEW: Maybe return Device object upon creation to prevent another sql call to get Device object?
229+
var ts = new Date() / 1000;
230+
device = await Device.create(uuid, null, ts, clientip, iosVersion, ipaVersion);
196231
if (device) {
197232
// Success, assign default config if there is one.
198233
assignDefault = true;
@@ -227,11 +262,11 @@ router.get('/config/:uuid', async function(req, res) {
227262
var c = await Config.getByName(device.config);
228263
if (c === null) {
229264
console.error('Failed to grab config', device.config);
230-
var data = {
265+
var noConfigData2 = {
231266
status: 'error',
232267
error: 'Device not assigned to config!'
233268
};
234-
res.send(JSON.stringify(data));
269+
res.send(JSON.stringify(noConfigData2));
235270
return;
236271
}
237272
// Build json config
@@ -397,9 +432,9 @@ router.get('/schedule/delete_all', function(req, res) {
397432

398433

399434
// Logging API requests
400-
router.get('/logs/:uuid', async function(req, res) {
435+
router.get('/logs/:uuid', function(req, res) {
401436
var uuid = req.params.uuid;
402-
var logs = await Log.getByDevice(uuid);
437+
var logs = Log.getByDevice(uuid);
403438
res.send({
404439
uuid: uuid,
405440
data: {
@@ -408,33 +443,37 @@ router.get('/logs/:uuid', async function(req, res) {
408443
});
409444
});
410445

411-
router.post('/log/new/:uuid', async function(req, res) {
446+
router.post('/log/new', function(req, res) {
412447
if (config.logging === false) {
413448
// Logs are disabled
414449
res.send('OK');
415450
return;
416451
}
417-
var uuid = req.params.uuid;
418-
var msg = Object.keys(req.body)[0]; // Dumb hack
419-
var result = await Log.create(uuid, msg);
420-
if (result) {
421-
// Success
452+
var uuid = req.body.uuid;
453+
var messages = req.body.messages;
454+
if (messages) {
455+
messages.forEach(function(message) {
456+
var result = Log.create(uuid, message);
457+
if (result) {
458+
// Success
459+
}
460+
console.log('[SYSLOG]', uuid, ':', message);
461+
});
422462
}
423-
console.log('[SYSLOG]', uuid, ':', msg);
424463
res.send('OK');
425464
});
426465

427-
router.get('/log/delete/:uuid', async function(req, res) {
466+
router.get('/log/delete/:uuid', function(req, res) {
428467
var uuid = req.params.uuid;
429-
var result = await Log.delete(uuid);
468+
var result = Log.delete(uuid);
430469
if (result) {
431470
// Success
432471
}
433472
res.redirect('/device/logs/' + uuid);
434473
});
435474

436-
router.get('/logs/delete_all', async function(req, res) {
437-
var result = await Log.deleteAll();
475+
router.get('/logs/delete_all', function(req, res) {
476+
var result = Log.deleteAll();
438477
if (result) {
439478
// Success
440479
}

src/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ function getDateTime(timestamp) {
1818
}
1919

2020
function buildConfig(backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout,
21-
accountManager, deployEggs, nearbyTracker, autoLogin) {
21+
accountManager, deployEggs, nearbyTracker, autoLogin) {
2222
var obj = {
2323
'backend_url': backendUrl,
2424
'data_endpoints': (dataEndpoints || '').split(',') || [],

src/views/device-manage.mustache

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,23 @@
116116
}
117117
118118
function get(url, id) {
119+
if (url.includes('/screen')) {
120+
121+
}
122+
var isScreen = url.includes('/screen');
119123
$.ajax({
120124
url: url,
121125
type: 'GET',
122-
dataType: 'json',
126+
dataType: isScreen ? 'image/jpeg' : 'json',
123127
success: function (data) {
124128
console.log("Ajax response:", data);
125-
$('#' + id).text(JSON.stringify(data, null, 2));
129+
if (isScreen) {
130+
img = new Image();
131+
img.src = data;
132+
$('#' + id).append(img);
133+
} else {
134+
$('#' + id).text(JSON.stringify(data, null, 2));
135+
}
126136
},
127137
error: function (error) {
128138
console.log("Error:", error);

0 commit comments

Comments
 (0)