added Callback and custon notification Toast, client notification event handler
This commit is contained in:
11
apps/dispatch-server/.d.ts
vendored
11
apps/dispatch-server/.d.ts
vendored
@@ -6,3 +6,14 @@ declare module "next-auth/jwt" {
|
||||
email: string;
|
||||
}
|
||||
}
|
||||
declare module "cookie-parser";
|
||||
|
||||
import type { User } from "@repo/db";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: User | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,18 @@ import cors from "cors";
|
||||
import { handleSendMessage } from "socket-events/send-message";
|
||||
import { handleConnectPilot } from "socket-events/connect-pilot";
|
||||
import { handleConnectDesktop } from "socket-events/connect-desktop";
|
||||
import cookieParser from "cookie-parser";
|
||||
import { authMiddleware } from "modules/expressMiddleware";
|
||||
import { prisma, User } from "@repo/db";
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: User | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const app = express();
|
||||
const server = createServer(app);
|
||||
@@ -31,6 +43,8 @@ io.on("connection", (socket) => {
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(cookieParser());
|
||||
app.use(authMiddleware as any);
|
||||
app.use(router);
|
||||
|
||||
server.listen(process.env.PORT, () => {
|
||||
|
||||
24
apps/dispatch-server/modules/expressMiddleware.ts
Normal file
24
apps/dispatch-server/modules/expressMiddleware.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { prisma, User } from "@repo/db";
|
||||
import { NextFunction } from "express";
|
||||
|
||||
interface AttachUserRequest extends Request {
|
||||
user?: User | null;
|
||||
}
|
||||
|
||||
interface AttachUserMiddleware {
|
||||
(req: AttachUserRequest, res: Response, next: NextFunction): Promise<void>;
|
||||
}
|
||||
|
||||
export const authMiddleware: AttachUserMiddleware = async (req, res, next) => {
|
||||
const authHeader = (req.headers as any).authorization;
|
||||
if (authHeader && authHeader.startsWith("User ")) {
|
||||
const userId = authHeader.split(" ")[1];
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
req.user = user;
|
||||
}
|
||||
next();
|
||||
};
|
||||
101
apps/dispatch-server/modules/mission.ts
Normal file
101
apps/dispatch-server/modules/mission.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { ConnectedAircraft, Mission, prisma } from "@repo/db";
|
||||
import { io } from "index";
|
||||
import { sendNtfyMission } from "modules/ntfy";
|
||||
|
||||
export const sendAlert = async (
|
||||
id: number,
|
||||
{
|
||||
stationId,
|
||||
}: {
|
||||
stationId?: number;
|
||||
},
|
||||
): Promise<{
|
||||
connectedAircrafts: ConnectedAircraft[];
|
||||
mission: Mission;
|
||||
}> => {
|
||||
const mission = await prisma.mission.findUnique({
|
||||
where: { id: id },
|
||||
});
|
||||
const Stations = await prisma.station.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: mission?.missionStationIds,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!mission) {
|
||||
throw new Error("Mission not found");
|
||||
}
|
||||
|
||||
// connectedAircrafts the alert is sent to
|
||||
const connectedAircrafts = await prisma.connectedAircraft.findMany({
|
||||
where: {
|
||||
stationId: stationId
|
||||
? stationId
|
||||
: {
|
||||
in: mission.missionStationIds,
|
||||
},
|
||||
logoutTime: null,
|
||||
},
|
||||
include: {
|
||||
Station: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const aircraft of connectedAircrafts) {
|
||||
console.log(`Sending mission to: station:${aircraft.stationId}`);
|
||||
io.to(`station:${aircraft.stationId}`).emit("mission-alert", {
|
||||
...mission,
|
||||
Stations,
|
||||
});
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: aircraft.userId },
|
||||
});
|
||||
if (!user) continue;
|
||||
if (user.settingsNtfyRoom) {
|
||||
await sendNtfyMission(
|
||||
mission,
|
||||
Stations,
|
||||
aircraft.Station,
|
||||
user.settingsNtfyRoom,
|
||||
);
|
||||
}
|
||||
const existingMissionOnStationUser =
|
||||
await prisma.missionOnStationUsers.findFirst({
|
||||
where: {
|
||||
missionId: mission.id,
|
||||
userId: aircraft.userId,
|
||||
stationId: aircraft.stationId,
|
||||
},
|
||||
});
|
||||
if (!existingMissionOnStationUser)
|
||||
await prisma.missionOnStationUsers.create({
|
||||
data: {
|
||||
missionId: mission.id,
|
||||
userId: aircraft.userId,
|
||||
stationId: aircraft.stationId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// for statistics only
|
||||
await prisma.missionsOnStations
|
||||
.createMany({
|
||||
data: mission.missionStationIds.map((stationId) => ({
|
||||
missionId: mission.id,
|
||||
stationId,
|
||||
})),
|
||||
})
|
||||
.catch(() => {
|
||||
// Ignore if the entry already exists
|
||||
});
|
||||
|
||||
await prisma.mission.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
state: "running",
|
||||
},
|
||||
});
|
||||
return { connectedAircrafts, mission };
|
||||
};
|
||||
@@ -10,6 +10,7 @@
|
||||
"devDependencies": {
|
||||
"@repo/db": "*",
|
||||
"@repo/typescript-config": "*",
|
||||
"@types/cookie-parser": "^1.4.8",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
@@ -21,6 +22,7 @@
|
||||
"@redis/json": "^1.0.7",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"axios": "^1.7.9",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"cron": "^4.1.0",
|
||||
"dotenv": "^16.4.7",
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import { HpgValidationState, Prisma, prisma } from "@repo/db";
|
||||
import {
|
||||
HpgValidationState,
|
||||
MissionSdsLog,
|
||||
MissionStationLog,
|
||||
NotificationPayload,
|
||||
Prisma,
|
||||
prisma,
|
||||
User,
|
||||
} from "@repo/db";
|
||||
import { Router } from "express";
|
||||
import { io } from "../index";
|
||||
import { sendNtfyMission } from "modules/ntfy";
|
||||
import { sendAlert } from "modules/mission";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -107,90 +116,8 @@ router.post("/:id/send-alert", async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { stationId } = req.body as { stationId?: number };
|
||||
try {
|
||||
const mission = await prisma.mission.findUnique({
|
||||
where: { id: Number(id) },
|
||||
});
|
||||
const Stations = await prisma.station.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: mission?.missionStationIds,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!mission) {
|
||||
res.status(404).json({ error: "Mission not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
// connectedAircrafts the alert is sent to
|
||||
const connectedAircrafts = await prisma.connectedAircraft.findMany({
|
||||
where: {
|
||||
stationId: stationId
|
||||
? stationId
|
||||
: {
|
||||
in: mission.missionStationIds,
|
||||
},
|
||||
logoutTime: null,
|
||||
},
|
||||
include: {
|
||||
Station: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const aircraft of connectedAircrafts) {
|
||||
console.log(`Sending mission to: station:${aircraft.stationId}`);
|
||||
io.to(`station:${aircraft.stationId}`).emit("mission-alert", {
|
||||
...mission,
|
||||
Stations,
|
||||
});
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: aircraft.userId },
|
||||
});
|
||||
if (!user) continue;
|
||||
if (user.settingsNtfyRoom) {
|
||||
await sendNtfyMission(
|
||||
mission,
|
||||
Stations,
|
||||
aircraft.Station,
|
||||
user.settingsNtfyRoom,
|
||||
);
|
||||
}
|
||||
const existingMissionOnStationUser =
|
||||
await prisma.missionOnStationUsers.findFirst({
|
||||
where: {
|
||||
missionId: mission.id,
|
||||
userId: aircraft.userId,
|
||||
stationId: aircraft.stationId,
|
||||
},
|
||||
});
|
||||
if (!existingMissionOnStationUser)
|
||||
await prisma.missionOnStationUsers.create({
|
||||
data: {
|
||||
missionId: mission.id,
|
||||
userId: aircraft.userId,
|
||||
stationId: aircraft.stationId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// for statistics only
|
||||
await prisma.missionsOnStations
|
||||
.createMany({
|
||||
data: mission.missionStationIds.map((stationId) => ({
|
||||
missionId: mission.id,
|
||||
stationId,
|
||||
})),
|
||||
})
|
||||
.catch(() => {
|
||||
// Ignore if the entry already exists
|
||||
});
|
||||
|
||||
await prisma.mission.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
state: "running",
|
||||
},
|
||||
const { connectedAircrafts, mission } = await sendAlert(Number(id), {
|
||||
stationId,
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
@@ -203,9 +130,36 @@ router.post("/:id/send-alert", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/:id/send-sds", async (req, res) => {
|
||||
const sdsMessage = req.body as MissionSdsLog;
|
||||
const newMission = await prisma.mission.update({
|
||||
where: {
|
||||
id: Number(req.params.id),
|
||||
},
|
||||
data: {
|
||||
missionLog: {
|
||||
push: sdsMessage as any,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
io.to(`station:${sdsMessage.data.stationId}`).emit("sds-message", sdsMessage);
|
||||
res.json({
|
||||
message: "SDS message sent",
|
||||
mission: newMission,
|
||||
});
|
||||
io.to("dispatchers").emit("update-mission", newMission);
|
||||
});
|
||||
|
||||
router.post("/:id/validate-hpg", async (req, res) => {
|
||||
try {
|
||||
console.log(req.user);
|
||||
const { id } = req.params;
|
||||
const config = req.body as
|
||||
| {
|
||||
alertWhenValid?: boolean;
|
||||
}
|
||||
| undefined;
|
||||
const mission = await prisma.mission.findFirstOrThrow({
|
||||
where: {
|
||||
id: Number(id),
|
||||
@@ -225,16 +179,11 @@ router.post("/:id/validate-hpg", async (req, res) => {
|
||||
},
|
||||
});
|
||||
|
||||
/* if (activeAircraftinMission.length === 0) {
|
||||
res.status(400).json({ error: "No active aircraft in mission" });
|
||||
return;
|
||||
} */
|
||||
|
||||
res.json({
|
||||
message: "HPG validation started",
|
||||
});
|
||||
|
||||
io.to(`desktop:${activeAircraftinMission}`).emit(
|
||||
/* io.to(`desktop:${activeAircraftinMission}`).emit(
|
||||
"hpg-validation",
|
||||
{
|
||||
hpgMissionType: mission?.hpgMissionString,
|
||||
@@ -266,8 +215,41 @@ router.post("/:id/validate-hpg", async (req, res) => {
|
||||
},
|
||||
});
|
||||
io.to("dispatchers").emit("update-mission", newMission);
|
||||
|
||||
const noActionRequired = result.state === "VALID";
|
||||
if (noActionRequired) {
|
||||
io.to(`user:${req.user?.id}`).emit("notification", {
|
||||
type: "hpg-validation",
|
||||
status: "success",
|
||||
message: `HPG Validierung erfolgreich`,
|
||||
} as NotificationPayload);
|
||||
if (config?.alertWhenValid) {
|
||||
sendAlert(Number(id), {});
|
||||
}
|
||||
} else {
|
||||
io.to(`user:${req.user?.id}`).emit("notification", {
|
||||
type: "hpg-validation",
|
||||
status: "failed",
|
||||
message: `HPG Validation fehlgeschlagen`,
|
||||
} as NotificationPayload);
|
||||
}
|
||||
},
|
||||
);
|
||||
); */
|
||||
setTimeout(() => {
|
||||
io.to(`user:${req.user?.id}`).emit("notification", {
|
||||
type: "hpg-validation",
|
||||
status: "success",
|
||||
message: "HPG_BUSY",
|
||||
data: {
|
||||
mission,
|
||||
},
|
||||
} as NotificationPayload);
|
||||
io.to(`user:${req.user?.id}`).emit("notification", {
|
||||
type: "hpg-validation",
|
||||
status: "failed",
|
||||
message: `HPG Validation fehlgeschlagen`,
|
||||
} as NotificationPayload);
|
||||
}, 5000);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.json({ error: (error as Error).message || "Failed to validate HPG" });
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"baseUrl": ".",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["**/*.ts", "./index.ts"],
|
||||
"include": ["**/*.ts", "./index.ts", "**/*.d.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user