feat: Add TURN server support
All checks were successful
Queue Release Build / prepare (push) Successful in 15s
Deploy Web Apps / deploy (push) Successful in 5m35s
Queue Release Build / build-linux (push) Successful in 24m45s
Queue Release Build / build-windows (push) Successful in 13m52s
Queue Release Build / finalize (push) Successful in 23s

This commit is contained in:
2026-04-18 21:27:04 +02:00
parent 167c45ba8d
commit 44588e8789
60 changed files with 2404 additions and 365 deletions

40
server/README.md Normal file
View File

@@ -0,0 +1,40 @@
# Server
Node/TypeScript signaling server for MetoYou / Toju. This package owns the public server-directory API, join-request flows, websocket runtime, and server-side persistence.
## Install
1. Run `cd server`.
2. Run `npm install`.
## Commands
- `npm run dev` starts the server with `ts-node-dev` reload.
- `npm run build` compiles TypeScript to `dist/`.
- `npm run start` runs the compiled server.
- From the repository root, `npm run server:dev`, `npm run server:build`, and `npm run server:start` call the same package commands.
## Runtime Config
- The server loads the repository-root `.env` file on startup.
- `SSL` can override the effective HTTP protocol, and `PORT` can override the effective port.
- `data/variables.json` is normalized on startup and stores `klipyApiKey`, `releaseManifestUrl`, `serverPort`, `serverProtocol`, `serverHost`, and `linkPreview`.
- When HTTPS is enabled, certificates are read from the repository `.certs/` directory.
## Structure
| Path | Description |
| --- | --- |
| `src/index.ts` | Bootstrap and server startup |
| `src/app/` | Express app composition |
| `src/routes/` | REST API routes |
| `src/websocket/` | WebSocket runtime and signaling transport |
| `src/cqrs/` | Command/query handlers |
| `src/config/` | Runtime config loading and normalization |
| `src/db/`, `src/entities/`, `src/migrations/` | Persistence layer |
| `data/` | Runtime data files such as `variables.json` |
## Notes
- `dist/` and `../dist-server/` are generated output.
- See [AGENTS.md](AGENTS.md) for package-specific editing guidance.

View File

@@ -137,7 +137,7 @@ async function gracefulShutdown(signal: string): Promise<void> {
staleJoinRequestInterval = null;
}
console.log(`\n[Shutdown] ${signal} received - closing database`);
console.log(`\n[Shutdown] ${signal} received - closing database...`);
if (listeningServer?.listening) {
try {

View File

@@ -154,7 +154,7 @@ describe('server websocket handler - status_update', () => {
// Identify first (required for handler)
await handleWebSocketMessage('conn-1', { type: 'identify', oderId: 'user-1', displayName: 'User 1' });
// user-2 joins server should receive server_users with user-1's status
// user-2 joins server -> should receive server_users with user-1's status
getSentMessagesStore(user2).sentMessages.length = 0;
await handleWebSocketMessage('conn-2', { type: 'join_server', serverId: 'server-1' });

View File

@@ -276,7 +276,7 @@ function handleStatusUpdate(user: ConnectedUser, message: WsMessage, connectionI
user.status = status as ConnectedUser['status'];
connectedUsers.set(connectionId, user);
console.log(`User ${normalizeDisplayName(user.displayName)} (${user.oderId}) status ${status}`);
console.log(`User ${normalizeDisplayName(user.displayName)} (${user.oderId}) status -> ${status}`);
for (const serverId of user.serverIds) {
broadcastToServer(serverId, {