diff --git a/migrations/6.sql b/migrations/6.sql new file mode 100644 index 0000000..f8ec76a --- /dev/null +++ b/migrations/6.sql @@ -0,0 +1 @@ +ALTER TABLE `configs` ADD COLUMN `provider` varchar(64) NOT NULL AFTER `name`; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e9b6962..da946a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -783,6 +783,26 @@ "toidentifier": "1.0.0" } }, + "i18n": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.9.0.tgz", + "integrity": "sha512-/Sie5k0xz8VoB0dilo0OPJic5gukT6j+/24aprosU700iWgHcyP5GqDwgNuNscwK2YkGHRE3fO/wn5d2Nc7dBw==", + "requires": { + "debug": "*", + "make-plural": "^6.0.1", + "math-interval-parser": "^2.0.1", + "messageformat": "^2.3.0", + "mustache": "*", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1005,6 +1025,16 @@ "yallist": "^3.0.2" } }, + "make-plural": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.1.tgz", + "integrity": "sha512-AmkruwJ9EjvyTv6AM8MBMK3TAeOJvhgTv5YQXzF0EP2qawhpvMjDpHvsdOIIT0Vn+BB0+IogmYZ1z+Ulm/m0Fg==" + }, + "math-interval-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz", + "integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1015,6 +1045,36 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "messageformat": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-2.3.0.tgz", + "integrity": "sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==", + "requires": { + "make-plural": "^4.3.0", + "messageformat-formatters": "^2.0.1", + "messageformat-parser": "^4.1.2" + }, + "dependencies": { + "make-plural": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", + "integrity": "sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==", + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "messageformat-formatters": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz", + "integrity": "sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==" + }, + "messageformat-parser": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", + "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", diff --git a/package.json b/package.json index ded5578..e94a0fc 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,9 @@ "license": "ISC", "dependencies": { "express": "^4.17.1", - "moment-timezone": "^0.5.28", "express-session": "^1.17.1", + "i18n": "^0.9.0", + "moment-timezone": "^0.5.28", "multer": "^1.4.2", "mustache": "^4.0.1", "mustache-express": "^1.3.0", diff --git a/src/config.example.json b/src/config.example.json index a0e6153..611e8f9 100644 --- a/src/config.example.json +++ b/src/config.example.json @@ -4,8 +4,11 @@ "port": 9991, "locale": "en", "style": "dark", - "logging": true, "secret": "-/!sup3rr4nd0m70p53cr3t70k3ny0u5h0u1dch4ng3!/-", + "logging": { + "enabled": true, + "max_size": 5 + }, "db": { "host": "127.0.0.1", "port": 3306, diff --git a/src/index.js b/src/index.js index 4c422e1..66db5a7 100644 --- a/src/index.js +++ b/src/index.js @@ -6,10 +6,13 @@ const session = require('express-session'); const bodyParser = require('body-parser'); const app = express(); const mustacheExpress = require('mustache-express'); +const i18n = require('i18n'); const config = require('./config.json'); +const utils = require('./utils.js'); const Device = require('./models/device.js'); const Config = require('./models/config.js'); +const Log = require('./models/log.js'); const Migrator = require('./migrator.js'); const ScheduleManager = require('./models/schedule-manager.js'); const apiRoutes = require('./routes/api.js'); @@ -18,30 +21,74 @@ const timezones = require('../static/data/timezones.json'); // TODO: Create route classes // TODO: Fix devices scroll with DataTables -// TODO: Secure /api/config/:uuid endpoint with token -// TODO: Provider option to show/hide config options +// TODO: Delete all logs button +// TODO: Secure /api/config endpoint with token // TODO: Accomodate for # in uuid name +// TODO: Fix schedule end time +// TODO: Center align data in table columns +// TODO: Change require to import -const defaultData = { - title: config.title, - locale: config.locale, - style: config.style == 'dark' ? 'dark' : '', - logging: config.logging -}; +const providers = [ + { name: 'GoCheats' }, + { name: 'Kevin' }, +]; -// Start database migrator -var dbMigrator = new Migrator(); -dbMigrator.load(); +run(); -// Middleware +async function run() { + // Start database migrator + var dbMigrator = new Migrator(); + dbMigrator.load(); + while (dbMigrator.done === false) { + await utils.snooze(1000); + } + app.listen(config.port, config.interface, () => console.log(`Listening on port ${config.port}...`)); +} + +i18n.configure({ + locales:['en', 'es', 'de'], + directory: path.resolve(__dirname, '../static/locales') +}); + +// View engine app.set('view engine', 'mustache'); app.set('views', path.resolve(__dirname, 'views')); app.engine('mustache', mustacheExpress()); + +// Static paths +app.use(express.static(path.resolve(__dirname, '../static'))); +//app.use('/logs', express.static(path.resolve(__dirname, '../logs'))); +app.use('/screenshots', express.static(path.resolve(__dirname, '../screenshots'))); + +//app.use(express.cookieParser()); +app.use(i18n.init); + +// register helper as a locals function wrapped as mustache expects +app.use(function (req, res, next) { + // mustache helper + res.locals.__ = function() { + /* eslint-disable no-unused-vars */ + return function(text, render) { + return i18n.__.apply(req, arguments); + }; + /* eslint-disable no-unused-vars */ + }; + next(); +}); + +// Default mustache data shared between pages +const defaultData = require('../static/locales/' + config.locale + '.json'); +defaultData.title = config.title; +defaultData.locale = config.locale; +defaultData.style = config.style == 'dark' ? 'dark' : ''; +defaultData.logging = config.logging.enabled; + +i18n.setLocale(config.locale); + +// Body parser middlewares app.use(bodyParser.json()); 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'))); // Sessions middleware app.use(session({ @@ -74,12 +121,14 @@ app.get(['/', '/index'], async function(req, res) { var configs = await Config.getAll(); var schedules = ScheduleManager.getAll(); var metadata = await Migrator.getEntries(); + var logsSize = Log.getTotalSize(); var data = defaultData; data.metadata = metadata; data.devices = devices.length; data.configs = configs.length; data.schedules = Object.keys(schedules).length; data.username = username; + data.logs_size = utils.formatBytes(logsSize); res.render('index', data); } }); @@ -110,6 +159,8 @@ app.get('/devices', function(req, res) { }); app.get('/device/new', async function(req, res) { + var data = defaultData; + data.configs = await Config.getAll(); res.render('device-new', defaultData); }); @@ -173,7 +224,9 @@ app.get('/config/assign/:uuid', async function(req, res) { }); app.get('/config/new', function(req, res) { - res.render('config-new', defaultData); + var data = defaultData; + data.providers = providers; + res.render('config-new', data); }); app.get('/config/edit/:name', async function(req, res) { @@ -183,6 +236,11 @@ app.get('/config/edit/:name', async function(req, res) { data.title = config.title; data.old_name = name; data.name = c.name; + data.providers = providers; + data.providers.forEach(function(provider) { + provider.selected = provider.name === c.provider; + }); + data.gocheats_selected = c.provider === data.providers[0].name; data.backend_url = c.backendUrl; data.data_endpoints = c.dataEndpoints; data.token = c.token; @@ -280,9 +338,8 @@ app.get('/settings', function(req, res) { data.languages.forEach(function(locale) { locale.selected = locale.name === config.locale; }); - data.logging = config.logging ? 'checked' : ''; + data.logging = config.logging.enabled ? 'checked' : ''; + data.max_size = config.logging.max_size; console.log('Settings:', data); res.render('settings', data); -}); - -app.listen(config.port, config.interface, () => console.log(`Listening on port ${config.port}...`)); \ No newline at end of file +}); \ No newline at end of file diff --git a/src/migrator.js b/src/migrator.js index d068a59..334f63d 100644 --- a/src/migrator.js +++ b/src/migrator.js @@ -9,6 +9,7 @@ const migrationsDir = path.resolve(__dirname, '../migrations'); class Migrator { constructor() { + this.done = false; } async load() { var count = 1; @@ -123,11 +124,12 @@ class Migrator { process.exit(-1); }); console.log('[DBController] Migration successful'); - if (newVersion === toVersion) { - console.log('[DBController] Migration done'); - } this.migrate(newVersion, toVersion); } + if (fromVersion === toVersion) { + console.log('[DBController] Migration done'); + this.done = true; + } } backup() { // TODO: Migrator backup diff --git a/src/models/config.js b/src/models/config.js index 11e73ed..d281868 100644 --- a/src/models/config.js +++ b/src/models/config.js @@ -3,9 +3,10 @@ const query = require('../db.js'); class Config { - constructor(name, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout, + constructor(name, provider, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout, accountManager, deployEggs, nearbyTracker, autoLogin, isDefault) { this.name = name; + this.provider = provider; this.backendUrl = backendUrl; this.dataEndpoints = dataEndpoints; this.token = token; @@ -23,7 +24,7 @@ class Config { } static async getByName(name) { var sql = ` - SELECT backend_url, data_endpoints, token, heartbeat_max_time, min_delay_logout, + SELECT backend_url, provider, data_endpoints, token, heartbeat_max_time, min_delay_logout, account_manager, deploy_eggs, nearby_tracker, auto_login, is_default FROM configs WHERE name = ? @@ -36,6 +37,7 @@ class Config { var c = result[0]; var data = new Config( name, + c.provider, c.backend_url, c.data_endpoints, c.token, @@ -49,13 +51,13 @@ class Config { ); return data; } - static async create(name, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout, + static async create(name, provider, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout, accountManager, deployEggs, nearbyTracker, autoLogin, isDefault) { var sql = ` - INSERT INTO configs (name, backend_url, data_endpoints, token, heartbeat_max_time, min_delay_logout, + INSERT INTO configs (name, provider, backend_url, data_endpoints, token, heartbeat_max_time, min_delay_logout, account_manager, deploy_eggs, nearby_tracker, auto_login, is_default) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; - var args = [name, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout, + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + var args = [name, provider, backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout, accountManager, deployEggs, nearbyTracker, autoLogin, isDefault]; var result = await query(sql, args); return result.affectedRows === 1; @@ -68,7 +70,7 @@ class Config { } static async getDefault() { var sql = ` - SELECT name, backend_url, data_endpoints, token, heartbeat_max_time, min_delay_logout, + SELECT name, provider, backend_url, data_endpoints, token, heartbeat_max_time, min_delay_logout, account_manager, deploy_eggs, nearby_tracker, auto_login, is_default FROM configs WHERE is_default = 1 @@ -80,6 +82,7 @@ class Config { var c = result[0]; var data = new Config( c.name, + c.provider, c.backend_url, c.data_endpoints, c.token, @@ -95,7 +98,7 @@ class Config { } static async setDefault(name) { var sql = ` - UPDATE dcm.configs + UPDATE configs SET is_default = IF(name = ?, 1, 0);`; var args = [name]; var result = await query(sql, args); @@ -104,11 +107,12 @@ class Config { async save(oldName) { var sql = ` UPDATE configs - SET name=?, backend_url=?, data_endpoints=?, token=?, heartbeat_max_time=?, min_delay_logout=?, + SET name=?, provider=?, backend_url=?, data_endpoints=?, token=?, heartbeat_max_time=?, min_delay_logout=?, account_manager=?, deploy_eggs=?, nearby_tracker=?, auto_login=?, is_default=? WHERE name=?`; var args = [ this.name, + this.provider, this.backendUrl, this.dataEndpoints, this.token, diff --git a/src/models/log.js b/src/models/log.js index 21f755d..4ce64fd 100644 --- a/src/models/log.js +++ b/src/models/log.js @@ -3,6 +3,7 @@ const fs = require('fs'); const path = require('path'); const utils = require('../utils.js'); +const config = require('../config.json'); const logsDir = path.resolve(__dirname, '../../logs'); if (!fs.existsSync(logsDir)) { @@ -38,6 +39,13 @@ class Log { static create(uuid, message) { var name = uuid + '.log'; var logFile = path.resolve(logsDir, name); + if (fs.existsSync(logFile)) { + var size = fs.statSync(logFile).size || 0; + var maxSize = (config.logging.max_size || 5) * 1024 * 1024; + if (size >= maxSize) { + this.delete(uuid); + } + } var msg = { message: message, timestamp: new Date() / 1000, @@ -67,6 +75,19 @@ class Log { }); }); } + static getTotalSize() { + var total = 0; + if (!fs.existsSync(logsDir)) { + return total; + } + var logs = fs.readdirSync(logsDir); + logs.forEach(function(log) { + var logFile = path.join(logsDir, log); + var stats = fs.statSync(logFile); + total += stats.size; + }); + return total; + } } module.exports = Log; \ No newline at end of file diff --git a/src/routes/api.js b/src/routes/api.js index 5074523..e4ad098 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -76,7 +76,8 @@ router.post('/settings/change_ui', function(req, res) { newConfig.title = data.title; newConfig.locale = data.locale; newConfig.style = data.style; - newConfig.logging = data.logging === 'on' ? 1 : 0; + newConfig.logging.enabled = data.logging === 'on'; + newConfig.logging.max_size = data.max_size; fs.writeFileSync(path.resolve(__dirname, '../config.json'), JSON.stringify(newConfig, null, 2)); res.redirect('/settings'); }); @@ -269,6 +270,7 @@ router.post('/config', async function(req, res) { res.send(JSON.stringify(noConfigData2)); return; } + // Build json config var json = utils.buildConfig( c.backendUrl, @@ -302,8 +304,9 @@ router.post('/config/assign/:uuid', async function(req, res) { router.post('/config/new', async function(req, res) { var data = req.body; - var result = await Config.create( + var cfg = await Config.create( data.name, + data.provider, data.backend_url, data.data_endpoints, data.token, @@ -315,8 +318,12 @@ router.post('/config/new', async function(req, res) { data.auto_login === 'on' ? 1 : 0, data.is_default === 'on' ? 1 : 0 ); - if (result) { + if (cfg) { console.log('Config inserted'); + if (cfg.isDefault) { + console.log('Setting default config:', data.name); + await Config.setDefault(data.name); + } } else { console.error('Failed to create new config'); } @@ -328,6 +335,7 @@ router.post('/config/edit/:name', async function(req, res) { var data = req.body; var c = await Config.getByName(oldName); c.name = data.name; + c.provider = data.provider; c.backendUrl = data.backend_url; c.dataEndpoints = data.data_endpoints; c.token = data.token; @@ -339,9 +347,11 @@ router.post('/config/edit/:name', async function(req, res) { c.autoLogin = data.auto_login === 'on' ? 1 : 0; c.isDefault = data.is_default === 'on' ? 1 : 0; if (await c.save(oldName)) { + console.log('Config saved'); // Success if (c.isDefault) { - await Config.setDefault(oldName); + console.log('Setting default config:', c.name); + await Config.setDefault(c.name); } } res.redirect('/configs'); @@ -444,7 +454,7 @@ router.get('/logs/:uuid', function(req, res) { }); router.post('/log/new', function(req, res) { - if (config.logging === false) { + if (config.logging.enabled === false) { // Logs are disabled res.send('OK'); return; @@ -472,6 +482,16 @@ router.get('/log/delete/:uuid', function(req, res) { res.redirect('/device/logs/' + uuid); }); +router.get('/log/export/:uuid', function(req, res) { + var uuid = req.params.uuid; + var logs = Log.getByDevice(uuid); + var logText = ''; + logs.forEach(function(log) { + logText += `${log.timestamp} ${log.uuid} ${log.message}\n`; + }); + res.send(logText); +}); + router.get('/logs/delete_all', function(req, res) { var result = Log.deleteAll(); if (result) { diff --git a/src/utils.js b/src/utils.js index 1be3e7c..de96926 100644 --- a/src/utils.js +++ b/src/utils.js @@ -19,6 +19,7 @@ function getDateTime(timestamp) { function buildConfig(backendUrl, dataEndpoints, token, heartbeatMaxTime, minDelayLogout, accountManager, deployEggs, nearbyTracker, autoLogin) { + // TODO: Check provider against keys needed (or just assume?) and only return those keys. var obj = { 'backend_url': backendUrl, 'data_endpoints': (dataEndpoints || '').split(',') || [], @@ -34,9 +35,29 @@ function buildConfig(backendUrl, dataEndpoints, token, heartbeatMaxTime, minDela return json; } +function saveDataAsImage(name, imgData) { + var data = imgData.replace(/^data:image\/\w+;base64,/, ''); + var buf = new Buffer(data, 'base64'); + fs.writeFileSync('../screenshots/' + name, buf); +} + +function formatBytes(bytes) { + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + if (bytes === 0) { + return 0; + } + const index = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + if (index == 0) { + return bytes + ' ' + sizes[index]; + } + return (bytes / Math.pow(1024, index)).toFixed(1) + ' ' + sizes[index]; +} + module.exports = { readFile, snooze, getDateTime, - buildConfig + buildConfig, + saveDataAsImage, + formatBytes }; \ No newline at end of file diff --git a/src/views/account.mustache b/src/views/account.mustache index dbfa6e1..a2305da 100644 --- a/src/views/account.mustache +++ b/src/views/account.mustache @@ -3,28 +3,28 @@ {{> navbar}}
-

