283 lines
7.3 KiB
TypeScript
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;
|