Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
5.0 kB · 131 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132#!/usr/bin/env node/** * FF1 Local Proxy Server * * This runs a simple local HTTP proxy that allows the kidlisp.com editor * to communicate with your FF1 device without CORS issues. * * Usage: * node ff1-proxy.mjs [deviceHost] * * Examples: * node ff1-proxy.mjs # Uses default FF1-DVVEKLZA.local:1111 * node ff1-proxy.mjs 192.168.1.100 # Use specific IP (port defaults to 1111) * node ff1-proxy.mjs 192.168.1.100:1111 # Use specific IP and port * node ff1-proxy.mjs FF1-MYDEVICE.local # Use specific mDNS hostname * * Find your FF1's IP address: * - Check your router's connected devices list * - On the FF1 device settings screen * - Use: arp -a | grep -i ff1 (on macOS/Linux on same network) */
import http from 'http';
let deviceInput = process.argv[2] || 'localhost'; // Via SSH tunnel to FF1-DVVEKLZA (192.168.1.162)// Add port if not specifiedconst DEFAULT_DEVICE_HOST = deviceInput.includes(':') ? deviceInput : `${deviceInput}:1111`;const PROXY_PORT = 3333;
const server = http.createServer(async (req, res) => { // CORS headers for browser access res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, API-KEY, topicID');
// Handle preflight if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
// Health check if (req.url === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', device: DEFAULT_DEVICE_HOST })); return; }
// Proxy /api/cast to device if (req.url.startsWith('/api/cast')) { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { const url = new URL(`http://${DEFAULT_DEVICE_HOST}${req.url}`); const options = { hostname: url.hostname, port: url.port || 1111, path: url.pathname + url.search, method: req.method, headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), }, timeout: 10000, };
console.log(`[Proxy] ${req.method} -> http://${DEFAULT_DEVICE_HOST}${req.url}`);
const proxyReq = http.request(options, (proxyRes) => { let responseBody = ''; proxyRes.on('data', chunk => { responseBody += chunk; }); proxyRes.on('end', () => { console.log(`[Proxy] <- ${proxyRes.statusCode} ${responseBody.substring(0, 100)}`); res.writeHead(proxyRes.statusCode, { 'Content-Type': proxyRes.headers['content-type'] || 'application/json', }); res.end(responseBody); }); });
proxyReq.on('error', (err) => { console.error(`[Proxy] Error: ${err.message}`); res.writeHead(502, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Failed to connect to FF1 device', details: err.message, device: DEFAULT_DEVICE_HOST })); });
proxyReq.on('timeout', () => { proxyReq.destroy(); res.writeHead(504, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Request to FF1 device timed out' })); });
proxyReq.write(body); proxyReq.end(); }); return; }
// Unknown endpoint res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found', endpoints: ['/health', '/api/cast'] }));});
server.listen(PROXY_PORT, () => { console.log(`╔════════════════════════════════════════════════════════════╗║ FF1 Local Proxy Server Running ║╠════════════════════════════════════════════════════════════╣║ ║║ Proxy URL: http://localhost:${PROXY_PORT} ║║ Device: ${DEFAULT_DEVICE_HOST.padEnd(40)} ║║ ║║ Endpoints: ║║ GET /health - Check proxy status ║║ POST /api/cast - Send playlist to FF1 ║║ ║╚════════════════════════════════════════════════════════════╝
In kidlisp.com settings, set: Device URL: http://localhost:${PROXY_PORT} Use Direct API: ✓
Press Ctrl+C to stop.`);});