new file: src/backend/signaling-server/chat-schema.sql
This commit is contained in:
parent
fa1d0fcc70
commit
48f095c8ad
7 changed files with 737 additions and 0 deletions
|
|
@ -11,6 +11,8 @@ const gameforgeRoutes = require('./routes/gameforgeRoutes');
|
|||
const callRoutes = require('./routes/callRoutes');
|
||||
const socketService = require('./services/socketService');
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const app = express();
|
||||
const httpServer = http.createServer(app);
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
|
@ -45,6 +47,12 @@ app.get('/health', (req, res) => {
|
|||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// Serve Astro build as main site
|
||||
app.use(express.static(path.join(__dirname, '../../astro-site/dist')));
|
||||
|
||||
// Serve React app at /app
|
||||
app.use('/app', express.static(path.join(__dirname, '../frontend/dist')));
|
||||
|
||||
// API routes
|
||||
app.use('/api/passport/domain', domainRoutes);
|
||||
app.use('/api/messaging', messagingRoutes);
|
||||
|
|
|
|||
49
src/backend/signaling-server/README.md
Normal file
49
src/backend/signaling-server/README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Signaling Server for AeThex Connect
|
||||
|
||||
This is a minimal Socket.io signaling server for WebRTC voice and game sync, using Supabase JWT authentication.
|
||||
|
||||
## Features
|
||||
- Authenticates users via Supabase JWT
|
||||
- Handles WebRTC signaling (voice:join, offer, answer, ice-candidate, leave)
|
||||
- Handles basic game sync events (game:join, game:position)
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Environment Variables
|
||||
Create a `.env` file in this directory with:
|
||||
|
||||
```
|
||||
SUPABASE_URL=https://your-project.supabase.co
|
||||
SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
PORT=4000
|
||||
```
|
||||
|
||||
### 2. Install Dependencies
|
||||
|
||||
```
|
||||
npm install socket.io node-fetch dotenv
|
||||
```
|
||||
|
||||
### 3. Run the Server
|
||||
|
||||
```
|
||||
node signaling-server.js
|
||||
```
|
||||
|
||||
## Frontend Usage
|
||||
- Connect to the server with Socket.io
|
||||
- Pass the Supabase JWT as `auth.token` in the connection options
|
||||
|
||||
```js
|
||||
const socket = io('https://your-signaling-server', {
|
||||
auth: { token: supabase.auth.getSession().access_token }
|
||||
});
|
||||
```
|
||||
|
||||
## Deployment
|
||||
- Deploy to Railway, Fly.io, or any Node.js host
|
||||
- Make sure to set the environment variables in your deployment settings
|
||||
|
||||
---
|
||||
|
||||
**This server does not store any data. All persistent data (users, messages, game state) should remain in Supabase.**
|
||||
16
src/backend/signaling-server/chat-schema.sql
Normal file
16
src/backend/signaling-server/chat-schema.sql
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
-- Minimal chat schema for Supabase
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
channel_id VARCHAR(255) NOT NULL,
|
||||
user_id UUID NOT NULL,
|
||||
username VARCHAR(255),
|
||||
text TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Optionally, create a channels table
|
||||
CREATE TABLE IF NOT EXISTS channels (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) DEFAULT 'text'
|
||||
);
|
||||
460
src/backend/signaling-server/package-lock.json
generated
Normal file
460
src/backend/signaling-server/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
{
|
||||
"name": "aethex-signaling-server",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "aethex-signaling-server",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.39.7",
|
||||
"dotenv": "^16.4.5",
|
||||
"node-fetch": "^2.6.7",
|
||||
"socket.io": "^4.7.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@supabase/auth-js": {
|
||||
"version": "2.90.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.90.1.tgz",
|
||||
"integrity": "sha512-vxb66dgo6h3yyPbR06735Ps+dK3hj0JwS8w9fdQPVZQmocSTlKUW5MfxSy99mN0XqCCuLMQ3jCEiIIUU23e9ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/functions-js": {
|
||||
"version": "2.90.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.90.1.tgz",
|
||||
"integrity": "sha512-x9mV9dF1Lam9qL3zlpP6mSM5C9iqMPtF5B/tU1Jj/F0ufX5mjDf9ghVBaErVxmrQJRL4+iMKWKY2GnODkpS8tw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/postgrest-js": {
|
||||
"version": "2.90.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.90.1.tgz",
|
||||
"integrity": "sha512-jh6vqzaYzoFn3raaC0hcFt9h+Bt+uxNRBSdc7PfToQeRGk7PDPoweHsbdiPWREtDVTGKfu+PyPW9e2jbK+BCgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/realtime-js": {
|
||||
"version": "2.90.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.90.1.tgz",
|
||||
"integrity": "sha512-PWbnEMkcQRuor8jhObp4+Snufkq8C6fBp+MchVp2qBPY1NXk/c3Iv3YyiFYVzo0Dzuw4nAlT4+ahuPggy4r32w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/phoenix": "^1.6.6",
|
||||
"@types/ws": "^8.18.1",
|
||||
"tslib": "2.8.1",
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/storage-js": {
|
||||
"version": "2.90.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.90.1.tgz",
|
||||
"integrity": "sha512-GHY+Ps/K/RBfRj7kwx+iVf2HIdqOS43rM2iDOIDpapyUnGA9CCBFzFV/XvfzznGykd//z2dkGZhlZZprsVFqGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iceberg-js": "^0.8.1",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/supabase-js": {
|
||||
"version": "2.90.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.90.1.tgz",
|
||||
"integrity": "sha512-U8KaKGLUgTIFHtwEW1dgw1gK7XrdpvvYo7nzzqPx721GqPe8WZbAiLh/hmyKLGBYQ/mmQNr20vU9tWSDZpii3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@supabase/auth-js": "2.90.1",
|
||||
"@supabase/functions-js": "2.90.1",
|
||||
"@supabase/postgrest-js": "2.90.1",
|
||||
"@supabase/realtime-js": "2.90.1",
|
||||
"@supabase/storage-js": "2.90.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cors": {
|
||||
"version": "2.8.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
|
||||
"integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.9.tgz",
|
||||
"integrity": "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/phoenix": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz",
|
||||
"integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/base64id": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^4.5.0 || >= 5.9"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.6.5",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz",
|
||||
"integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.18.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/iceberg-js": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
|
||||
"integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz",
|
||||
"integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io": "~6.6.0",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz",
|
||||
"integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "~4.4.1",
|
||||
"ws": "~8.18.3"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter/node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz",
|
||||
"integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/backend/signaling-server/package.json
Normal file
12
src/backend/signaling-server/package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "aethex-signaling-server",
|
||||
"version": "1.0.0",
|
||||
"main": "signaling-server.js",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.39.7",
|
||||
"dotenv": "^16.4.5",
|
||||
"node-fetch": "^2.6.7",
|
||||
"socket.io": "^4.7.5"
|
||||
}
|
||||
}
|
||||
153
src/backend/signaling-server/signaling-server.js
Normal file
153
src/backend/signaling-server/signaling-server.js
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// signaling-server.js
|
||||
// Minimal Socket.io signaling server for AeThex Connect (Supabase JWT auth)
|
||||
|
||||
const http = require('http');
|
||||
const { Server } = require('socket.io');
|
||||
const fetch = require('node-fetch');
|
||||
require('dotenv').config();
|
||||
|
||||
const { createClient } = require('@supabase/supabase-js');
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
const SUPABASE_URL = process.env.SUPABASE_URL;
|
||||
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY;
|
||||
|
||||
// Supabase client for DB operations
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
||||
|
||||
const server = http.createServer();
|
||||
const io = new Server(server, {
|
||||
cors: { origin: '*' }
|
||||
});
|
||||
|
||||
// Helper: Validate Supabase JWT
|
||||
async function validateSupabaseJWT(token) {
|
||||
// Use Supabase's /user endpoint to validate JWT
|
||||
const res = await fetch(`${SUPABASE_URL}/auth/v1/user`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'apikey': SUPABASE_ANON_KEY
|
||||
}
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
return res.json();
|
||||
}
|
||||
|
||||
io.use(async (socket, next) => {
|
||||
const token = socket.handshake.auth?.token;
|
||||
if (!token) return next(new Error('Authentication required'));
|
||||
const user = await validateSupabaseJWT(token);
|
||||
if (!user) return next(new Error('Invalid token'));
|
||||
socket.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
console.log(`User connected: ${socket.user.email}`);
|
||||
|
||||
// WebRTC signaling events
|
||||
socket.on('voice:join', (roomId) => {
|
||||
socket.join(`voice:${roomId}`);
|
||||
socket.to(`voice:${roomId}`).emit('voice:user-joined', {
|
||||
user: socket.user
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('voice:offer', (roomId, offer) => {
|
||||
socket.to(`voice:${roomId}`).emit('voice:offer', {
|
||||
from: socket.user,
|
||||
offer
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('voice:answer', (roomId, answer) => {
|
||||
socket.to(`voice:${roomId}`).emit('voice:answer', {
|
||||
from: socket.user,
|
||||
answer
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('voice:ice-candidate', (roomId, candidate) => {
|
||||
socket.to(`voice:${roomId}`).emit('voice:ice-candidate', {
|
||||
from: socket.user,
|
||||
candidate
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('voice:leave', (roomId) => {
|
||||
socket.leave(`voice:${roomId}`);
|
||||
socket.to(`voice:${roomId}`).emit('voice:user-left', {
|
||||
user: socket.user
|
||||
});
|
||||
});
|
||||
|
||||
// Game sync events (example)
|
||||
socket.on('game:join', (gameId) => {
|
||||
socket.join(`game:${gameId}`);
|
||||
socket.to(`game:${gameId}`).emit('game:player-joined', {
|
||||
user: socket.user
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('game:position', (gameId, position) => {
|
||||
socket.to(`game:${gameId}`).emit('game:player-position', {
|
||||
user: socket.user,
|
||||
position
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log(`User disconnected: ${socket.user.email}`);
|
||||
});
|
||||
|
||||
// ===== Minimal Chat (channels, DMs, presence) =====
|
||||
|
||||
// Join a chat channel
|
||||
socket.on('chat:join', async (channelId) => {
|
||||
socket.join(`chat:${channelId}`);
|
||||
// Mark user as present in channel (optional: store in-memory or in DB)
|
||||
socket.to(`chat:${channelId}`).emit('chat:user-joined', {
|
||||
user: socket.user,
|
||||
channelId
|
||||
});
|
||||
});
|
||||
|
||||
// Leave a chat channel
|
||||
socket.on('chat:leave', (channelId) => {
|
||||
socket.leave(`chat:${channelId}`);
|
||||
socket.to(`chat:${channelId}`).emit('chat:user-left', {
|
||||
user: socket.user,
|
||||
channelId
|
||||
});
|
||||
});
|
||||
|
||||
// Send a chat message
|
||||
socket.on('chat:message', async (channelId, text) => {
|
||||
const message = {
|
||||
user_id: socket.user.id,
|
||||
username: socket.user.user_metadata?.username || socket.user.email,
|
||||
text,
|
||||
channel_id: channelId,
|
||||
created_at: new Date().toISOString()
|
||||
};
|
||||
// Store message in Supabase
|
||||
await supabase.from('messages').insert([message]);
|
||||
// Broadcast to channel
|
||||
io.to(`chat:${channelId}`).emit('chat:message', message);
|
||||
});
|
||||
|
||||
// Fetch recent messages for a channel
|
||||
socket.on('chat:history', async (channelId, cb) => {
|
||||
const { data, error } = await supabase
|
||||
.from('messages')
|
||||
.select('*')
|
||||
.eq('channel_id', channelId)
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(50);
|
||||
if (cb) cb(data || []);
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Signaling server running on port ${PORT}`);
|
||||
});
|
||||
39
src/frontend/utils/socket.js
Normal file
39
src/frontend/utils/socket.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// socket.js
|
||||
// Example Socket.io client for AeThex Connect (Supabase JWT auth)
|
||||
|
||||
import { io } from 'socket.io-client';
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
// Initialize Supabase client
|
||||
const supabase = createClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY
|
||||
);
|
||||
|
||||
export async function connectSocket() {
|
||||
// Get current session (JWT)
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
if (!session) throw new Error('Not authenticated');
|
||||
|
||||
// Connect to signaling server
|
||||
const socket = io(import.meta.env.PUBLIC_SIGNALING_SERVER_URL, {
|
||||
auth: { token: session.access_token }
|
||||
});
|
||||
|
||||
// Example: handle connection
|
||||
socket.on('connect', () => {
|
||||
console.log('Connected to signaling server');
|
||||
});
|
||||
|
||||
// Example: join a voice room
|
||||
function joinVoice(roomId) {
|
||||
socket.emit('voice:join', roomId);
|
||||
}
|
||||
|
||||
// Example: send/receive offers, answers, ICE
|
||||
socket.on('voice:offer', (data) => { /* handle offer */ });
|
||||
socket.on('voice:answer', (data) => { /* handle answer */ });
|
||||
socket.on('voice:ice-candidate', (data) => { /* handle candidate */ });
|
||||
|
||||
return { socket, joinVoice };
|
||||
}
|
||||
Loading…
Reference in a new issue