Files
crm.stream.graff.tech/server/src/routes/scheduledSessions.ts
T
2025-03-01 18:59:36 +05:00

283 lines
7.3 KiB
TypeScript

import { Router } from "express";
import ScheduledSession from "../models/ScheduledSession.js";
import Build from "../models/Build.js";
import Schedule from "../models/Schedule.js";
import {
addMinutes,
areIntervalsOverlapping,
endOfDay,
isValid,
parseISO,
startOfDay,
} from "date-fns";
import { createTransport } from "nodemailer";
import got from "got";
const router = Router();
router.get("/", async (_req, res) => {
const scheduledSessions = await ScheduledSession.find({
startAt: { $gte: startOfDay(new Date()) },
endAt: { $lte: endOfDay(new Date()) },
});
res.json(scheduledSessions);
});
router.get("/:id", async (req, res) => {
const scheduledSessionId = req.params.id;
const scheduledSession = await ScheduledSession.findById(scheduledSessionId);
res.json(scheduledSession);
});
router.get("/companies/:companyId/builds/:buildId", async (req, res) => {
const { companyId, buildId } = req.params;
const { date } = req.query;
try {
const scheduledSessions = await ScheduledSession.find({
companyId,
buildId,
startAt: {
$gte: startOfDay(parseISO(date as string)),
$lt: endOfDay(parseISO(date as string)),
},
});
res.json(scheduledSessions);
} catch (error) {
res.json({ error: (error as Error).message });
}
});
router.get("/:buildId", async (req, res) => {
if (!req.params.buildId) {
res.status(400).json({ error: "Parameter `buildId` is required" });
return;
}
if (!req.query.date) {
res.status(400).json({ error: "Query parameter `date` is required" });
return;
}
const buildId = req.params.buildId;
const date = req.query.date as string;
const scheduledSessions = await ScheduledSession.find({
buildId,
startAt: {
$gte: startOfDay(parseISO(date)),
$lt: endOfDay(parseISO(date)),
},
});
res.json(scheduledSessions);
});
router.post("/", async (req, res) => {
const { companyId, buildId, slot, startAt, client, duration } = req.body;
const startAtISO = parseISO(startAt);
if (!isValid(startAtISO)) {
return res.status(400).json({
status: "error",
message: "Invalid date format",
});
}
const build = await Build.findById(buildId);
if (!build) {
return res.status(400).json({
status: "error",
message: "An assembly with such a `buildId` was not found",
});
}
const endAtISO = addMinutes(startAtISO, duration);
// Check for overlapping sessions first
const scheduledSessions = await ScheduledSession.find({
companyId,
buildId,
slot,
startAt: {
$gte: startOfDay(startAtISO),
$lte: endOfDay(startAtISO),
},
});
if (scheduledSessions.length) {
const overlappingSessions = [];
for (const session of scheduledSessions) {
if (
areIntervalsOverlapping(
{
start: session.startAt,
end: session.endAt,
},
{ start: startAtISO, end: endAtISO }
)
) {
overlappingSessions.push(session);
}
}
if (overlappingSessions.length > 0) {
return res.status(400).json({
status: "error",
message:
"Невозможно создать сеанс, поскольку он пересекается со временем другого сеанса",
});
}
}
// Then check duration
if (duration < 30) {
return res.status(400).json({
status: "error",
message: "Продолжительность сеанса должна быть не менее 30 минут",
});
}
const scheduledSession = await ScheduledSession.create({
companyId,
buildId,
slot,
startAt: startAtISO,
duration,
endAt: endAtISO,
client,
});
const url = `https://stream.graff.tech/scheduled/${scheduledSession.id}`;
// <-- Send an mail
if (client?.email) {
console.log("client?.email", client?.email);
// create reusable transporter object using the default SMTP transport
let transporter = createTransport({
host: "mail.netangels.ru",
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: "stream@graff.tech", // generated ethereal user
pass: "zLUbt8Io7dh2F9KT", // generated ethereal password
},
});
// send mail with defined transport object
try {
await transporter.sendMail({
from: "stream@graff.tech", // sender address
to: client.email, // list of receivers
subject: "Приглашение на демонстрацию - stream.graff.tech", // Subject line
html: `<div>
Ссылка для подключения к демонстрации: <a href="${url}" target="_blank">${url}</a>
</div>`,
});
} catch (error) {
console.log("error", (error as Error).message);
}
}
return res.json({
status: "success",
scheduledSessionId: scheduledSession.id,
url: `https://stream.graff.tech/scheduled/${scheduledSession.id}`,
});
});
router.put("/:id", async (req, res) => {
const scheduledSessionId = req.params.id;
let {
activeSessionId,
}: { startAt: string; endAt: string; activeSessionId: string } = req.body;
const scheduledSession = await ScheduledSession.findById(scheduledSessionId);
if (!scheduledSession) {
return res.json({
status: "error",
message: "Session with this ID not found",
});
}
try {
const scheduledSession = await ScheduledSession.findByIdAndUpdate(
scheduledSessionId,
{ activeSessionId },
{ new: true }
);
res.json({ status: "success", scheduledSession });
} catch (error) {
if (error instanceof Error) {
res.json({ status: "error", message: error.message });
}
}
});
router.delete("/:id", async (req, res) => {
const scheduledSessionId = req.params.id;
let scheduledSession;
try {
scheduledSession = await ScheduledSession.findById(scheduledSessionId);
} catch (error) {
if (error instanceof Error) {
return res.json({ status: "error", message: error.message });
}
}
if (!scheduledSession) {
return res.json({ status: "error", message: "Session not found" });
}
if (scheduledSession.activeSessionId) {
// Check if more than 10 minutes have passed since session start
const startTime = new Date(scheduledSession.startAt);
const tenMinutesAfterStart = addMinutes(startTime, 10);
const now = new Date();
if (now > tenMinutesAfterStart) {
return res.json({
status: "error",
message: "Cannot delete session after 10 minutes from start"
});
}
try {
await ScheduledSession.findByIdAndDelete(scheduledSessionId);
await got
.post(`https://coord.graff.tech/end`, {
json: {
activeSessionId: scheduledSession.activeSessionId,
},
})
.json();
return res.json({ status: "success" });
} catch (error) {
return res.json({ status: "error", message: (error as Error).message });
}
}
try {
await ScheduledSession.findByIdAndDelete(scheduledSessionId);
return res.json({ status: "success" });
} catch (error) {
return res.json({ status: "error", message: (error as Error).message });
}
});
const scheduledSessionsRoute = router;
export default scheduledSessionsRoute;