134 lines
3.6 KiB
JavaScript
134 lines
3.6 KiB
JavaScript
// ===== Configuration =====
|
|
var config = {
|
|
port: 8080,
|
|
};
|
|
//
|
|
// =========================
|
|
let rootDir = __dirname;
|
|
if (!rootDir.endsWith('/')) rootDir += '/';
|
|
const binDir = rootDir + `build`;
|
|
const watchPaths = [rootDir, binDir];
|
|
|
|
const http = require('http');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { exec } = require('child_process');
|
|
const WebSocket = require('ws');
|
|
|
|
const mimeTypes = {
|
|
'.html': 'text/html',
|
|
'.js': 'text/javascript',
|
|
'.wasm': 'application/wasm',
|
|
'.css': 'text/css',
|
|
'.json': 'application/json'
|
|
};
|
|
|
|
const server = http.createServer((req, res) => {
|
|
let norm = path.normalize(req.url);
|
|
console.log(`${req.method} ${norm}`);
|
|
|
|
// Serve config endpoint
|
|
if (norm === '/config') {
|
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
res.end(JSON.stringify(config));
|
|
return;
|
|
}
|
|
|
|
// Serve files
|
|
let filePath;
|
|
if (norm.startsWith('/bin/')) {
|
|
filePath = path.join(binDir, norm.slice(5));
|
|
} else if (norm == '/') {
|
|
filePath = rootDir + 'index.html';
|
|
} else {
|
|
filePath = rootDir + norm;
|
|
}
|
|
|
|
const extname = String(path.extname(filePath)).toLowerCase();
|
|
const contentType = mimeTypes[extname] || 'application/octet-stream';
|
|
|
|
fs.readFile(filePath, (error, content) => {
|
|
if (error) {
|
|
if (error.code === 'ENOENT') {
|
|
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
res.end('<h1>404 - File Not Found</h1>', 'utf-8');
|
|
} else {
|
|
res.writeHead(500);
|
|
res.end(`Server Error: ${error.code}`, 'utf-8');
|
|
}
|
|
} else {
|
|
res.writeHead(200, {
|
|
'Content-Type': contentType,
|
|
'Content-Length': content.byteLength,
|
|
'Access-Control-Expose-Headers': 'Content-Length',
|
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
|
});
|
|
res.end(content, 'utf-8');
|
|
}
|
|
});
|
|
});
|
|
|
|
const wss = new WebSocket.Server({ noServer: true });
|
|
wss.on('connection', () => { });
|
|
server.on('upgrade', (request, socket, head) => {
|
|
if (request.url === '/control') {
|
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
wss.emit('connection', ws, request);
|
|
});
|
|
} else {
|
|
socket.destroy();
|
|
}
|
|
});
|
|
|
|
function broadcastRefresh() {
|
|
const message = JSON.stringify({ type: 'refresh' });
|
|
let clientCount = 0;
|
|
|
|
wss.clients.forEach((client) => {
|
|
if (client.readyState === WebSocket.OPEN) {
|
|
client.send(message);
|
|
clientCount++;
|
|
}
|
|
});
|
|
|
|
if (clientCount > 0) {
|
|
console.log(`Sent refresh to ${clientCount} client(s)`);
|
|
}
|
|
}
|
|
|
|
// Watch files for changes
|
|
const watchers = [];
|
|
watchPaths.forEach((watchPath) => {
|
|
try {
|
|
const watcher = fs.watch(watchPath, { recursive: true }, (eventType, filename) => {
|
|
console.log(`File changed: ${filename} (${eventType})`);
|
|
if (filename) {
|
|
console.log(`File changed: ${filename} (${eventType})`);
|
|
broadcastRefresh();
|
|
}
|
|
});
|
|
watchers.push(watcher);
|
|
console.log(`Watching: ${path.resolve(watchPath)}`);
|
|
} catch (error) {
|
|
console.error(`Failed to watch ${watchPath}:`, error.message);
|
|
}
|
|
});
|
|
|
|
server.listen(config.port, () => {
|
|
console.log(`Server running at http://localhost:${config.port}/`);
|
|
console.log(`WebSocket listening on ws://localhost:${config.port}/control`);
|
|
console.log(`WASM binary path configured as: ${binDir}`);
|
|
|
|
const shouldLaunchBrowser = process.argv.includes('--launch');
|
|
if (shouldLaunchBrowser) {
|
|
exec(`librewolf --new-window http://localhost:${config.port}/`, (error) => {
|
|
if (error) {
|
|
console.error(`Failed to launch LibreWolf: ${error.message}`);
|
|
} else {
|
|
console.log('LibreWolf launched');
|
|
}
|
|
});
|
|
}
|
|
});
|