added Callback and custon notification Toast, client notification event handler

This commit is contained in:
PxlLoewe
2025-05-22 00:43:03 -07:00
parent 0f04174516
commit 8a4b42f02b
38 changed files with 715 additions and 339 deletions

View File

@@ -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;
}
}
}

View File

@@ -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, () => {

View 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();
};

View 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 };
};

View File

@@ -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",

View File

@@ -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" });

View File

@@ -6,6 +6,6 @@
"baseUrl": ".",
"jsx": "react"
},
"include": ["**/*.ts", "./index.ts"],
"include": ["**/*.ts", "./index.ts", "**/*.d.ts"],
"exclude": ["node_modules", "dist"]
}