{{username}} Account Profile

+

{{username}} {{Account Profile}}


-
Change Password
+
{{Change Password}}
- Old Password + {{Old Password}}
- Password + {{Password}}
- Confirm Password + {{Confirm Password}}

- +
diff --git a/src/views/config-assign.mustache b/src/views/config-assign.mustache index 1faf850..e35e26c 100644 --- a/src/views/config-assign.mustache +++ b/src/views/config-assign.mustache @@ -3,20 +3,20 @@ {{> navbar}}
-

Assign Config to {{device}}

+

{{Assign Config to}} {{device}}


- Select Device Config + {{Select Device Config}}
- +

diff --git a/src/views/config-delete.mustache b/src/views/config-delete.mustache index f647a57..0726bb5 100644 --- a/src/views/config-delete.mustache +++ b/src/views/config-delete.mustache @@ -3,14 +3,14 @@ {{> navbar}}
-

Delete Device Config {{name}}

+

{{Delete Device Config}} {{name}}


-

Are you sure that you want to delete device config {{name}}?

- Cancel - +

{{Are you sure that you want to delete device config}} {{name}}?

+ {{Cancel}} +

diff --git a/src/views/config-edit.mustache b/src/views/config-edit.mustache index 2b9a7f5..3fa2c97 100644 --- a/src/views/config-edit.mustache +++ b/src/views/config-edit.mustache @@ -3,58 +3,67 @@ {{> navbar}}
-

