/* eslint-disable @typescript-eslint/no-var-requires */ const http = require("http"); const crypto = require("crypto"); const log = require("electron-log"); const Config = require("electron-config"); const config = new Config(); let server; let window; async function initialize(win) { window = win; server = http.createServer(async function (req, res) { let body = ""; res.setHeader('Content-Type', 'application/json'); req.on("data", (chunk) => { body += chunk.toString(); // convert Buffer to string }); req.on("end", async () => { const providedToken = req.headers?.authorization?.replace('Bearer ', '') ?? ''; const isValid = providedToken === getAuthenticationToken(); if (isValid) { log.debug('Valid authentication token'); } else { log.log('Invalid authentication token'); res.writeHead(401); res.end(JSON.stringify({ success: false, msg: 'Invalid authentication token' })); return; } let data; try { data = JSON.parse(body); } catch (error) { log.warn(`Invalid body data`); res.writeHead(400); res.end(JSON.stringify({ success: false, msg: 'Invalid body data' })); return; } let result; switch(req.method) { // Request files case "GET": result = await window.webContents.executeJavaScript(`document.getFiles()`); break; // Create or update files // Support POST for VScode implementation case "POST": case "PUT": if (!data) { log.warn(`Invalid script update request - No data`); res.writeHead(400); res.end(JSON.stringify({ success: false, msg: 'Invalid script update request - No data' })); return; } result = await window.webContents.executeJavaScript(`document.saveFile("${data.filename}", "${data.code}")`); break; // Delete files case "DELETE": result = await window.webContents.executeJavaScript(`document.deleteFile("${data.filename}")`); break; } if (!result.res) { //We've encountered an error res.writeHead(400); log.warn(`Api Server Error`, result.msg); } res.end(JSON.stringify({ success: result.res, msg: result.msg, data: result.data })); }); }); const autostart = config.get('autostart', false); if (autostart) { try { await enable() } catch (error) { return Promise.reject(error); } } return Promise.resolve(); } function enable() { if (isListening()) { log.warn('API server already listening'); return Promise.resolve(); } const port = config.get('port', 9990); const host = config.get('host', '127.0.0.1'); log.log(`Starting http server on port ${port} - listening on ${host}`); // https://stackoverflow.com/a/62289870 let startFinished = false; return new Promise((resolve, reject) => { server.listen(port, host, () => { if (!startFinished) { startFinished = true; resolve(); } }); server.once('error', (err) => { if (!startFinished) { startFinished = true; console.log( 'There was an error starting the server in the error listener:', err ); reject(err); } }); }); } function disable() { if (!isListening()) { log.warn('API server not listening'); return Promise.resolve(); } log.log('Stopping http server'); return server.close(); } function toggleServer() { if (isListening()) { return disable(); } else { return enable(); } } function isListening() { return server?.listening ?? false; } function toggleAutostart() { const newValue = !isAutostart(); config.set('autostart', newValue); log.log(`New autostart value is '${newValue}'`); } function isAutostart() { return config.get('autostart'); } function getAuthenticationToken() { const token = config.get('token'); if (token) return token; const newToken = generateToken(); config.set('token', newToken); return newToken; } function generateToken() { const buffer = crypto.randomBytes(48); return buffer.toString('base64') } module.exports = { initialize, enable, disable, toggleServer, toggleAutostart, isAutostart, getAuthenticationToken, isListening, }