feat: Implement connected user API and integrate chat and report components

- Added API routes for fetching connected users, keywords, missions, and stations.
- Created a new QueryProvider component for managing query states and socket events.
- Introduced connection stores for dispatch and pilot, managing socket connections and states.
- Updated Prisma schema for connected aircraft model.
- Enhanced UI with toast notifications for status updates and chat interactions.
- Implemented query functions for fetching connected users and keywords with error handling.
This commit is contained in:
PxlLoewe
2025-05-07 00:43:45 -07:00
parent 152b3d4689
commit 50f42e99d3
49 changed files with 1040 additions and 701 deletions

View File

@@ -9,11 +9,12 @@ import { handleConnectDispatch } from "socket-events/connect-dispatch";
import router from "routes/router";
import cors from "cors";
import { handleSendMessage } from "socket-events/send-message";
import { handleConnectPilot } from "socket-events/connect-pilot";
const app = express();
const server = createServer(app);
const io = new Server(server, {
export const io = new Server(server, {
adapter: createAdapter(pubClient, subClient),
cors: {},
});
@@ -21,6 +22,7 @@ io.use(jwtMiddleware);
io.on("connection", (socket) => {
socket.on("connect-dispatch", handleConnectDispatch(socket, io));
socket.on("connect-pilot", handleConnectPilot(socket, io));
socket.on("send-message", handleSendMessage(socket, io));
});

View File

@@ -1,5 +1,6 @@
import { prisma } from "@repo/db";
import { Router } from "express";
import { io } from "../index";
const router = Router();
@@ -41,6 +42,7 @@ router.put("/", async (req, res) => {
const newMission = await prisma.mission.create({
data: req.body,
});
io.to("missions").emit("new-mission", newMission);
res.status(201).json(newMission);
} catch (error) {
res.status(500).json({ error: "Failed to create mission" });
@@ -55,6 +57,7 @@ router.patch("/:id", async (req, res) => {
where: { id: Number(id) },
data: req.body,
});
io.to("missions").emit("update-mission", updatedMission);
res.json(updatedMission);
} catch (error) {
console.error(error);
@@ -69,6 +72,7 @@ router.delete("/:id", async (req, res) => {
await prisma.mission.delete({
where: { id: Number(id) },
});
io.to("missions").emit("delete-mission", id);
res.status(204).send();
} catch (error) {
console.error(error);

View File

@@ -1,5 +1,4 @@
import { getPublicUser, prisma, User } from "@repo/db";
import { pubClient } from "modules/redis";
import { Server, Socket } from "socket.io";
export const handleConnectDispatch =
@@ -80,9 +79,10 @@ export const handleConnectDispatch =
socket.join("dispatchers"); // Dem Dispatcher-Raum beitreten
socket.join(`user:${user.id}`); // Dem User-Raum beitreten
socket.join("missions");
io.to("dispatchers").emit("dispatcher-update");
io.to("pilots").emit("dispatcher-update");
io.to("dispatchers").emit("dispatchers-update");
io.to("pilots").emit("dispatchers-update");
socket.on("disconnect", async () => {
console.log("Disconnected from dispatch server");
@@ -94,6 +94,8 @@ export const handleConnectDispatch =
logoutTime: new Date().toISOString(),
},
});
io.to("dispatchers").emit("dispatchers-update");
io.to("pilots").emit("dispatchers-update");
});
socket.on("reconnect", async () => {
console.log("Reconnected to dispatch server");

View File

@@ -1,25 +1,42 @@
import { getPublicUser, prisma } from "@repo/db";
import { Server, Socket } from "socket.io";
export const handleConnectDispatch =
export const handleConnectPilot =
(socket: Socket, io: Server) =>
async ({
logoffTime,
stationId,
}: {
logoffTime: string;
stationId: number;
stationId: string;
}) => {
try {
const user = socket.data.user; // User ID aus dem JWT-Token
const userId = socket.data.user.id; // User ID aus dem JWT-Token
console.log("User connected to dispatch server");
const user = await prisma.user.findUnique({
if (!user) return Error("User not found");
const existingConnection = await prisma.connectedAircraft.findFirst({
where: {
id: userId,
userId: user.id,
logoutTime: null,
},
});
if (!user) return Error("User not found");
if (existingConnection) {
await io
.to(`user:${user.id}`)
.emit("force-disconnect", "double-connection");
await prisma.connectedAircraft.updateMany({
where: {
userId: user.id,
logoutTime: null,
},
data: {
logoutTime: new Date().toISOString(),
},
});
}
let parsedLogoffDate = null;
if (logoffTime.length > 0) {
@@ -44,7 +61,7 @@ export const handleConnectDispatch =
lastHeartbeat: new Date().toISOString(),
userId: userId,
loginTime: new Date().toISOString(),
stationId: stationId,
stationId: parseInt(stationId),
/* user: { connect: { id: userId } }, // Ensure the user relationship is set
station: { connect: { id: stationId } }, // Ensure the station relationship is set */
},
@@ -54,8 +71,8 @@ export const handleConnectDispatch =
socket.join(`user:${userId}`); // Join the user-specific room
socket.join(`station:${stationId}`); // Join the station-specific room
io.to("dispatchers").emit("dispatcher-update");
io.to("pilots").emit("dispatcher-update");
io.to("dispatchers").emit("pilots-update");
io.to("pilots").emit("pilots-update");
// Add a listener for station-specific events
socket.on(`station:${stationId}:event`, async (data) => {
@@ -66,7 +83,7 @@ export const handleConnectDispatch =
socket.on("disconnect", async () => {
console.log("Disconnected from dispatch server");
await prisma.connectedDispatcher.update({
await prisma.connectedAircraft.update({
where: {
id: connectedAircraftEntry.id,
},
@@ -74,19 +91,8 @@ export const handleConnectDispatch =
logoutTime: new Date().toISOString(),
},
});
});
socket.on("reconnect", async () => {
console.log("Reconnected to dispatch server");
await prisma.connectedDispatcher.update({
where: {
id: connectedAircraftEntry.id,
},
data: {
lastHeartbeat: new Date().toISOString(),
logoutTime: null,
},
});
io.to("dispatchers").emit("pilots-update");
io.to("pilots").emit("pilots-update");
});
} catch (error) {
console.error("Error connecting to dispatch server:", error);