Edit Device Config {{old_name}}

+

{{Edit Device Config}} {{old_name}}


- Name + {{Name}}
- Backend Url - + {{Provider}} +
- Data Endpoints (Separated by a `,`) - + {{Backend Url}} +
- Backend Token - + {{Data Endpoints (Separated by a `,`)}} +
- Heartbeat Max Time + {{Backend Token}} + +
+
+ {{Heartbeat Max Time}}
-
- Minimum Delay Logout +
+ {{Minimum Delay Logout}}
-
+ -
+
- +
-
+
- +
-
+ - +
- +

- +
@@ -67,4 +76,21 @@ $('body').css('color', 'rgb(255, 255, 255)'); //$('#header').css('color', 'white'); } + $('.provider-select').change(function() { + if ($(this).val() === 'GoCheats') { + $('.heartbeat_max_time').addClass('d-none'); + $('.min_delay_logout').addClass('d-none'); + $('.account_manager').addClass('d-none'); + $('.deploy_eggs').addClass('d-none'); + $('.nearby_tracker').addClass('d-none'); + $('.auto_login').addClass('d-none'); + } else { + $('.heartbeat_max_time').removeClass('d-none'); + $('.min_delay_logout').removeClass('d-none'); + $('.account_manager').removeClass('d-none'); + $('.deploy_eggs').removeClass('d-none'); + $('.nearby_tracker').removeClass('d-none'); + $('.auto_login').removeClass('d-none'); + } + }); \ No newline at end of file diff --git a/src/views/config-new.mustache b/src/views/config-new.mustache index 8229aca..f8354c6 100644 --- a/src/views/config-new.mustache +++ b/src/views/config-new.mustache @@ -3,58 +3,67 @@ {{> navbar}}
-

