first commit
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { Server as IOServer } from 'socket.io';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import {
|
||||
SOCKET_EVENTS,
|
||||
departmentRoom,
|
||||
DEPARTMENTS,
|
||||
type ServerToClientEvents,
|
||||
type ClientToServerEvents,
|
||||
type AuthUser,
|
||||
type Department,
|
||||
type Officer,
|
||||
type OfficerPositionUpdate,
|
||||
type OfficerDutyUpdate,
|
||||
type DispatchCall,
|
||||
type Warrant,
|
||||
type Coords,
|
||||
} from '@d4rk-tablet/shared';
|
||||
import { env } from '../config/env';
|
||||
import { officerSnapshot } from './officers';
|
||||
|
||||
type AppIO = IOServer<ClientToServerEvents, ServerToClientEvents>;
|
||||
|
||||
let io: AppIO | null = null;
|
||||
|
||||
/** Departments, in denen der User Mitglied ist (für Room-Zuordnung). */
|
||||
function userDepartments(user: AuthUser): Department[] {
|
||||
return DEPARTMENTS.filter((d) => user.roles.includes(d));
|
||||
}
|
||||
|
||||
export function initSocket(app: FastifyInstance): AppIO {
|
||||
io = new IOServer<ClientToServerEvents, ServerToClientEvents>(app.server, {
|
||||
cors: { origin: env.CORS_ORIGINS, credentials: true },
|
||||
transports: ['websocket'],
|
||||
});
|
||||
|
||||
// JWT-Auth beim Handshake
|
||||
io.use((socket, next) => {
|
||||
try {
|
||||
const token = (socket.handshake.auth as { token?: string })?.token;
|
||||
if (!token) return next(new Error('Kein Token'));
|
||||
const user = app.jwt.verify<AuthUser>(token);
|
||||
socket.data.user = user;
|
||||
next();
|
||||
} catch {
|
||||
next(new Error('Ungültiges Token'));
|
||||
}
|
||||
});
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
const user = socket.data.user as AuthUser;
|
||||
const depts = userDepartments(user);
|
||||
for (const d of depts) void socket.join(departmentRoom(d));
|
||||
|
||||
// Initialer Snapshot der Officer der eigenen Departments
|
||||
const snapshot: Officer[] = depts.flatMap((d) => officerSnapshot(d));
|
||||
socket.emit(SOCKET_EVENTS.OFFICER_SNAPSHOT, snapshot);
|
||||
|
||||
// Client → Server: Waypoint an einen Officer senden (→ Bridge in M7)
|
||||
socket.on(SOCKET_EVENTS.OFFICER_SET_WAYPOINT, (payload) => {
|
||||
app.log.info({ payload }, 'setWaypoint (Bridge-Weiterleitung folgt in M7)');
|
||||
});
|
||||
});
|
||||
|
||||
app.addHook('onClose', async () => {
|
||||
await io?.close();
|
||||
});
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
function getIO(): AppIO {
|
||||
if (!io) throw new Error('Socket.io nicht initialisiert');
|
||||
return io;
|
||||
}
|
||||
|
||||
// ── Broadcast-Helfer (von Services genutzt) ──
|
||||
export function broadcastDispatchCreated(call: DispatchCall): void {
|
||||
getIO().to(departmentRoom(call.department)).emit(SOCKET_EVENTS.DISPATCH_CREATED, call);
|
||||
}
|
||||
|
||||
export function broadcastDispatchUpdated(call: DispatchCall): void {
|
||||
getIO().to(departmentRoom(call.department)).emit(SOCKET_EVENTS.DISPATCH_UPDATED, call);
|
||||
}
|
||||
|
||||
export function broadcastOfficerMoved(dept: Department, update: OfficerPositionUpdate): void {
|
||||
getIO().to(departmentRoom(dept)).emit(SOCKET_EVENTS.OFFICER_MOVED, update);
|
||||
}
|
||||
|
||||
export function broadcastOfficerDuty(dept: Department, update: OfficerDutyUpdate): void {
|
||||
getIO().to(departmentRoom(dept)).emit(SOCKET_EVENTS.OFFICER_DUTY, update);
|
||||
}
|
||||
|
||||
export function broadcastWarrantUpdated(dept: Department, warrant: Warrant): void {
|
||||
getIO().to(departmentRoom(dept)).emit(SOCKET_EVENTS.WARRANT_UPDATED, warrant);
|
||||
}
|
||||
|
||||
export type { Coords };
|
||||
Reference in New Issue
Block a user