import { MissionLog, NotificationPayload, prisma } from "@repo/db"; import { io } from "index"; import cron from "node-cron"; const removeMission = async (id: number, reason: string) => { const log: MissionLog = { type: "completed-log", auto: true, timeStamp: new Date().toISOString(), data: {}, }; const updatedMission = await prisma.mission.update({ where: { id: id, }, data: { state: "finished", missionLog: { push: log as any, }, }, }); io.to("dispatchers").emit("new-mission", { updatedMission }); io.to("dispatchers").emit("notification", { type: "mission-auto-close", status: "chron", message: `Einsatz ${updatedMission.publicId} wurde aufgrund ${reason} geschlossen.`, data: { missionId: updatedMission.id, publicMissionId: updatedMission.publicId, }, } as NotificationPayload); console.log(`Mission ${updatedMission.id} closed due to inactivity.`); }; const removeClosedMissions = async () => { const oldMissions = await prisma.mission.findMany({ where: { state: "running", }, }); oldMissions.forEach(async (mission) => { const lastAlert = (mission.missionLog as unknown as MissionLog[]).find((l) => { return l.type === "alert-log"; }); const lastAlertTime = lastAlert ? new Date(lastAlert.timeStamp) : null; const allStationsInMissionChangedFromStatus4to1Or8to1 = mission.missionStationIds.every( (stationId) => { const status4Log = (mission.missionLog as unknown as MissionLog[]).findIndex((l) => { return ( l.type === "station-log" && l.data?.stationId === stationId && l.data?.newFMSstatus === "4" ); }); const status8Log = (mission.missionLog as unknown as MissionLog[]).findIndex((l) => { return ( l.type === "station-log" && l.data?.stationId === stationId && l.data?.newFMSstatus === "8" ); }); const status1Log = (mission.missionLog as unknown as MissionLog[]).findIndex((l) => { return ( l.type === "station-log" && l.data?.stationId === stationId && l.data?.newFMSstatus === "1" ); }); const status6Log = (mission.missionLog as unknown as MissionLog[]).findIndex((l) => { return ( l.type === "station-log" && l.data?.stationId === stationId && l.data?.newFMSstatus === "6" ); }); return ( (status4Log !== -1 || status8Log !== -1) && (status1Log !== -1 || status6Log !== -1) && (status4Log < status1Log || status8Log < status1Log || status8Log < status6Log || status1Log < status6Log) ); }, ); const missionHasManualReactivation = (mission.missionLog as unknown as MissionLog[]).some( (l) => l.type === "reopened-log", ); if (missionHasManualReactivation) return; if (!lastAlertTime) return; const lastStatus1or6Log = (mission.missionLog as unknown as MissionLog[]) .filter((l) => { return ( l.type === "station-log" && (l.data?.newFMSstatus === "1" || l.data?.newFMSstatus === "6") ); }) .sort((a, b) => new Date(b.timeStamp).getTime() - new Date(a.timeStamp).getTime())[0]; // Case 1: Forgotten Mission, last alert more than 3 Hours ago const now = new Date(); if (now.getTime() - lastAlertTime.getTime() > 1000 * 60 * 180) return removeMission(mission.id, "inaktivität"); // Case 2: All stations in mission changed from status 4 to 1/6 or from status 8 to 1/6, Status 1/6 change less more 5 minutes ago if ( allStationsInMissionChangedFromStatus4to1Or8to1 && lastStatus1or6Log && now.getTime() - new Date(lastStatus1or6Log.timeStamp).getTime() > 1000 * 60 * 5 ) return removeMission(mission.id, "dem freimelden aller Stationen"); }); }; const removeConnectedAircrafts = async () => { const connectedAircrafts = await prisma.connectedAircraft.findMany({ where: { logoutTime: null, }, }); connectedAircrafts.forEach(async (aircraft) => { const lastUpdate = new Date(aircraft.lastHeartbeat); const now = new Date(); if (now.getTime() - lastUpdate.getTime() > 12 * 60 * 60 * 1000) { await prisma.connectedAircraft.update({ where: { id: aircraft.id }, data: { logoutTime: now }, }); console.log(`Aircraft ${aircraft.id} disconnected due to inactivity.`); } }); }; cron.schedule("*/1 * * * *", async () => { try { await removeClosedMissions(); await removeConnectedAircrafts(); } catch (error) { console.error("Error on cron job:", error); } });