updates
This commit is contained in:
15
server/README.md
Normal file
15
server/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# chatroom
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.2.21. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||
9
server/eslint.config.ts
Normal file
9
server/eslint.config.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import {defineConfig} from 'eslint/config';
|
||||
|
||||
export default defineConfig([
|
||||
{files: ['**/*.{js,mjs,cjs,ts,mts,cts}'], plugins: {js}, extends: ['js/recommended'], languageOptions: {globals: globals.node}},
|
||||
tseslint.configs.recommended
|
||||
]);
|
||||
29
server/package.json
Normal file
29
server/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "chatroom",
|
||||
"version": "0.0.0",
|
||||
"module": "src/index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"format": "prettier --write ./",
|
||||
"prelint": "bun run format",
|
||||
"lint": "eslint --max-warnings 0 ./",
|
||||
"lint:fix": "eslint --fix ./",
|
||||
"dev": "bun run --watch src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.34.0",
|
||||
"@types/bun": "latest",
|
||||
"@types/node": "24.3.0",
|
||||
"eslint": "9.34.0",
|
||||
"globals": "16.3.0",
|
||||
"jiti": "2.5.1",
|
||||
"prettier": "3.6.2",
|
||||
"typescript": "5.9.2",
|
||||
"typescript-eslint": "8.40.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@techniker-me/logger": "0.0.15",
|
||||
"@techniker-me/tools": "2025.0.16"
|
||||
}
|
||||
}
|
||||
7
server/src/index.ts
Normal file
7
server/src/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import Servers from './net/Servers';
|
||||
|
||||
|
||||
const server = Servers.createServerWithWebSockets();
|
||||
|
||||
Bun.serve(server);
|
||||
console.log(`Server is running on [:${server.port}]`);
|
||||
39
server/src/net/ServerWithWebSockets.ts
Normal file
39
server/src/net/ServerWithWebSockets.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type {Server, WebSocketHandler} from 'bun';
|
||||
import {LoggerFactory, type ILogger} from '@techniker-me/logger';
|
||||
import WebSocketManager from './sockets/WebsocketManager';
|
||||
|
||||
export default class ServerWithWebSockets {
|
||||
private readonly _logger: ILogger = LoggerFactory.getLogger('ServerWithWebSockets');
|
||||
private readonly _webSocketManager: WebSocketManager = new WebSocketManager();
|
||||
private readonly _port: number = 8080;
|
||||
|
||||
constructor() {
|
||||
this.fetch = this.fetch.bind(this);
|
||||
}
|
||||
|
||||
get port(): number {
|
||||
return this._port;
|
||||
}
|
||||
|
||||
get websocket(): WebSocketHandler {
|
||||
return this._webSocketManager.websocketHandler;
|
||||
}
|
||||
|
||||
public fetch(request: Request, server: Server) {
|
||||
const requestUrl = new URL(request.url);
|
||||
const requestCookies = new Bun.CookieMap(request.headers.get('cookie') ?? '');
|
||||
|
||||
if (requestUrl.pathname === '/ws') {
|
||||
server.upgrade(request, {
|
||||
data: {
|
||||
receivedAt: new Date(),
|
||||
cookies: requestCookies
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return new Response('Not found', {status: 404});
|
||||
}
|
||||
}
|
||||
11
server/src/net/Servers.ts
Normal file
11
server/src/net/Servers.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import ServerWithWebSockets from './ServerWithWebSockets';
|
||||
|
||||
export default class Servers {
|
||||
public static createServerWithWebSockets() {
|
||||
return new ServerWithWebSockets();
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
throw new Error('Servers is a static class and cannot be instantiated');
|
||||
}
|
||||
}
|
||||
28
server/src/net/sockets/WebSocketClient.ts
Normal file
28
server/src/net/sockets/WebSocketClient.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type {ServerWebSocket} from 'bun';
|
||||
import type {IDisposable} from '@techniker-me/tools';
|
||||
|
||||
export default class WebSocketClient implements IDisposable {
|
||||
private readonly _id: string;
|
||||
private readonly _socket: ServerWebSocket;
|
||||
|
||||
constructor(id: string, socket: ServerWebSocket) {
|
||||
this._id = id;
|
||||
this._socket = socket;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get socket(): ServerWebSocket {
|
||||
return this._socket;
|
||||
}
|
||||
|
||||
public send<T>(message: T) {
|
||||
this._socket.send(JSON.stringify(message));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._socket.close();
|
||||
}
|
||||
}
|
||||
47
server/src/net/sockets/WebSocketHandler.ts
Normal file
47
server/src/net/sockets/WebSocketHandler.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type {ServerWebSocket} from 'bun';
|
||||
import WebSocketClient from './WebSocketClient';
|
||||
|
||||
|
||||
export default class WebSocketHandler<T = unknown> {
|
||||
private readonly _sockets: Map<string, WebSocketClient> = new Map();
|
||||
|
||||
constructor(sockets: Map<string, WebSocketClient>) {
|
||||
this._sockets = sockets;
|
||||
this.open = this.open.bind(this);
|
||||
this.message = this.message.bind(this);
|
||||
this.drain = this.drain.bind(this);
|
||||
this.close = this.close.bind(this);
|
||||
this.ping = this.ping.bind(this);
|
||||
this.pong = this.pong.bind(this);
|
||||
}
|
||||
|
||||
public open(ws: ServerWebSocket<T>) {
|
||||
const websocketClient = new WebSocketClient('id', ws);
|
||||
|
||||
this._sockets.set(websocketClient.id, websocketClient);
|
||||
}
|
||||
|
||||
public message(ws: ServerWebSocket<T>, message: string) {
|
||||
console.log('WebSocket message:', message);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public drain(_ws: ServerWebSocket<T>) {
|
||||
console.log('WebSocket drained');
|
||||
}
|
||||
|
||||
public close(ws: ServerWebSocket<T>) {
|
||||
console.log('WebSocket closed');
|
||||
this._sockets.delete(ws.id);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public ping(ws: ServerWebSocket<T>) {
|
||||
console.log('WebSocket ping');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public pong(ws: ServerWebSocket<T>) {
|
||||
console.log('WebSocket pong');
|
||||
}
|
||||
}
|
||||
22
server/src/net/sockets/WebsocketManager.ts
Normal file
22
server/src/net/sockets/WebsocketManager.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {type ILogger, LoggerFactory} from '@techniker-me/logger';
|
||||
import type {IDisposable} from '@techniker-me/tools';
|
||||
import WebSocketClient from './WebSocketClient';
|
||||
import WebSocketHandler from './WebSocketHandler';
|
||||
|
||||
export default class WebsocketManager implements IDisposable {
|
||||
private readonly _logger: ILogger = LoggerFactory.getLogger('WebsocketManager');
|
||||
private readonly _clients: Map<string, WebSocketClient> = new Map();
|
||||
private readonly _websocketHandler: WebSocketHandler;
|
||||
|
||||
constructor() {
|
||||
this._websocketHandler = new WebSocketHandler(this._clients);
|
||||
}
|
||||
|
||||
get websocketHandler(): WebSocketHandler {
|
||||
return this._websocketHandler;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._clients.forEach(client => client.dispose());
|
||||
}
|
||||
}
|
||||
29
server/tsconfig.json
Normal file
29
server/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user