Trudy's picture
3 dots game
21daab4
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}`);
});