/** * Copyright (c) 2023 MERCENARIES.AI PTE. LTD. * All rights reserved. */ //@ts-check const VERSION = '0.6.0.hf.010e'; const express = require('express'); const http = require('http'); const session = require('express-session'); const axios = require('axios'); const { spawn } = require('child_process'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); const OMNITOOL_INSTALL_SCRIPT = './omnitool_init.sh'; // './omnitool_start.sh'; const CONTAINER_HOST = '127.0.0.1'; const OMNI_URL = 'http://127.0.0.1:1688'; // URL of the OMNITOOL service const PROXY_PORT_OMNITOOL = 4444; const CONTAINER_PORT_OMNITOOL = 1688; const OMNITOOL_HEALTH_PING = '/api/v1/mercenaries/ping'; const FAVICON = 'https://github.com/omnitool-ai/omnitool/raw/main/packages/omni-ui/omni-web/public/favicon.jpg' const OMNITOOL_SPACE_NAME = 'omnitool-test-3'; const OMNITOOL_SPACE_OWNER = 'manu-sapiens'; const HF_SPACE_URL = `https://huggingface.co/spaces/${OMNITOOL_SPACE_OWNER}/${OMNITOOL_SPACE_NAME}`; const HF_SPACE_DUPLICATE_URL = `https://huggingface.co/spaces/manu-sapiens/omnitool_test_3?duplicate=true`; const DELAY_OMNITOOL_SET_TO_RUNNING = 2000; // 2 seconds const CHECK_OMNI_INTERVAL = 60000; // 1 minute // Global variable global.OMNITOOL_RUNNING = false; global.OMNITOOL_READY = false; global.ALREADY_STARTING = false; global.LOCAL_URL = ""; global.PING_URL = ""; global.PROTOCOL = ""; global.PROXY_STARTED = false; global.logs = []; global.OMNITOOL_PROXY = null; global.CONNECTED_TO_MASTER = false; global.SPACE_OWNER = ""; console.log(`************ Omnitool Proxy Server v${VERSION} ************`); // HTML templates and constants const LOG_CONTAINER_HTML = `
`; const COMMON_STYLES = ` `; async function startOmnitoolServer() { if (global.ALREADY_STARTING) return; global.ALREADY_STARTING = true; console.log('Starting Omnitool Server...'); return new Promise((resolve, reject) => { const omnitoolStartProcess = spawn(OMNITOOL_INSTALL_SCRIPT); omnitoolStartProcess.stdout.on('data', (data) => { global.logs.push(data.toString()); console.log(`omnitool stdout: ${data}`); if (data.toString().includes(`Server has started and is ready to accept connections`)) { console.log('Omnitool server started successfully'); setOmnitoolRunning(true); } }); omnitoolStartProcess.stderr.on('data', (data) => { console.error(`omnitool stderr: ${data}`); global.logs.push(data.toString()); }); omnitoolStartProcess.on('close', (code) => { const message = `Omnitool server process exited with code: ${code}`; console.log(message); global.logs.push(message); global.ALREADY_STARTING = false; if (code === 0) { //@ts-ignore resolve(); } else { reject(`Omnitool server did not start properly.`); } }); }); } function setOmnitoolRunning(set_running) { if (set_running === true) { if (global.OMNITOOL_READY === false) { global.OMNITOOL_READY = true; console.log('Omnitool server is READY! '); setTimeout(() => { console.log('Omnitool server is RUNNING! '); global.OMNITOOL_RUNNING = true; }, DELAY_OMNITOOL_SET_TO_RUNNING); // Delay by 2 second } } else { global.OMNITOOL_READY = false; global.OMNITOOL_RUNNING = false; } } // Function to check the status of the external service async function checkOmnitoolStatus() { console.log("Checking external service"); try { //@ts-ignore const response = await axios.get('http://127.0.0.1:1688/api/v1/mercenaries/ping'); if (response.data && response.data.ping === 'pong' && Object.keys(response.data.payload).length === 0) { setOmnitoolRunning(true); } else { setOmnitoolRunning(false); } } catch (error) { console.error('Cannot access OMNITOOL. It is probably not running...', error.message); setOmnitoolRunning(false); } } async function handleGetStartOmnitoolServer(req, res) { console.log(`Omnitool Server:ALREADY_STARTING = ${global.ALREADY_STARTING}`); if (global.ALREADY_STARTING) { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end("Omnitool server already starting"); return; } try { await startOmnitoolServer(); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end("Omnitool server started successfully"); } catch (error) { console.error(error); global.ALREADY_STARTING = false; res.writeHead(500, { 'Content-Type': 'text/html' }); res.end(`Error starting Omnitool server: ${error}`); } } async function handleGetOmnitoolLogs(req, res) { res.writeHead(200, { 'Content-Type': 'application/json' }); const reply = { logs: global.logs, ready: global.OMNITOOL_READY }; res.end(JSON.stringify(reply)); return; } async function handleGetBurstIframe(req, res) { res.writeHead(200, { 'Content-Type': 'application/json' }); req.session.isVisited = true; const reply = { burst_iframe: true }; res.end(JSON.stringify(reply)); return; } async function proxyRequest(req, res) { console.log('Proxying request'); if (global.PROXY_STARTED) return; global.PROXY_STARTED = true; // Proxy logic... const options = { hostname: CONTAINER_HOST, port: CONTAINER_PORT_OMNITOOL, path: req.url, method: req.method, headers: req.headers, }; const proxy = http.request(options, (proxyRes) => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res, { end: true }); }); req.pipe(proxy, { end: true }); return; } function getButtonsString(buttonsHTML) { // Message to display based on CONNECTED_TO_MASTER let connectionStatusMessage; if (global.CONNECTED_TO_MASTER) { connectionStatusMessage = 'Version: ${VERSION}
-----------------------------------
This Space URL: ${global.LOCAL_URL}
This Space Owner: ${global.SPACE_OWNER}
Using REFERENCE Space: ${global.CONNECTED_TO_MASTER}
Omnitool SERVER Launched: ${global.ALREADY_STARTING}
Omnitool SERVER Ready: ${global.OMNITOOL_READY}
-----------------------------------
${connectionStatusMessage} ${buttonsHTML} ${LOG_CONTAINER_HTML} `; } async function handleGetRoot(req, res) { setGlobals(req); console.log(`req.session.isVisited = ${req.session.isVisited}`); console.log(`global.OMNITOOL_RUNNING = ${global.OMNITOOL_RUNNING}`); if (!req.session.isVisited || !global.OMNITOOL_RUNNING) { await checkOmnitoolStatus(); } if (!global.OMNITOOL_RUNNING) { console.log('Omnitool server is not running'); let startButtonClass = ''; if (!global.ALREADY_STARTING) startButtonClass = 'highlight-button'; const gotoButtonClass = ''; let buttonsHTML = ` DUPLICATE Space`; const html = getButtonsString(buttonsHTML); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html); return; } if (!req.session.isVisited) { console.log('First time visitor'); const gotoButtonClass = 'highlight-button-green'; let buttonsHTML = ` DUPLICATE SPACE`; const html = getButtonsString(buttonsHTML); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html); return; } proxyRequest(req, res); } function setGlobals(req) { console.log(`Setting global using req.headers['host'] = ${req.headers['host']}`); global.LOCAL_URL = req.headers['host']; // !!!!!!!!! // TEST ONLY: global.LOCAL_URL = 'manu-sapiens-omnitool-test-3.hf.space/'; // !!!!!!!!! if (req.protocol === "https") global.PROTOCOL = 'https'; else global.PROTOCOL = 'http'; console.log(`global.LOCAL_URL = ${global.LOCAL_URL}\nglobal.PROTOCOL = ${global.PROTOCOL}`); if (!global.LOCAL_URL) throw new Error('No host header found in request' + JSON.stringify(req.headers, null, 2)); const hostname = global.LOCAL_URL.split(':')[0]; const newUrl = `${global.PROTOCOL}://${hostname}`;//:${PROXY_PORT_OMNITOOL}`; global.PING_URL = `${newUrl}${OMNITOOL_HEALTH_PING}`; console.log(`global.PING_URL = ${global.PING_URL}`); // New logic to set CONNECTED_TO_MASTER global.CONNECTED_TO_MASTER = false; const recreated_string = `-${OMNITOOL_SPACE_NAME}.hf.space`; // remove the protocol, a possible trailing / and the recreated string from the url global.SPACE_OWNER = global.LOCAL_URL.replace(/\/$/, '').replace(recreated_string, ''); console.log(`global.SPACE_OWNER = ${global.SPACE_OWNER}`); if (global.SPACE_OWNER === OMNITOOL_SPACE_OWNER) { global.CONNECTED_TO_MASTER = true; } console.log(`global.CONNECTED_TO_MASTER = ${global.CONNECTED_TO_MASTER}`); } async function startServer() { // Start server http.createServer(app).listen(PROXY_PORT_OMNITOOL, () => { console.log(`Server running on port ${PROXY_PORT_OMNITOOL}`); }); } function omnitoolProxyMiddleware(req, res, next) { if (global.OMNITOOL_RUNNING && req.session.isVisited) { // Proxy all requests to OMNITOOL when conditions are met return createProxyMiddleware({ target: OMNI_URL, changeOrigin: true, ws: true // if you need WebSocket support })(req, res, next); } else { // Continue with normal processing for other cases next(); } } async function main(app) { await checkOmnitoolStatus(); // Configure session middleware app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true })); try { await startServer(); } catch (error) { console.error(`There was an error starting the server: ${error}`); return; } app.use(omnitoolProxyMiddleware); app.get('/favicon.ico', (req, res) => res.status(204)); app.get('/start-omnitool-server', (req, res) => handleGetStartOmnitoolServer(req, res)); app.get('/omnitool-logs', (req, res) => handleGetOmnitoolLogs(req, res)); app.get('/burst-iframe', (req, res) => handleGetBurstIframe(req, res)); app.get('/', (req, res) => handleGetRoot(req, res)); setInterval(async () => { await checkOmnitoolStatus(); }, CHECK_OMNI_INTERVAL); } // Define the proxy middleware outside the function global.OMNITOOL_PROXY = createProxyMiddleware({ target: OMNI_URL, changeOrigin: true, ws: true // if you need WebSocket support }); function omnitoolProxyMiddleware(req, res, next) { if (global.OMNITOOL_RUNNING && req.session.isVisited) { // Use the predefined proxy middleware return global.OMNITOOL_PROXY(req, res, next); } else { // Continue with normal processing for other cases next(); } } main(app);