382 lines
8.9 KiB
TypeScript
382 lines
8.9 KiB
TypeScript
import {
|
|
getPublicUser,
|
|
HpgValidationState,
|
|
MissionSdsLog,
|
|
NotificationPayload,
|
|
prisma,
|
|
User,
|
|
} from "@repo/db";
|
|
import { Router } from "express";
|
|
import { io } from "../index";
|
|
import { sendAlert } from "modules/mission";
|
|
|
|
const router: Router = Router();
|
|
|
|
// Get all missions
|
|
router.post("/", async (req, res) => {
|
|
try {
|
|
const filter = req.body?.filter || {};
|
|
const missions = await prisma.mission.findMany({
|
|
where: filter,
|
|
});
|
|
res.json(missions);
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).json({ error: "Failed to fetch missions" });
|
|
}
|
|
});
|
|
|
|
// Get a single mission by ID
|
|
router.get("/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
const mission = await prisma.mission.findUnique({
|
|
where: { id: Number(id) },
|
|
});
|
|
if (mission) {
|
|
res.json(mission);
|
|
} else {
|
|
res.status(404).json({ error: "Mission not found" });
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).json({ error: "Failed to fetch mission" });
|
|
}
|
|
});
|
|
|
|
// Create a new mission
|
|
router.put("/", async (req, res) => {
|
|
try {
|
|
const startOfToday = new Date();
|
|
startOfToday.setHours(0, 0, 0, 0);
|
|
|
|
const missionsTodayCount = await prisma.mission.count({
|
|
where: {
|
|
createdAt: {
|
|
gte: startOfToday,
|
|
},
|
|
},
|
|
});
|
|
|
|
const date = new Date();
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, "0"); // Months are zero-based
|
|
const day = String(date.getDate()).padStart(2, "0");
|
|
|
|
const publicId = `ENr.: ${year}${month}${day}${missionsTodayCount ? missionsTodayCount + 1 : 1}`;
|
|
const newMission = await prisma.mission.create({
|
|
data: {
|
|
...req.body,
|
|
publicId,
|
|
},
|
|
});
|
|
io.to("dispatchers").emit("new-mission", { newMission });
|
|
res.status(201).json(newMission);
|
|
} catch (error) {
|
|
res.status(500).json({ error: "Failed to create mission" });
|
|
}
|
|
});
|
|
|
|
// Update a mission by ID
|
|
router.patch("/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
console.log("Updating mission with ID:", id, req.body);
|
|
const updatedMission = await prisma.mission.update({
|
|
where: { id: Number(id) },
|
|
data: req.body,
|
|
});
|
|
io.to("dispatchers").emit("update-mission", { updatedMission });
|
|
if (req.body.state === "finished") {
|
|
updatedMission.missionStationUserIds?.forEach((userId) => {
|
|
io.to(`user:${userId}`).emit("notification", {
|
|
type: "mission-closed",
|
|
status: "closed",
|
|
message: `Einsatz ${updatedMission.publicId} wurde beendet`,
|
|
data: {
|
|
missionId: updatedMission.id,
|
|
publicMissionId: updatedMission.publicId,
|
|
},
|
|
} as NotificationPayload);
|
|
});
|
|
}
|
|
|
|
res.json(updatedMission);
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).json({ error: "Failed to update mission" });
|
|
}
|
|
});
|
|
|
|
// Delete a mission by ID
|
|
router.delete("/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
await prisma.mission.delete({
|
|
where: { id: Number(id) },
|
|
});
|
|
io.to("dispatchers").emit("delete-mission", id);
|
|
res.status(204).send();
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).json({ error: "Failed to delete mission" });
|
|
}
|
|
});
|
|
|
|
// Send mission
|
|
|
|
router.post("/:id/send-alert", async (req, res) => {
|
|
const { id } = req.params;
|
|
const { stationId, vehicleName, desktopOnly } = req.body as {
|
|
stationId?: number;
|
|
vehicleName?: "RTW" | "POL" | "FW";
|
|
desktopOnly?: boolean;
|
|
};
|
|
|
|
if (!req.user) {
|
|
res.status(401).json({ error: "Unauthorized" });
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (vehicleName) {
|
|
const mission = await prisma.mission.findFirst({
|
|
where: {
|
|
id: Number(id),
|
|
},
|
|
select: {
|
|
missionStationIds: true,
|
|
},
|
|
});
|
|
const hpgAircrafts = await prisma.connectedAircraft.findMany({
|
|
where: {
|
|
stationId: { in: mission?.missionStationIds },
|
|
logoutTime: null,
|
|
posH145active: true,
|
|
},
|
|
});
|
|
|
|
const updateData: any = {
|
|
missionLog: {
|
|
push: {
|
|
type: "alert-log",
|
|
auto: false,
|
|
timeStamp: new Date().toISOString(),
|
|
data: {
|
|
vehicle: vehicleName,
|
|
user: getPublicUser(req.user as User, { ignorePrivacy: true }),
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
if (vehicleName === "RTW") updateData.hpgAmbulanceState = "DISPATCHED";
|
|
if (vehicleName === "FW") updateData.hpgFireEngineState = "DISPATCHED";
|
|
if (vehicleName === "POL") updateData.hpgPoliceState = "DISPATCHED";
|
|
|
|
const newMission = await prisma.mission.update({
|
|
where: { id: Number(id) },
|
|
data: updateData,
|
|
});
|
|
|
|
hpgAircrafts.forEach((aircraft) => {
|
|
io.to(`desktop:${aircraft.userId}`).emit("hpg-vehicle-update", {
|
|
missionId: id,
|
|
ambulanceState: newMission.hpgAmbulanceState,
|
|
fireEngineState: newMission.hpgFireEngineState,
|
|
policeState: newMission.hpgPoliceState,
|
|
});
|
|
});
|
|
|
|
io.to("dispatchers").emit("update-mission", newMission);
|
|
res.status(200).json({
|
|
message: `Rettungsmittel disponiert (${hpgAircrafts.length} Nutzer)`,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { connectedAircrafts, mission } = await sendAlert(
|
|
Number(id),
|
|
{ stationId, desktopOnly },
|
|
req.user,
|
|
);
|
|
|
|
io.to("dispatchers").emit("update-mission", mission);
|
|
res.status(200).json({
|
|
message: `Einsatz gesendet (${connectedAircrafts.length} Nutzer)`,
|
|
});
|
|
return;
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).json({
|
|
error: `Ein Fehler ist aufgetreten. Bitte melde den Fehler als Bug (${(error as Error).message})`,
|
|
});
|
|
return;
|
|
}
|
|
});
|
|
|
|
router.post("/send-sds", async (req, res) => {
|
|
const { sdsMessage, missionId } = req.body as {
|
|
missionId?: number;
|
|
sdsMessage: MissionSdsLog;
|
|
};
|
|
|
|
io.to(`station:${sdsMessage.data.stationId}`).emit("sds-message", sdsMessage);
|
|
if (missionId) {
|
|
const newMission = await prisma.mission.update({
|
|
where: {
|
|
id: Number(missionId),
|
|
},
|
|
data: {
|
|
missionLog: {
|
|
push: sdsMessage as any,
|
|
},
|
|
},
|
|
});
|
|
|
|
res.json({
|
|
message: "SDS message sent",
|
|
mission: newMission,
|
|
});
|
|
io.to("dispatchers").emit("update-mission", newMission);
|
|
} else {
|
|
res.json({
|
|
message: "SDS message sent",
|
|
});
|
|
}
|
|
});
|
|
|
|
router.post("/:id/hpg-validation-result", async (req, res) => {
|
|
try {
|
|
const missionId = req.params.id;
|
|
const result = req.body as {
|
|
state: HpgValidationState;
|
|
lat: number;
|
|
lng: number;
|
|
alertWhenValid?: boolean;
|
|
userId?: number;
|
|
};
|
|
|
|
if (!result) return;
|
|
|
|
const newMission = await prisma.mission.update({
|
|
where: { id: Number(missionId) },
|
|
data: {
|
|
hpgLocationLat: result.lat,
|
|
hpgLocationLng: result.lng,
|
|
hpgValidationState: result.state,
|
|
},
|
|
});
|
|
io.to("dispatchers").emit("update-mission", newMission);
|
|
const noActionRequired = result.state === "VALID";
|
|
if (noActionRequired) {
|
|
io.to(`user:${result.userId}`).emit("notification", {
|
|
type: "hpg-validation",
|
|
status: "success",
|
|
message: `HPG Validierung erfolgreich`,
|
|
data: {
|
|
mission: newMission,
|
|
},
|
|
} as NotificationPayload);
|
|
|
|
if (result.alertWhenValid) {
|
|
sendAlert(Number(missionId), {}, "HPG");
|
|
}
|
|
} else {
|
|
io.to(`user:${result.userId}`).emit("notification", {
|
|
type: "hpg-validation",
|
|
status: "failed",
|
|
message: result.state,
|
|
data: {
|
|
mission: newMission,
|
|
},
|
|
} as NotificationPayload);
|
|
}
|
|
res.json({
|
|
message: `HPG Validation result processed`,
|
|
});
|
|
} catch (error) {
|
|
console.error("Error in HPG validation result:", error);
|
|
res.status(500).json({ error: "Failed to process HPG validation result" });
|
|
return;
|
|
}
|
|
});
|
|
|
|
router.post("/:id/validate-hpg", async (req, res) => {
|
|
try {
|
|
const config = await prisma.config.findFirst({
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
if (config?.disableHPG) {
|
|
res.status(400).json({
|
|
error: "HPG is disabled",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { id } = req.params;
|
|
const body = req.body as
|
|
| {
|
|
alertWhenValid?: boolean;
|
|
}
|
|
| undefined;
|
|
const mission = await prisma.mission.findFirstOrThrow({
|
|
where: {
|
|
id: Number(id),
|
|
},
|
|
});
|
|
|
|
const activeAircraftinMission = await prisma.connectedAircraft.findFirst({
|
|
where: {
|
|
stationId: {
|
|
in: mission?.missionStationIds,
|
|
},
|
|
posH145active: true,
|
|
logoutTime: null,
|
|
},
|
|
include: {
|
|
Station: true,
|
|
},
|
|
});
|
|
const user = await prisma.user.findFirst({
|
|
where: {
|
|
id: activeAircraftinMission?.userId,
|
|
},
|
|
});
|
|
|
|
const clients = await io.in(`desktop:${activeAircraftinMission?.userId}`).fetchSockets();
|
|
if (!clients.length) {
|
|
res.status(400).json({
|
|
error: `Keine Desktop Verbindung für ${user?.publicId} gefunden`,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const newMission = await prisma.mission.update({
|
|
where: {
|
|
id: Number(id),
|
|
},
|
|
data: {
|
|
hpgValidationState: "PENDING",
|
|
},
|
|
});
|
|
io.to("dispatchers").emit("update-mission", newMission);
|
|
|
|
res.json({
|
|
message: "HPG validierung gestartet",
|
|
});
|
|
io.to(`desktop:${activeAircraftinMission?.userId}`).emit("hpg-validation", {
|
|
missionId: parseInt(id),
|
|
userId: req.user?.id,
|
|
alertWhenValid: body?.alertWhenValid || false,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.json({ error: (error as Error).message || "Failed to validate HPG" });
|
|
}
|
|
});
|
|
|
|
export default router;
|