New Device Config

+

{{New Device Config}}


- Name + {{Name}}
- Backend Url - + {{Provider}} +
- Data Endpoints (Separated by a `,`) - + {{Backend Url}} +
- Backend Token - + {{Data Endpoints (Separated by a `,`)}} +
- Heartbeat Max Time + {{Backend Token}} + +
+
+ {{Heartbeat Max Time}}
-
- Minimum Delay Logout +
+ {{Minimum Delay Logout}}
-
+ -
+
- +
-
+
- +
-
+ - +
- +

- +
@@ -62,9 +71,27 @@ \ No newline at end of file diff --git a/src/views/configs.mustache b/src/views/configs.mustache index a19ea1c..9f17ad0 100644 --- a/src/views/configs.mustache +++ b/src/views/configs.mustache @@ -3,19 +3,20 @@ {{> navbar}}
-

Configs

+

{{Configs}}


- New Config + {{New Config}}

- - - - - + + + + + + @@ -44,6 +45,7 @@ "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]], "columns": [ { "data": "name" }, + { "data": "provider" }, { "data": "backend_url" }, { "data": "data_endpoints" }, { "data": "token" }, @@ -54,7 +56,7 @@ "order": [[ 0, "asc" ]], "search.caseInsensitive": true, "columnDefs": [ { - "targets": [5], + "targets": [6], "orderable": false }], "responsive": true diff --git a/src/views/device-delete.mustache b/src/views/device-delete.mustache index ad0d6e8..0b10c2f 100644 --- a/src/views/device-delete.mustache +++ b/src/views/device-delete.mustache @@ -3,14 +3,14 @@ {{> navbar}}
-

Delete Device {{uuid}}

+

{{Delete Device}} {{uuid}}


-

Are you sure that you want to delete device {{uuid}}?

- Cancel - +

{{Are you sure that you want to delete device}} {{uuid}}?

+ {{Cancel}} +

diff --git a/src/views/device-logs.mustache b/src/views/device-logs.mustache index 2425bf6..d90abd5 100644 --- a/src/views/device-logs.mustache +++ b/src/views/device-logs.mustache @@ -3,17 +3,18 @@ {{> navbar}}
-

