Untitled
unknown
javascript
a year ago
21 kB
4
Indexable
// session.ts import { NextFunction, Response } from "express"; import { v4 as uuid } from "uuid"; import redis from "../controllers/redis"; import { Req } from "../utils/types"; export default async function session(req: Req, res: Response, next: NextFunction) { try { const sessionToken = req?.cookies?.[process.env.SESSION_COOKIE_KEY!]; if (sessionToken) { req.session = sessionToken; return next(); } const token = uuid(); res.cookie(process.env.SESSION_COOKIE_KEY!, token, { httpOnly: true, secure: process.env.MODE === "development" ? false : true, sameSite: "strict", priority: "high", expires: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000) // domain: process.env.OWN_DOMAIN!, }); let sessions = await redis.getValue("sessions"); if (!sessions) sessions = [token]; else sessions = [...JSON.parse(sessions), token]; await redis.setValue("sessions", JSON.stringify(sessions)); req.session = token; return next(); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } } // booking.ts import { Response } from "express"; import { v4 as uuid } from "uuid"; import redis from "../../controllers/redis"; import { find, findOne, remove, update } from "../../db/operations"; import calculatePrice from "../../utils/calculatePrice"; import secondsToDhms from "../../utils/secondsToDhms"; import { BookingPayload, Req } from "../../utils/types"; import Room from "../room/model"; import Booking from "./model"; const CREATE_ALLOWED = new Set([ "name", "phone", "email", "numberOfPerson", "totalPrice", "paid", "startDate", "endDate", "note", "rooms", "status", "trackingId" ]); const ALLOWED_QUERY = new Set([ "name", "phone", "email", "startDate", "endDate", "status", "page", "limit", "paginate", "sortBy", "trackingId", "$in" ]); const populate = { path: "rooms approvedBy", populate: { path: "room", select: "name" } }; export const createBooking = async (req: Req, res: Response) => { try { const isValid = Object.keys(req.body).every(k => CREATE_ALLOWED.has(k)); if (!isValid) return res.status(400).send({ status: 400, messae: "Invalid parameters provided" }); req.body.trackingId = uuid(); let data = req.body as BookingPayload; const { startDate, endDate, rooms } = data; if (!rooms || rooms.length === 0) return res.status(400).send({ status: 400, message: "No Rooms Selected" }); const start = new Date(startDate); const end = new Date(endDate); const today = new Date().setHours(0, 0, 0, 0); if (isNaN(start.getTime()) || isNaN(end.getTime())) { return res.status(400).send({ status: 400, message: "Invalid date format" }); } if (start.getTime() < today) { return res.status(400).send({ message: "Start date cannot be in the past date" }); } if (end < start) { return res.status(400).send({ message: "End date cannot be before start date" }); } const overlappingBooking = await Booking.find({ "rooms.room": { $in: rooms.map(room => room.room) }, startDate: { $eq: new Date(startDate).toISOString().split("T")[0] + "T00:00:00.000Z" } }).populate({ path: "rooms", populate: { path: "room", select: "name" } }); if (overlappingBooking.length > 0) { let unavailableRooms = overlappingBooking.flatMap(booking => booking.rooms).map(room => room); return res.status(400).send({ status: 400, message: "Some rooms are not available in the given date range", unavailableRooms }); } const roomDocs = await find({ table: Room, key: { query: { $in: rooms.map(room => room.room), paginate: "false" }, allowedQuery: ALLOWED_QUERY } }); data.totalPrice = calculatePrice(roomDocs, data.rooms); data.numberOfPerson = data.rooms.reduce((total, room) => room.guests + total, 0); data.startDate = new Date(new Date(data.startDate).setUTCHours(0, 0, 0, 0)).toISOString(); data.endDate = new Date(new Date(data.endDate).setUTCHours(0, 0, 0, 0)).toISOString(); const booking = new Booking(data); await booking.save(); await booking.populate({ path: "rooms", populate: { path: "room" } }); let holded = await redis.getValue(req.session!); holded = JSON.parse(holded); if (holded) { holded = holded.filter((h: string) => !rooms.map(r => r.room).includes(h)); await redis.setValue(req.session!, JSON.stringify(holded)); await Promise.all(rooms.map(async room => await redis.removeValue(room.room))); } if (!booking) return res.status(503).send({ status: 503, message: "Failed to save booking, please try again later" }); return res.status(201).send({ status: 201, message: "Booking created successfully", data: { booking, trackingUrl: `${process.env.TRACKING_URL!}?trackingId=${booking.trackingId}` } }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const updateBooking = async (req: Req, res: Response) => { try { console.log(req.body); const isValid = Object.keys(req.body).every(k => CREATE_ALLOWED.has(k)); if (!isValid) return res.status(400).send({ status: 400, message: "Invalid Parameters provided" }); const { id } = req.params; if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" }); let booking = await findOne({ table: Booking, key: { id } }); if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" }); req.body.approvedBy = req?.user?.id; booking = await update({ table: Booking, key: { id, body: req.body, populate } }); if (!booking) return res.status(503).send({ status: 503, message: "Failed to update booking,please try again later." }); return res.status(200).send({ status: 200, message: "Booking updated successfully", data: booking }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const getOneBooking = async (req: Req, res: Response) => { try { const { id } = req.params; if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" }); const booking = await findOne({ table: Booking, key: { id, populate } }); if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" }); return res.status(200).send({ status: 200, message: "Booking retrieved successfully", data: booking }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const getAllBookings = async (req: Req, res: Response) => { try { const isValidQuery = Object.keys(req.query).every(k => ALLOWED_QUERY.has(k)); if (!isValidQuery) return res.status(400).send({ status: 400, message: "Query Validation Failed" }); const bookings = await find({ table: Booking, key: { query: req.query, allowedQuery: ALLOWED_QUERY, populate } }); return res.status(200).send({ status: 200, message: "Bookings retrieved successfully", data: bookings }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const deleteBooking = async (req: Req, res: Response) => { try { const { id } = req.params; if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" }); const booking = await findOne({ table: Booking, key: { id } }); if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" }); await remove({ table: Booking, key: { id } }); return res.status(200).send({ status: 200, message: "Booking deleted successfully", data: booking }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const trackBooking = async (req: Req, res: Response) => { try { const { trackingId } = req.params; if (!trackingId || trackingId === "") return res.status(400).send({ status: 400, message: "Tracking Id is required" }); const booking = await findOne({ table: Booking, key: { trackingId, populate } }); if (!booking) return res.status(404).send({ status: 404, message: "Booking not found" }); return res.status(200).send({ status: 200, message: "Booking retrieved successfully", data: booking }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const tempHold = async (req: Req, res: Response) => { try { if (!req?.body?.date) return res.status(400).send({ status: 400, message: "Date is required" }); const key = req.session; if (Object.keys(req.body).length === 0) return res.status(400).send({ status: 400, message: "Body is empty" }); let rooms = req.body.rooms; if (rooms.length === 0) return res.status(400).send({ status: 400, message: "No room id provided" }); const dateKey = new Date(new Date(req.body.date as string)).toISOString().split("T")[0] + "T00:00:00.000Z"; let roomsByDate = await redis.getValue(dateKey); if (roomsByDate && JSON.parse(roomsByDate).length > 0) { roomsByDate = Array.from(new Set([...JSON.parse(roomsByDate), ...rooms])); await redis.setValue(dateKey, JSON.stringify(roomsByDate), 24 * 60 * 60); } else await redis.setValue(dateKey, JSON.stringify(rooms), 24 * 60 * 60); let holded = await redis.getValue(key!); if (holded && JSON.parse(holded).length > 0) { holded = JSON.parse(holded); holded.forEach(async (h: string, index: number) => { if (rooms.includes(h)) { await redis.removeValue(h); rooms = []; holded.splice(index, 1); } }); if (rooms.length === 0 && holded.length === 0) return res.status(200).send({ status: 200, message: "Holded room updated successfully", data: [] }); const holdExpirations = await Promise.all( holded.map(async (hold: string) => ({ id: hold, expiration: await redis.getExpirationTime(hold) })) ); holded = holdExpirations.filter(h => h.expiration > 0).map(h => h.id); holded = Array.from(new Set([...holded, ...rooms])); } else holded = [...rooms]; await redis.setValue(key!, JSON.stringify(holded)); await Promise.all(rooms.map(async (room: string) => await redis.setValue(room, room, 15 * 60))); const holdedWithTime = await Promise.all( holded.map(async (hold: string) => ({ id: hold, time: secondsToDhms(await redis.getExpirationTime(hold)) })) ); return res.status(200).send({ status: 200, message: "Holded room updated successfully", data: holdedWithTime }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const getHoldRooms = async (req: Req, res: Response) => { try { if (!req.query.date) return res.status(400).send({ status: 400, message: "Date is required" }); const session = req.session; const dateKey = new Date(new Date(req.query.date as string)).toISOString().split("T")[0] + "T00:00:00.000Z"; let roomsByDate = await redis.getValue(dateKey); if (roomsByDate) roomsByDate = JSON.parse(roomsByDate); else roomsByDate = []; const allSessions = JSON.parse(await redis.getValue("sessions")); let holds = await Promise.all(allSessions.map(async (s: string) => await redis.getValue(s))); holds = holds.filter(h => h !== null); holds = holds.map(h => JSON.parse(h)).flat(); holds = Array.from(new Set([...holds.map(h => h)])); const holdExpirations = await Promise.all( holds.map(async (hold: string) => ({ id: hold, expiration: await redis.getExpirationTime(hold) })) ); holds = holdExpirations.filter(h => h.expiration > 0).map(h => h.id); holds = holds.filter(h => roomsByDate.includes(h)); let ownHold = await redis.getValue(session!); if (ownHold) { ownHold = JSON.parse(ownHold); ownHold = ownHold.filter((hold: string) => new Set(holds).has(hold)); ownHold = await Promise.all(ownHold.map(async (h: string) => ({ id: h, time: secondsToDhms(await redis.getExpirationTime(h)) }))); ownHold = ownHold.filter((hold: any) => roomsByDate.includes(hold.id)); } else ownHold = []; return res.status(200).send({ status: 200, message: "Holds data retrieved", data: { totalHold: holds, holdByCurrentUser: ownHold } }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; // room.ts import { Response } from "express"; import cloudy from "../../controllers/cloudinary"; import { create, find, findOne, remove, update } from "../../db/operations"; import { Req } from "../../utils/types"; import Booking from "../booking/model"; import Room from "./model"; const CREATE_ALLOWED = new Set(["name", "description", "price", "pricePerPerson", "maxCapacity", "images", "videos", "discount", "status"]); const ALLOWED_QUERY = new Set(["name", "limit", "page", "paginate", "sort", "sortBy"]); export const createRoom = async (req: Req, res: Response) => { try { let data = JSON.parse(req.body?.data || "{}"); if (typeof data === "string") data = JSON.parse(data); if (data?.imageType) delete data.imageType; if (Object.keys(data).length === 0 && !req?.files?.images) return res.status(400).send({ status: 400, message: "Body is empty" }); const isValid = Object.keys(data).every(k => CREATE_ALLOWED.has(k)); if (!isValid) return res.status(400).send({ status: 400, message: "Invalid Fields provided" }); if (req?.files?.images) { if (!(req.files.images instanceof Array)) req.files.images = [req.files.images]; if (req.files.images.length > 10) return res.status(400).send({ status: 400, message: "Don't provide more than 10 files at a time" }); const imgRes = await cloudy.uploadImages(req.files.images.map((img: any) => img.path)); data.images = imgRes.map(img => img.secure_url).filter(Boolean); } const room = await create({ table: Room, key: { ...data } }); if (!room) return res.status(503).send({ status: 503, message: "Failed to create room,try again later" }); return res.status(200).send({ status: 200, message: "Room created successfully", data: room }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const updateRoom = async (req: Req, res: Response) => { try { const id = req.params.id; if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" }); let data = JSON.parse(req.body?.data || "{}"); if (typeof data === "string") data = JSON.parse(data); if (data?.imageType) delete data.imageType; if (Object.keys(data).length === 0 && !req?.files?.images) return res.status(400).send({ status: 400, message: "Body is empty" }); const isValid = Object.keys(data).every(k => CREATE_ALLOWED.has(k)); if (!isValid) return res.status(400).send({ status: 400, message: "Invalid Fields provided" }); let room = await findOne({ table: Room, key: { id } }); if (!room) return res.status(404).send({ status: 404, message: "Room not found" }); if (data?.images && room?.images) { const existingImages = room.images.filter((img: string) => img.includes("cloudinary.com")); const filteredImages = data?.images.filter((img: string) => img.includes("cloudinary.com")); const imagesToDelete = existingImages.filter((img: string) => !filteredImages.includes(img)); if (imagesToDelete.length > 0) { await cloudy.deleteImages(imagesToDelete); } } if (req?.files?.images) { if (!(req.files.images instanceof Array)) req.files.images = [req.files.images]; if (req.files.images.length > 10) return res.status(400).send({ status: 400, message: "Don't provide more than 10 files at a time" }); const imgRes = await cloudy.uploadImages(req.files.images.map((img: any) => img.path)); const imgUrls = imgRes.map(img => img.secure_url).filter(Boolean); if (data?.images) data.images = [...data.images, ...imgUrls]; else data.images = imgUrls; } room = await update({ table: Room, key: { id, body: data } }); if (!room) return res.status(503).send({ status: 503, message: "Failed to update room,try again later" }); return res.status(200).send({ status: 200, message: "Room updated successfully", data: room }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const getAllRooms = async (req: Req, res: Response) => { try { const isValidQuery = Object.keys(req.query).every(k => ALLOWED_QUERY.has(k)); if (!isValidQuery) return res.status(400).send({ status: 400, message: "Invalid query parameter" }); const rooms = await find({ table: Room, key: { query: req.query, allowedQuery: ALLOWED_QUERY } }); return res.status(200).send({ status: 200, message: "Rooms retrieved successfully", data: rooms }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const getOneRoom = async (req: Req, res: Response) => { try { const { id } = req.params; if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" }); const room = await findOne({ table: Room, key: { id } }); if (!room) return res.status(404).send({ status: 404, message: "Room not found" }); return res.status(200).send({ status: 200, message: "Room retrieved successfully", data: room }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const deleteRoom = async (req: Req, res: Response) => { try { const { id } = req.params; if (id === "undefined" || !id || id.trim().length === 0) return res.status(400).send({ status: 400, message: "ID is required" }); const room = await findOne({ table: Room, key: { id } }); if (!room) return res.status(404).send({ status: 404, message: "Room not found" }); await remove({ table: Room, key: { id } }); return res.status(200).send({ status: 200, message: "Room deleted successfully", data: room }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } }; export const getAllRoomByDate = async (req: Req, res: Response) => { try { const { startDate, endDate } = req.query; const today = new Date().setHours(0, 0, 0, 0); let start = startDate ? new Date(startDate as string).setHours(0, 0, 0, 0) : today; if (start < today || isNaN(start)) return res.status(400).send({ status: 400, message: "Invalid Start Date" }); let end = endDate ? new Date(endDate as string).setHours(0, 0, 0, 0) : start; if (end < start || isNaN(end)) return res.status(400).send({ status: 400, message: "Invalid End Date" }); const bookings = await Booking.find({ startDate: { $eq: new Date(startDate as string).toISOString().split("T")[0] + "T00:00:00.000Z" } // endDate: { $gte: start } }) .select("rooms") .lean() .populate({ path: "rooms", populate: { path: "room", select: "name" } }) .lean(); const bookedRoomIds = bookings.flatMap(booking => booking.rooms.map(room => room?.room?.id.toString())); const availableRooms = await Room.find({ _id: { $nin: bookedRoomIds }, status: "active" }); return res.status(200).send({ status: 200, message: "Available Rooms retrieved successfully", data: availableRooms }); } catch (err) { console.error(err); return res.status(500).send({ status: 500, message: "Internal Server Error" }); } };
Editor is loading...
Leave a Comment