Files
alice-backend/server/index.mjs
T

34 lines
1.9 KiB
JavaScript

import http from 'node:http';
import { timingSafeEqual } from 'node:crypto';
import { WebSocketServer } from 'ws';
import { openStore } from './store.mjs';
import { handleAdmin } from './admin.mjs';
import { handleTurn } from './turns.mjs';
const { DEVICE_TOKEN, ELEVENLABS_API_KEY, LLM_MODE = 'openrouter', OPENROUTER_API_KEY, PORT = '3000', HOST = '127.0.0.1' } = process.env;
if (!DEVICE_TOKEN || !ELEVENLABS_API_KEY || !['openrouter', 'echo'].includes(LLM_MODE) || (LLM_MODE === 'openrouter' && !OPENROUTER_API_KEY)) {
console.error('Missing required configuration or invalid LLM_MODE'); process.exit(1);
}
const store = openStore();
const server = http.createServer(async (req, res) => {
try {
if (req.method === 'GET' && req.url === '/healthz') { res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' }); res.end('{"ok":true}'); return; }
if (await handleAdmin(req, res, store)) return;
res.writeHead(404); res.end();
} catch { if (!res.headersSent) res.writeHead(500); res.end(); }
});
const sockets = new WebSocketServer({ noServer: true, maxPayload: 128 * 1024, perMessageDeflate: false });
let active = false;
server.on('upgrade', (req, socket, head) => {
const token = req.headers.authorization?.startsWith('Bearer ') ? req.headers.authorization.slice(7) : '';
const a = Buffer.from(token), b = Buffer.from(DEVICE_TOKEN);
if (req.url !== '/v1/voice' || a.length !== b.length || !timingSafeEqual(a, b)) { socket.end('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n'); return; }
if (active) { socket.end('HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\n\r\n'); return; }
sockets.handleUpgrade(req, socket, head, ws => {
active = true;
ws.once('close', () => { active = false; });
handleTurn(ws, store);
});
});
server.listen(Number(PORT), HOST, () => console.log(`Voice server listening on ${HOST}:${PORT}`));