{{uuid}} Device Logs

+

{{uuid}} {{Device Logs}}


NameBackendData EndpointsTokenIs Default{{Name}}{{Provider}}{{Backend}}{{Data Endpoints}}{{Token}}{{Is Default}}
- - - + + + @@ -38,7 +39,7 @@ }, "paging": true, "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]], - "pageLength": 50, + "pageLength": 100, "columns": [ { "data": "date" }, { "data": "uuid" }, @@ -59,5 +60,34 @@ table.ajax.reload(null, false); }, 5000); } ); - } ); + }); + + function exportLogs() { + $.ajax({ + url: '/api/log/export/{{uuid}}', + type: 'GET', + success: function(data, textStatus, jqXHR) { + //console.log("RECEIVED:", data); + downloader(data, 'html/text', '{{uuid}}.log'); + }, + error: function(jqXHR, textStatus, errorThrown) { + console.error("ERROR:", textStatus); + console.error("ERROR THROWN:", errorThrown); + } + }); + } + + function downloader(data, type, name) { + let blob = new Blob([data], {type}); + let url = window.URL.createObjectURL(blob); + downloadURI(url, name); + window.URL.revokeObjectURL(url); + } + + function downloadURI(uri, name) { + let link = document.createElement("a"); + link.download = name; + link.href = uri; + link.click(); + } diff --git a/src/views/device-manage.mustache b/src/views/device-manage.mustache index 59c511c..e960004 100644 --- a/src/views/device-manage.mustache +++ b/src/views/device-manage.mustache @@ -3,51 +3,51 @@ {{> navbar}}
-

{{name}} Management

+

{{name}} {{Management}}


