Spaces:
Running
Running
const express = require('express'); | |
const path = require('node:path'); | |
const { WebSocketServer, WebSocket } = require('ws'); | |
const http = require('node:http'); | |
require('dotenv').config(); | |
const app = express(); | |
const server = http.createServer(app); | |
const wss = new WebSocketServer({ server }); | |
// Serve static files from the React app build directory | |
app.use(express.static(path.join(__dirname, '../build'))); | |
// Make API key available to the WebSocket server but not to the client | |
const GEMINI_API_KEY = process.env.GEMINI_API_KEY; | |
if (!GEMINI_API_KEY) { | |
console.error('GEMINI_API_KEY environment variable is not set!'); | |
process.exit(1); | |
} | |
// Create a WebSocket connection to Gemini for each client | |
const createGeminiWebSocket = (clientWs) => { | |
const geminiWs = new WebSocket( | |
`wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContent?key=${GEMINI_API_KEY}` | |
); | |
// Set up event handlers before connecting | |
geminiWs.on('open', () => { | |
console.log('Connected to Gemini API'); | |
// If there's a pending setup message, send it now | |
if (geminiWs.pendingSetup) { | |
console.log('Sending pending setup:', geminiWs.pendingSetup); | |
geminiWs.send(JSON.stringify(geminiWs.pendingSetup)); | |
geminiWs.pendingSetup = null; | |
} | |
}); | |
geminiWs.on('message', (data) => { | |
try { | |
// Convert the message to a Blob before sending to client | |
const message = data.toString(); | |
console.log('Received from Gemini:', message); | |
// Create a Blob from the message | |
const blob = Buffer.from(message); | |
clientWs.send(blob, { binary: true }); | |
} catch (error) { | |
console.error('Error handling Gemini message:', error); | |
} | |
}); | |
geminiWs.on('error', (error) => { | |
console.error('Gemini WebSocket error:', error); | |
}); | |
geminiWs.on('close', (code, reason) => { | |
console.log('Gemini WebSocket closed:', code, reason.toString()); | |
}); | |
return geminiWs; | |
}; | |
wss.on('connection', (ws) => { | |
console.log('Client connected'); | |
let geminiWs = null; | |
ws.on('message', async (message) => { | |
try { | |
const data = JSON.parse(message); | |
console.log('Received from client:', data); | |
// Initialize Gemini connection when receiving setup message | |
if (data.setup) { | |
console.log('Initializing Gemini connection with config:', data.setup); | |
geminiWs = createGeminiWebSocket(ws); | |
// Store setup message to send once connection is established | |
if (geminiWs.readyState !== WebSocket.OPEN) { | |
geminiWs.pendingSetup = data; | |
} else { | |
geminiWs.send(JSON.stringify(data)); | |
} | |
return; | |
} | |
// Forward message to Gemini if connection exists | |
if (geminiWs && geminiWs.readyState === WebSocket.OPEN) { | |
console.log('Forwarding to Gemini:', data); | |
geminiWs.send(JSON.stringify(data)); | |
} else if (geminiWs) { | |
console.log('Waiting for Gemini connection to be ready...'); | |
} else { | |
console.error('No Gemini connection established'); | |
} | |
} catch (error) { | |
console.error('Error processing message:', error); | |
} | |
}); | |
ws.on('close', () => { | |
console.log('Client disconnected'); | |
if (geminiWs) { | |
geminiWs.close(); | |
} | |
}); | |
}); | |
// Handle any remaining requests by returning the React app | |
app.get('*', (req, res) => { | |
res.sendFile(path.join(__dirname, '../build', 'index.html')); | |
}); | |
const PORT = process.env.PORT || 3001; | |
server.listen(PORT, () => { | |
console.log(`Server is running on port ${PORT}`); | |
}); | |