From 05b7d0fd39ba92cd3126445db191ddc2b7d2c47d Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Fri, 14 Mar 2025 00:18:06 -0700 Subject: [PATCH] continue dispatch-server --- apps/dispatch-server/.d.ts | 8 +++ apps/dispatch-server/.env.example | 3 +- apps/dispatch-server/index.ts | 30 ++++---- apps/dispatch-server/modules/redis.ts | 8 +++ apps/dispatch-server/modules/socketJWT.ts | 28 ++++++++ apps/dispatch-server/package.json | 1 + .../socket-events/connect-dispatch.ts | 12 +++- .../app/(dispatch)/_components/Navbar.tsx | 5 ++ apps/dispatch/app/(dispatch)/layout.tsx | 1 - apps/dispatch/app/(dispatch)/socket.ts | 7 ++ apps/dispatch/app/_store/connectionStore.ts | 16 +++++ apps/dispatch/app/_store/stationsStore.ts | 9 +++ apps/dispatch/package.json | 1 + package-lock.json | 72 +++++++++++++++++++ 14 files changed, 179 insertions(+), 22 deletions(-) create mode 100644 apps/dispatch-server/.d.ts create mode 100644 apps/dispatch-server/modules/redis.ts create mode 100644 apps/dispatch-server/modules/socketJWT.ts create mode 100644 apps/dispatch/app/(dispatch)/socket.ts create mode 100644 apps/dispatch/app/_store/connectionStore.ts create mode 100644 apps/dispatch/app/_store/stationsStore.ts diff --git a/apps/dispatch-server/.d.ts b/apps/dispatch-server/.d.ts new file mode 100644 index 00000000..8b3f9cfb --- /dev/null +++ b/apps/dispatch-server/.d.ts @@ -0,0 +1,8 @@ +declare module "next-auth/jwt" { + interface JWT { + uid: string; + firstname: string; + lastname: string; + email: string; + } +} diff --git a/apps/dispatch-server/.env.example b/apps/dispatch-server/.env.example index 47b922aa..3ed5e2cd 100644 --- a/apps/dispatch-server/.env.example +++ b/apps/dispatch-server/.env.example @@ -1,3 +1,4 @@ PORT=3002 REDIS_HOST=localhost -REDIS_PORT=6379 \ No newline at end of file +REDIS_PORT=6379 +DISPATCH_APP_TOKEN= \ No newline at end of file diff --git a/apps/dispatch-server/index.ts b/apps/dispatch-server/index.ts index 962f3caa..995a3b38 100644 --- a/apps/dispatch-server/index.ts +++ b/apps/dispatch-server/index.ts @@ -3,27 +3,23 @@ import express from "express"; import { createServer } from "http"; import { handle } from "socket-events/connect-dispatch"; import { Server } from "socket.io"; -import { createClient } from "redis"; import { createAdapter } from "@socket.io/redis-adapter"; - -const pubClient = createClient(); -const subClient = pubClient.duplicate(); +import { jwtMiddleware } from "modules/socketJWT"; +import { pubClient, subClient } from "modules/redis"; const app = express(); const server = createServer(app); -const initApp = async () => { - await Promise.all([pubClient.connect(), subClient.connect()]); - const io = new Server(server, { - adapter: createAdapter(pubClient, subClient), - }); +const io = new Server(server, { + adapter: createAdapter(pubClient, subClient), + cors: {}, +}); - io.on("connection", (socket) => { - socket.on("connect-dispatch", handle); - }); - server.listen(process.env.PORT, () => { - console.log(`Server running on port ${process.env.PORT}`); - }); -}; +io.use(jwtMiddleware); -initApp(); +io.on("connection", (socket) => { + socket.on("connect-dispatch", () => {}); +}); +server.listen(process.env.PORT, () => { + console.log(`Server running on port ${process.env.PORT}`); +}); diff --git a/apps/dispatch-server/modules/redis.ts b/apps/dispatch-server/modules/redis.ts new file mode 100644 index 00000000..02151d4a --- /dev/null +++ b/apps/dispatch-server/modules/redis.ts @@ -0,0 +1,8 @@ +import { createClient } from "redis"; + +export const pubClient = createClient(); +export const subClient = pubClient.duplicate(); + +Promise.all([pubClient.connect(), subClient.connect()]).then(() => { + console.log("Redis connected"); +}); diff --git a/apps/dispatch-server/modules/socketJWT.ts b/apps/dispatch-server/modules/socketJWT.ts new file mode 100644 index 00000000..e88c1301 --- /dev/null +++ b/apps/dispatch-server/modules/socketJWT.ts @@ -0,0 +1,28 @@ +import { ExtendedError, Server, Socket } from "socket.io"; +import jwt from "jsonwebtoken"; +import { pubClient } from "modules/redis"; + +if (!process.env.DISPATCH_APP_TOKEN) + throw new Error("DISPATCH_APP_TOKEN is not defined"); + +export const jwtMiddleware = async ( + socket: Socket, + next: (err?: ExtendedError) => void, +) => { + try { + const token = socket.handshake.auth?.token; + console.log(socket.handshake); + if (!token) return new Error("Authentication error"); + const decoded = jwt.verify(token, process.env.DISPATCH_APP_TOKEN!); + // socket.data.userId = decoded.; // User ID lokal speichern + + // Prüfen, ob der Nutzer ein Disponent ist + const userId: any = decoded; + socket.data.user = decoded; // Lokal speichern + + // In Redis speichern: Key = 'connected_dispatchers', Feld = socket.id, Wert = JSON-String des Users + } catch (err) { + console.error(err); + next(new Error("Authentication error")); + } +}; diff --git a/apps/dispatch-server/package.json b/apps/dispatch-server/package.json index bb691558..25330a35 100644 --- a/apps/dispatch-server/package.json +++ b/apps/dispatch-server/package.json @@ -24,6 +24,7 @@ "cron": "^4.1.0", "dotenv": "^16.4.7", "express": "^4.21.2", + "jsonwebtoken": "^9.0.2", "nodemailer": "^6.10.0", "react": "^19.0.0", "redis": "^4.7.0", diff --git a/apps/dispatch-server/socket-events/connect-dispatch.ts b/apps/dispatch-server/socket-events/connect-dispatch.ts index b43c3d28..47048cb3 100644 --- a/apps/dispatch-server/socket-events/connect-dispatch.ts +++ b/apps/dispatch-server/socket-events/connect-dispatch.ts @@ -1,8 +1,14 @@ +import { pubClient } from "modules/redis"; import { Server, Socket } from "socket.io"; -export const handle = async (socket: Socket, io: Server) => { - console.log("Connected to dispatch server"); - socket.on("disconnect", () => { +export const handle = (socket: Socket, io: Server) => async (jwt: string) => { + const userId = socket.data.user.id; // User ID aus dem JWT-Token + await pubClient.set(`dispatchers:${socket.id}`, userId); + + socket.join("dispatchers"); // Dem Dispatcher-Raum beitreten + + socket.on("disconnect", async () => { console.log("Disconnected from dispatch server"); + await pubClient.del(`dispatchers:${socket.id}`); }); }; diff --git a/apps/dispatch/app/(dispatch)/_components/Navbar.tsx b/apps/dispatch/app/(dispatch)/_components/Navbar.tsx index 20b5e05d..88d5dba0 100644 --- a/apps/dispatch/app/(dispatch)/_components/Navbar.tsx +++ b/apps/dispatch/app/(dispatch)/_components/Navbar.tsx @@ -3,8 +3,13 @@ import { ToggleTalkButton } from "../_components/ToggleTalkButton"; import { ChangeRufgruppe } from "../_components/ChangeRufgruppe"; import { Notifications } from "../_components/Notifications"; import Link from "next/link"; +import { connectionStore } from "../../_store/connectionStore"; +import { useEffect } from "react"; +import { socket } from "../socket"; export default function Navbar() { + const connected = connectionStore((state) => state); + return (
diff --git a/apps/dispatch/app/(dispatch)/layout.tsx b/apps/dispatch/app/(dispatch)/layout.tsx index eb1953ba..feae307f 100644 --- a/apps/dispatch/app/(dispatch)/layout.tsx +++ b/apps/dispatch/app/(dispatch)/layout.tsx @@ -1,6 +1,5 @@ import type { Metadata } from "next"; import Navbar from "./_components/Navbar"; -import { useSession } from "next-auth/react"; import { redirect } from "next/navigation"; import { getServerSession } from "../api/auth/[...nextauth]/auth"; diff --git a/apps/dispatch/app/(dispatch)/socket.ts b/apps/dispatch/app/(dispatch)/socket.ts new file mode 100644 index 00000000..9f751448 --- /dev/null +++ b/apps/dispatch/app/(dispatch)/socket.ts @@ -0,0 +1,7 @@ +import { io } from "socket.io-client"; + +export const socket = io(process.env.NEXT_PUBLIC_DISPATCH_SERVER_URL, { + auth: (cb) => { + cb({ token: "jwt" }); + }, +}); diff --git a/apps/dispatch/app/_store/connectionStore.ts b/apps/dispatch/app/_store/connectionStore.ts new file mode 100644 index 00000000..5d03082a --- /dev/null +++ b/apps/dispatch/app/_store/connectionStore.ts @@ -0,0 +1,16 @@ +import { create } from "zustand"; +import { socket } from "../(dispatch)/socket"; + +console.log("connectionStore"); + +export const connectionStore = create((set) => ({ + isConnected: false, + connect: async (jwt: string) => { + socket.auth = { token: "jwt" }; + socket.connect(); + }, +})); + +socket.on("connect", () => { + connectionStore.setState({ isConnected: true }); +}); diff --git a/apps/dispatch/app/_store/stationsStore.ts b/apps/dispatch/app/_store/stationsStore.ts new file mode 100644 index 00000000..0a384b0a --- /dev/null +++ b/apps/dispatch/app/_store/stationsStore.ts @@ -0,0 +1,9 @@ +import { create } from "zustand"; +import { socket } from "../(dispatch)/socket"; + +export const stationStore = create((set) => { + return { + stations: [], + setStations: (stations: any) => set({ stations }), + }; +}); diff --git a/apps/dispatch/package.json b/apps/dispatch/package.json index 6e16ec2d..11b0cf81 100644 --- a/apps/dispatch/package.json +++ b/apps/dispatch/package.json @@ -20,6 +20,7 @@ "postcss": "^8.5.1", "react": "^19.0.0", "react-dom": "^19.0.0", + "socket.io-client": "^4.8.1", "tailwindcss": "^4.0.2", "zustand": "^5.0.3" }, diff --git a/package-lock.json b/package-lock.json index 5876c5b7..a0a4faa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "postcss": "^8.5.1", "react": "^19.0.0", "react-dom": "^19.0.0", + "socket.io-client": "^4.8.1", "tailwindcss": "^4.0.2", "zustand": "^5.0.3" }, @@ -52,6 +53,7 @@ "cron": "^4.1.0", "dotenv": "^16.4.7", "express": "^4.21.2", + "jsonwebtoken": "^9.0.2", "nodemailer": "^6.10.0", "react": "^19.0.0", "redis": "^4.7.0", @@ -5879,6 +5881,36 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -16067,6 +16099,38 @@ } } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -17786,6 +17850,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",