diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ec544c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 58e8a59..d1cc339 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "express": "^5.1.0" + "express": "^5.1.0", + "ws": "^8.18.2" } }, "node_modules/accepts": { @@ -811,6 +812,27 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index d1c10c9..489fd0f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ai-tutor-proxy", "version": "1.0.0", - "main": "index.js", + "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -13,6 +13,7 @@ "license": "MIT", "description": "", "dependencies": { - "express": "^5.1.0" + "express": "^5.1.0", + "ws": "^8.18.2" } } diff --git a/server.js b/server.js new file mode 100644 index 0000000..4bc26b3 --- /dev/null +++ b/server.js @@ -0,0 +1,44 @@ +// server.js +import express from 'express'; +import { WebSocketServer } from 'ws'; +import { createServer } from 'http'; +import WebSocket from 'ws'; + +const app = express(); +const server = createServer(app); +const wss = new WebSocketServer({ server }); + +const OPENAI_API_KEY = process.env.OPENAI_API_KEY; + +wss.on('connection', (clientWs) => { + const openaiWs = new WebSocket('wss://api.openai.com/v1/realtime', { + headers: { + Authorization: `Bearer ${OPENAI_API_KEY}` + } + }); + + openaiWs.on('open', () => { + console.log('Connected to OpenAI'); + }); + + // Proxy messages from client to OpenAI + clientWs.on('message', (message) => { + if (openaiWs.readyState === WebSocket.OPEN) { + openaiWs.send(message); + } + }); + + // Proxy messages from OpenAI back to client + openaiWs.on('message', (message) => { + if (clientWs.readyState === WebSocket.OPEN) { + clientWs.send(message); + } + }); + + openaiWs.on('close', () => clientWs.close()); + clientWs.on('close', () => openaiWs.close()); +}); + +server.listen(3001, () => { + console.log('WebSocket proxy listening on ws://localhost:3001'); +});