"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; //Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = __importDefault(require("fs")); const crypto_1 = __importDefault(require("crypto")); const http_1 = require("http"); const url_1 = require("url"); require("dotenv/config"); const watcher_1 = require("./watcher"); const CACHE_MAX_AGE = parseInt(process.env.CACHE_MAX_AGE || '0', 10); const APP_HOST = process.env.app_host || process.env.APP_HOST || '0.0.0.0'; const APP_PORT = parseInt(process.env.PORT || process.env.app_port || process.env.APP_PORT || '8080', 10); const SECRET = process.env.SECRET || ''; const DATA_PATH = process.env.DATA_PATH || './data/'; const DBG = Boolean(process.env.DBG || false); const FEET_STEAM = Boolean(process.env.STEAM_WEB_API_KEY); const FEET_DISCORD = Boolean(process.env.DISCORD_BOT_TOKEN); const FEET_TELEGRAM = Boolean(process.env.TELEGRAM_BOT_TOKEN); let loop; (0, http_1.createServer)(async (req, res) => { if (DBG) console.log('DBG: %j %j', (new Date()), req.url); const reqUrl = new url_1.URL(req.url || '', 'http://localhost'); const p = reqUrl.pathname === '/' ? 'index.html' : reqUrl.pathname.slice(1); if (['index.html', 'main.css', 'main.js'].includes(p)) { if (SECRET !== '') { let ct = 'text/html'; if (p === 'main.css') ct = 'text/css'; else if (p === 'main.js') ct = 'text/javascript'; res.writeHead(200, { 'Content-Type': ct, 'Cache-Control': 'max-age=' + String(CACHE_MAX_AGE) }); fs_1.default.createReadStream('./public/' + p).pipe(res); } else { res.end('Configure the `SECRET` env var to enable the web UI!'); } } else if (p === 'ping') { if (DBG) console.log('ping'); res.end('pong'); } else if (SECRET !== '' && req.headers['x-btoken']) { let status = 200; let re = {}; if (validateBearerToken(String(req.headers['x-btoken']))) { const reqPath = p.split('/'); try { if (reqPath[0] === 'features') { re.features = { steam: FEET_STEAM, discord: FEET_DISCORD, telegram: FEET_TELEGRAM }; } else if (reqPath[0] === 'config') { if (req.method === 'GET') { re.config = await (0, watcher_1.readConfig)(); } else if (req.method === 'POST') { const body = await new Promise(resolve => { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { resolve(body); }); }); // TODO: validate (ajv) await (0, watcher_1.updateConfig)(JSON.parse(String(body)) || []); await restart(); re.message = 'Configuration updated. Watcher restarted.'; } else { status = 400; re.error = 'Invalid Request'; } } else if (reqPath[0] === 'flush' && ['servers', 'discord', 'telegram'].includes(reqPath[1])) { await restart(reqPath[1]); re.message = '🗑️ ' + reqPath[1].slice(0, 1).toUpperCase() + reqPath[1].slice(1) + ' data flushed.'; } else { status = 400; re.error = 'Invalid Request'; } } catch (err) { status = 500; re.error = err.message || String(err); } } else { status = 401; re.error = 'Unauthorized'; } res.writeHead(status, { 'Content-Type': 'application/json', 'Cache-Control': 'max-age=0' }); res.end(JSON.stringify(re, null, DBG ? 2 : 0)); } else { res.writeHead(404, { 'Content-Type': 'text/html' }); res.end('404 💢'); } }).listen(APP_PORT, APP_HOST, () => { console.log('Web service started %s:%s', APP_HOST, APP_PORT); }); (0, watcher_1.main)().then(l => { loop = l; }); async function restart(flush) { if (DBG) console.log('stopping loop'); if (loop) { clearInterval(loop); loop = undefined; } if (flush) { if (DBG) console.log('Deleting ' + flush + ' data'); try { fs_1.default.unlinkSync(DATA_PATH + flush + '.json'); } catch (e) { } } loop = await (0, watcher_1.main)(); } function validateBearerToken(btoken) { const salt = btoken.slice(0, btoken.length - 141); const valid = btoken.slice(-141, -128); const hash = btoken.slice(-128); if (DBG) console.log('validateBT', valid, salt); if (salt.length > 24 && /^\d{13}$/.test(valid) && /^[a-f0-9]{128}$/.test(hash) && Date.now() < Number(valid)) { return hash === crypto_1.default.createHash('sha512').update(salt + valid + SECRET).digest('hex'); } return false; }