DateUUIDMessage{{Date}}{{UUID}}{{Message}}
- - - + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -87,20 +87,20 @@ - + - - + +
DescriptionActionResponse{{Description}}{{Action}}{{Response}}
Get Screenshot
{{Get Screenshot}}
Get Current Location{{Get Current Location}}
Get Current Config Data{{Get Current Config Data}}
Get Current Account Data{{Get Current Account Data}}
Get System Information Data{{Get System Information Data}}
Restart Game{{Restart Game}}
Reboot Device (WIP currently restarts the game){{Reboot Device}} (WIP currently restarts the game)
Send Typing Data (WIP){{Send Typing Data}} (WIP)
- +
Delete Device{{Delete Device}}
@@ -116,27 +116,38 @@ } function get(url, id) { - if (url.includes('/screen')) { - - } + var uuid = '{{uuid}}'; var isScreen = url.includes('/screen'); $.ajax({ url: url, type: 'GET', - dataType: isScreen ? 'image/jpeg' : 'json', - success: function (data) { - console.log("Ajax response:", data); + beforeSend: function (xhr) { + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + }, + dataType: isScreen ? null : 'json', + async: true, + success: function (result, textStatus, jqXHR) { if (isScreen) { - img = new Image(); - img.src = data; - $('#' + id).append(img); + if (result.length < 1) { + $('#' + id).attr('src', 'data:image/png;base64,'); + return; + } + var binary = ""; + var responseText = jqXHR.responseText; + var responseTextLen = responseText.length; + for ( i = 0; i < responseTextLen; i++ ) { + binary += String.fromCharCode(responseText.charCodeAt(i) & 255) + } + //utils.saveDataAsImage(uuid + '.png', binary); + $('#' + id).attr('src', 'data:image/png;base64,' + btoa(binary)); } else { - $('#' + id).text(JSON.stringify(data, null, 2)); + $('#' + id).text(JSON.stringify(result, null, 2)); } }, - error: function (error) { - console.log("Error:", error); - } + error: function(xhr, textStatus, errorThrown) { + console.error('Error:', textStatus); + console.error('Error Thrown:', errorThrown); + } }); } diff --git a/src/views/device-new.mustache b/src/views/device-new.mustache index 47da8e8..36cfe8b 100644 --- a/src/views/device-new.mustache +++ b/src/views/device-new.mustache @@ -3,29 +3,29 @@ {{> navbar}}
-

New Device

+

{{New Device}}


- UUID + {{UUID}}
- Select Device Config + {{Select Device Config}}
- Device IP + {{Device IP}}

- +
diff --git a/src/views/devices.mustache b/src/views/devices.mustache index 76944d1..da53881 100644 --- a/src/views/devices.mustache +++ b/src/views/devices.mustache @@ -3,20 +3,20 @@ {{> navbar}}
-

Devices

+

{{Devices}}


- New Device + {{New Device}}

- - - - - - + + + + + + @@ -42,6 +42,7 @@ }, "paging": true, "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]], + "pageLength": 100, "columns": [ { "data": "image" }, { "data": "uuid" }, diff --git a/src/views/index.mustache b/src/views/index.mustache index 3540f52..f1ac8af 100644 --- a/src/views/index.mustache +++ b/src/views/index.mustache @@ -7,34 +7,43 @@
PreviewUUIDConfigiOS VersionIPA VersionLast Seen{{Preview}}{{UUID}}{{Config}}{{iOS Version}}{{IPA Version}}{{Last Config}}
- - + + diff --git a/src/views/login.mustache b/src/views/login.mustache index ce7bd10..8f378bb 100644 --- a/src/views/login.mustache +++ b/src/views/login.mustache @@ -3,20 +3,20 @@ {{> navbar}}
-

Login

+

{{Login}}


- Username + {{Username}}
- Password + {{Password}}

- +
diff --git a/src/views/navbar.mustache b/src/views/navbar.mustache index 710bd91..2880940 100644 --- a/src/views/navbar.mustache +++ b/src/views/navbar.mustache @@ -12,16 +12,16 @@ {{#logged_in}} diff --git a/src/views/schedule-delete.mustache b/src/views/schedule-delete.mustache index 2ec7461..31081ca 100644 --- a/src/views/schedule-delete.mustache +++ b/src/views/schedule-delete.mustache @@ -3,14 +3,14 @@ {{> navbar}}
-

Delete Schedule {{name}}

+

{{Delete Schedule}} {{name}}


-

Are you sure that you want to delete schedule {{name}}?

- Cancel - +

{{Are you sure that you want to delete schedule}} {{name}}?

+ {{Cancel}} +

diff --git a/src/views/schedule-edit.mustache b/src/views/schedule-edit.mustache index 684dfe2..d8b3f69 100644 --- a/src/views/schedule-edit.mustache +++ b/src/views/schedule-edit.mustache @@ -3,53 +3,53 @@ {{> navbar}}
-

Edit Device Config Schedule {{old_name}}

+

{{Edit Device Config Schedule}} {{old_name}}


- Name + {{Name}}
- Select Config + {{Select Config}}
- Select Device(s) + {{Select Device(s)}}
- Start Time + {{Start Time}}
- End Time + {{End Time}}
- Time Zone + {{Time Zone}}
- Select On Complete Config + {{Select On Complete Config}} - +

- +
diff --git a/src/views/schedule-new.mustache b/src/views/schedule-new.mustache index 23f9d6b..75c00c1 100644 --- a/src/views/schedule-new.mustache +++ b/src/views/schedule-new.mustache @@ -3,53 +3,53 @@ {{> navbar}}
-

New Device Config Schedule

+

{{New Device Config Schedule}}


- Name + {{Name}}
- Select Config + {{Select Config}}
- Select Device(s) + {{Select Device(s)}}
- Start Time + {{Start Time}}
- End Time + {{End Time}}
- Time Zone + {{Time Zone}}
- Select On Complete Config + {{Select On Complete Config}} - +

- +
diff --git a/src/views/schedules.mustache b/src/views/schedules.mustache index 8d63ac9..61eec3b 100644 --- a/src/views/schedules.mustache +++ b/src/views/schedules.mustache @@ -3,23 +3,23 @@ {{> navbar}}
-

Schedules

+

{{Schedules}}


NameValue{{Name}}{{Value}}
- - - - - - - - + + + + + + + + diff --git a/src/views/settings.mustache b/src/views/settings.mustache index e5213a6..1bae7b0 100644 --- a/src/views/settings.mustache +++ b/src/views/settings.mustache @@ -3,31 +3,31 @@ {{> navbar}}
-

Settings

+

{{Settings}}


-
User Interface
+
{{User Interface}}
- Title + {{Title}}
- Locale + {{Locale}}
- Theme + {{Theme}} - + +
+
+ {{Maximum Log File Size (Per Phone)}} +

- +
-
Database
+
{{Database}}
- Host + {{Host}}
@@ -58,23 +62,23 @@
- Username + {{Username}}
- Password + {{Password}}
- Database + {{Database}}
- Character Set + {{Character Set}}

- +
diff --git a/static/img/device.png b/static/img/device.png index 5e59327..8e90484 100644 Binary files a/static/img/device.png and b/static/img/device.png differ diff --git a/static/img/logs.png b/static/img/logs.png new file mode 100644 index 0000000..cdc4bce Binary files /dev/null and b/static/img/logs.png differ diff --git a/static/locales/de.json b/static/locales/de.json new file mode 100644 index 0000000..4d43676 --- /dev/null +++ b/static/locales/de.json @@ -0,0 +1,112 @@ +{ + "Home": "Home", + "Devices": "Devices", + "Configs": "Configs", + "Schedules": "Schedules", + "Profile": "Profile", + "Settings": "Settings", + "Logout": "Logout", + "Overview": "Overview", + "Total Log Size": "Total Log Size", + "Metadata": "Metadata", + "Name": "Name", + "Value": "Value", + "Login": "Login", + "Username": "Username", + "Password": "Password", + "User Interface": "User Interface", + "Title": "Title", + "Locale": "Locale", + "Choose a Locale": "Choose a Locale", + "Theme": "Theme", + "Choose a Theme": "Choose a Theme", + "Enable Device Logging": "Enable Device Logging", + "Maximum Log File Size (Per Phone)": "Maximum Log File Size (Per Phone)", + "Save": "Save", + "Database": "Database", + "Host": "Host", + "Port": "Port", + "Character Set": "Character Set", + "Account Profile": "Account Profile", + "Change Password": "Change Password", + "Old Password": "Old Password", + "Confirm Password": "Confirm Password", + "Change": "Change", + "New Device": "New Device", + "Preview": "Preview", + "UUID": "UUID", + "Config": "Config", + "iOS Version": "iOS Version", + "IPA Version": "IPA Version", + "Last Config": "Last Config", + "New Config": "New Config", + "Provider": "Provider", + "Backend": "Backend", + "Data Endpoints": "Data Endpoints", + "Token": "Token", + "Is Default": "Is Default", + "New Schedule": "New Schedule", + "Delete All": "Delete All", + "Device(s)": "Device(s)", + "Start Time": "Start Time", + "End Time": "End Time", + "Timezone Offset": "Timezone Offset", + "Next Config": "Next Config", + "Enabled": "Enabled", + "New Device Config": "New Device Config", + "Choose a Provider": "Choose a Provider", + "Backend Url": "Backend Url", + "Data Endpoints (Separated by a `,`)": "Data Endpoints (Separated by a `,`)", + "Backend Token": "Backend Token", + "Heartbeat Max Time": "Heartbeat Max Time", + "Minimum Delay Logout": "Minimum Delay Logout", + "Account Manager": "Account Manager", + "Deploy Eggs": "Deploy Eggs", + "Nearby Tracker": "Nearby Tracker", + "Auto-Login": "Auto-Login", + "Is Default Config": "Is Default Config", + "Create": "Create", + "Edit Device Config": "Edit Device Config", + "Delete Device Config": "Delete Device Config", + "Delete": "Delete", + "Cancel": "Cancel", + "Are you sure that you want to delete device config": "Are you sure that you want to delete device config", + "Assign Config to": "Assign Config to", + "Select Device Config": "Select Device Config", + "Choose a Config": "Choose a Config", + "Assign": "Assign", + "New Device Config Schedule": "New Device Config Schedule", + "Select Config": "Select Config", + "Select Device(s)": "Select Device(s)", + "Choose Device(s)": "Choose Device(s)", + "Time Zone": "Time Zone", + "Choose a Timezone": "Choose a Timezone", + "Select On Complete Config": "Select On Complete Config", + "Edit Device Config Schedule": "Edit Device Config Schedule", + "Delete Schedule": "Delete Schedule", + "Are you sure that you want to delete schedule": "Are you sure that you want to delete schedule", + "Device Logs": "Device Logs", + "Date": "Date", + "Message": "Message", + "Are you sure you want to delete all logs for": "Are you sure you want to delete all logs for", + "Delete Device": "Delete Device", + "Are you sure that you want to delete device": "Are you sure that you want to delete device", + "Device IP": "Device IP", + "Management": "Management", + "Description": "Description", + "Action": "Action", + "Response": "Response", + "GET": "GET", + "RESTART": "RESTART", + "REBOOT": "REBOOT", + "SEND": "SEND", + "DELETE": "DELETE", + "Get Screenshot": "Get Screenshot", + "Get Current Location": "Get Current Location", + "Get Current Config Data": "Get Current Config Data", + "Get Current Account Data": "Get Current Account Data", + "Get System Information Data": "Get System Information Data", + "Restart Game": "Restart Game", + "Reboot Device": "Reboot Device", + "Send Typing Data": "Send Typing Data" +} \ No newline at end of file diff --git a/static/locales/en.json b/static/locales/en.json new file mode 100644 index 0000000..4d43676 --- /dev/null +++ b/static/locales/en.json @@ -0,0 +1,112 @@ +{ + "Home": "Home", + "Devices": "Devices", + "Configs": "Configs", + "Schedules": "Schedules", + "Profile": "Profile", + "Settings": "Settings", + "Logout": "Logout", + "Overview": "Overview", + "Total Log Size": "Total Log Size", + "Metadata": "Metadata", + "Name": "Name", + "Value": "Value", + "Login": "Login", + "Username": "Username", + "Password": "Password", + "User Interface": "User Interface", + "Title": "Title", + "Locale": "Locale", + "Choose a Locale": "Choose a Locale", + "Theme": "Theme", + "Choose a Theme": "Choose a Theme", + "Enable Device Logging": "Enable Device Logging", + "Maximum Log File Size (Per Phone)": "Maximum Log File Size (Per Phone)", + "Save": "Save", + "Database": "Database", + "Host": "Host", + "Port": "Port", + "Character Set": "Character Set", + "Account Profile": "Account Profile", + "Change Password": "Change Password", + "Old Password": "Old Password", + "Confirm Password": "Confirm Password", + "Change": "Change", + "New Device": "New Device", + "Preview": "Preview", + "UUID": "UUID", + "Config": "Config", + "iOS Version": "iOS Version", + "IPA Version": "IPA Version", + "Last Config": "Last Config", + "New Config": "New Config", + "Provider": "Provider", + "Backend": "Backend", + "Data Endpoints": "Data Endpoints", + "Token": "Token", + "Is Default": "Is Default", + "New Schedule": "New Schedule", + "Delete All": "Delete All", + "Device(s)": "Device(s)", + "Start Time": "Start Time", + "End Time": "End Time", + "Timezone Offset": "Timezone Offset", + "Next Config": "Next Config", + "Enabled": "Enabled", + "New Device Config": "New Device Config", + "Choose a Provider": "Choose a Provider", + "Backend Url": "Backend Url", + "Data Endpoints (Separated by a `,`)": "Data Endpoints (Separated by a `,`)", + "Backend Token": "Backend Token", + "Heartbeat Max Time": "Heartbeat Max Time", + "Minimum Delay Logout": "Minimum Delay Logout", + "Account Manager": "Account Manager", + "Deploy Eggs": "Deploy Eggs", + "Nearby Tracker": "Nearby Tracker", + "Auto-Login": "Auto-Login", + "Is Default Config": "Is Default Config", + "Create": "Create", + "Edit Device Config": "Edit Device Config", + "Delete Device Config": "Delete Device Config", + "Delete": "Delete", + "Cancel": "Cancel", + "Are you sure that you want to delete device config": "Are you sure that you want to delete device config", + "Assign Config to": "Assign Config to", + "Select Device Config": "Select Device Config", + "Choose a Config": "Choose a Config", + "Assign": "Assign", + "New Device Config Schedule": "New Device Config Schedule", + "Select Config": "Select Config", + "Select Device(s)": "Select Device(s)", + "Choose Device(s)": "Choose Device(s)", + "Time Zone": "Time Zone", + "Choose a Timezone": "Choose a Timezone", + "Select On Complete Config": "Select On Complete Config", + "Edit Device Config Schedule": "Edit Device Config Schedule", + "Delete Schedule": "Delete Schedule", + "Are you sure that you want to delete schedule": "Are you sure that you want to delete schedule", + "Device Logs": "Device Logs", + "Date": "Date", + "Message": "Message", + "Are you sure you want to delete all logs for": "Are you sure you want to delete all logs for", + "Delete Device": "Delete Device", + "Are you sure that you want to delete device": "Are you sure that you want to delete device", + "Device IP": "Device IP", + "Management": "Management", + "Description": "Description", + "Action": "Action", + "Response": "Response", + "GET": "GET", + "RESTART": "RESTART", + "REBOOT": "REBOOT", + "SEND": "SEND", + "DELETE": "DELETE", + "Get Screenshot": "Get Screenshot", + "Get Current Location": "Get Current Location", + "Get Current Config Data": "Get Current Config Data", + "Get Current Account Data": "Get Current Account Data", + "Get System Information Data": "Get System Information Data", + "Restart Game": "Restart Game", + "Reboot Device": "Reboot Device", + "Send Typing Data": "Send Typing Data" +} \ No newline at end of file diff --git a/static/locales/es.json b/static/locales/es.json new file mode 100644 index 0000000..4d43676 --- /dev/null +++ b/static/locales/es.json @@ -0,0 +1,112 @@ +{ + "Home": "Home", + "Devices": "Devices", + "Configs": "Configs", + "Schedules": "Schedules", + "Profile": "Profile", + "Settings": "Settings", + "Logout": "Logout", + "Overview": "Overview", + "Total Log Size": "Total Log Size", + "Metadata": "Metadata", + "Name": "Name", + "Value": "Value", + "Login": "Login", + "Username": "Username", + "Password": "Password", + "User Interface": "User Interface", + "Title": "Title", + "Locale": "Locale", + "Choose a Locale": "Choose a Locale", + "Theme": "Theme", + "Choose a Theme": "Choose a Theme", + "Enable Device Logging": "Enable Device Logging", + "Maximum Log File Size (Per Phone)": "Maximum Log File Size (Per Phone)", + "Save": "Save", + "Database": "Database", + "Host": "Host", + "Port": "Port", + "Character Set": "Character Set", + "Account Profile": "Account Profile", + "Change Password": "Change Password", + "Old Password": "Old Password", + "Confirm Password": "Confirm Password", + "Change": "Change", + "New Device": "New Device", + "Preview": "Preview", + "UUID": "UUID", + "Config": "Config", + "iOS Version": "iOS Version", + "IPA Version": "IPA Version", + "Last Config": "Last Config", + "New Config": "New Config", + "Provider": "Provider", + "Backend": "Backend", + "Data Endpoints": "Data Endpoints", + "Token": "Token", + "Is Default": "Is Default", + "New Schedule": "New Schedule", + "Delete All": "Delete All", + "Device(s)": "Device(s)", + "Start Time": "Start Time", + "End Time": "End Time", + "Timezone Offset": "Timezone Offset", + "Next Config": "Next Config", + "Enabled": "Enabled", + "New Device Config": "New Device Config", + "Choose a Provider": "Choose a Provider", + "Backend Url": "Backend Url", + "Data Endpoints (Separated by a `,`)": "Data Endpoints (Separated by a `,`)", + "Backend Token": "Backend Token", + "Heartbeat Max Time": "Heartbeat Max Time", + "Minimum Delay Logout": "Minimum Delay Logout", + "Account Manager": "Account Manager", + "Deploy Eggs": "Deploy Eggs", + "Nearby Tracker": "Nearby Tracker", + "Auto-Login": "Auto-Login", + "Is Default Config": "Is Default Config", + "Create": "Create", + "Edit Device Config": "Edit Device Config", + "Delete Device Config": "Delete Device Config", + "Delete": "Delete", + "Cancel": "Cancel", + "Are you sure that you want to delete device config": "Are you sure that you want to delete device config", + "Assign Config to": "Assign Config to", + "Select Device Config": "Select Device Config", + "Choose a Config": "Choose a Config", + "Assign": "Assign", + "New Device Config Schedule": "New Device Config Schedule", + "Select Config": "Select Config", + "Select Device(s)": "Select Device(s)", + "Choose Device(s)": "Choose Device(s)", + "Time Zone": "Time Zone", + "Choose a Timezone": "Choose a Timezone", + "Select On Complete Config": "Select On Complete Config", + "Edit Device Config Schedule": "Edit Device Config Schedule", + "Delete Schedule": "Delete Schedule", + "Are you sure that you want to delete schedule": "Are you sure that you want to delete schedule", + "Device Logs": "Device Logs", + "Date": "Date", + "Message": "Message", + "Are you sure you want to delete all logs for": "Are you sure you want to delete all logs for", + "Delete Device": "Delete Device", + "Are you sure that you want to delete device": "Are you sure that you want to delete device", + "Device IP": "Device IP", + "Management": "Management", + "Description": "Description", + "Action": "Action", + "Response": "Response", + "GET": "GET", + "RESTART": "RESTART", + "REBOOT": "REBOOT", + "SEND": "SEND", + "DELETE": "DELETE", + "Get Screenshot": "Get Screenshot", + "Get Current Location": "Get Current Location", + "Get Current Config Data": "Get Current Config Data", + "Get Current Account Data": "Get Current Account Data", + "Get System Information Data": "Get System Information Data", + "Restart Game": "Restart Game", + "Reboot Device": "Reboot Device", + "Send Typing Data": "Send Typing Data" +} \ No newline at end of file
NameConfigDevice(s)Start TimeEnd TimeTimezone OffsetNext ConfigEnabled{{Name}}{{Config}}{{Device(s)}}{{Start Time}}{{End Time}}{{Timezone Offset}}{{Next Config}}{{Enabled}}