const mongoose = require("mongoose") const router = require('express').Router() const { MasterModel } = require('./model/master') const { OrderModel } = require('./model/order') const { orderStatus } = require('./model/const') const isValidPhoneNumber = (value) => /^(\+)?\d{9,15}/.test(value) const isValidCarNumber = (value) => /^[авекмнорстух][0-9]{3}[авекмнорстух]{2}[0-9]{2,3}$/i.test(value) const isValidCarBodyType = (value) => typeof value === 'number' && value > 0 && value < 100 const isValidCarColor = (value) => value.length < 50 && /^[#a-z0-9а-я-\s,.()]+$/i.test(value) const isValidISODate = (value) => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:.\d{1,3})?Z$/.test(value) const latitudeRe = /^(-?[1-8]?\d(?:\.\d{1,18})?|90(?:\.0{1,18})?)$/ const longitudeRe = /^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,18})?|180(?:\.0{1,18})?)$/ const addressRe = /^[а-я0-9\s,.'-/()]*$/i const isValidLocation = (value) => { if (value.length > 200) { return false } const [coordinates, address] = value.split(' ') const [latitude, longitude] = coordinates.split(',') return latitudeRe.test(latitude) && longitudeRe.test(longitude) && addressRe.test(address) } const isValidOrderStatus = (value) => Object.values(orderStatus).includes(value) const isValidOrderNotes = (value) => value.length < 500 const VALIDATION_MESSAGES = { order: { notFound: 'Order not found' }, orderId: { invalid: 'Valid order ID is required', }, orderStatus: { invalid: 'Invalid order status' }, orderNotes: { invalid: 'Invalid order notes' }, master: { notFound: 'Master not found' }, masterId: { invalid: 'Invalid master ID', }, phoneNumber: { required: 'Phone number is required', invalid: 'Invalid phone number' }, carNumber: { required: 'Car number is required', invalid: 'Invalid car number' }, carBody: { required: 'Car body type is required', invalid: 'Invalid car body type' }, carColor: { invalid: 'Invalid car color' }, washingBegin: { required: 'Begin time of washing is required', invalid: 'Invalid begin time of washing' }, washingEnd: { required: 'End time of washing is required', invalid: 'Invalid end time of washing' }, washingLocation: { required: 'Location of washing is required', invalid: 'Invalid location of washing' }, } const createOrder = async (req, res) => { const bodyErrors = [] const { customer } = req.body if (!customer.phone) { bodyErrors.push(VALIDATION_MESSAGES.phoneNumber.required) } else if (!isValidPhoneNumber(customer.phone)) { bodyErrors.push(VALIDATION_MESSAGES.phoneNumber.invalid) } const { car } = req.body if (!car.number) { bodyErrors.push(VALIDATION_MESSAGES.carNumber.required) } else if (!isValidCarNumber(car.number)) { bodyErrors.push(VALIDATION_MESSAGES.carNumber.invalid) } if (!car.body) { bodyErrors.push(VALIDATION_MESSAGES.carBody.required) } else if (!isValidCarBodyType(car.body)) { bodyErrors.push(VALIDATION_MESSAGES.carBody.invalid) } if (!isValidCarColor(car.color)) { bodyErrors.push(VALIDATION_MESSAGES.carColor.invalid) } const { washing } = req.body if (!washing.begin) { bodyErrors.push(VALIDATION_MESSAGES.washingBegin.required) } else if (!isValidISODate(washing.begin)) { bodyErrors.push(VALIDATION_MESSAGES.washingBegin.invalid) } if (!washing.end) { bodyErrors.push(VALIDATION_MESSAGES.washingEnd.required) } else if (!isValidISODate(washing.end)) { bodyErrors.push(VALIDATION_MESSAGES.washingEnd.invalid) } if (!washing.location) { bodyErrors.push(VALIDATION_MESSAGES.washingLocation.required) } else if (!isValidLocation(washing.location)) { bodyErrors.push(VALIDATION_MESSAGES.washingLocation.invalid) } if (bodyErrors.length > 0) { throw new Error(bodyErrors.join(', ')) } const order = await OrderModel.create({ phone: customer.phone, carNumber: car.number, carBody: car.body, carColor: car.color, startWashTime: washing.begin, endWashTime: washing.end, location: washing.location, status: orderStatus.PROGRESS, notes: '', created: new Date().toISOString(), }) res.status(200).send({ success: true, body: order }) } const getOrder = async (req, res) => { const { id } = req.params if (!mongoose.Types.ObjectId.isValid(id)) { throw new Error(VALIDATION_MESSAGES.orderId.invalid) } const order = await OrderModel.findById(id) if (!order) { throw new Error(VALIDATION_MESSAGES.order.notFound) } res.status(200).send({ success: true, body: order }) } const updateOrder = async (req, res) => { const { id } = req.params if (!mongoose.Types.ObjectId.isValid(id)) { throw new Error(VALIDATION_MESSAGES.orderId.invalid) } const bodyErrors = [] const { status } = req.body if (status) { if (!isValidOrderStatus(status)) { bodyErrors.push(VALIDATION_MESSAGES.orderStatus.invalid) } } const { master: masterId } = req.body if (masterId) { if (!mongoose.Types.ObjectId.isValid(masterId)) { bodyErrors.push(VALIDATION_MESSAGES.masterId.invalid) } else { const master = await MasterModel.findById(masterId) if (!master) { bodyErrors.push(VALIDATION_MESSAGES.master.notFound) } } } const { notes } = req.body if (notes) { if (!isValidOrderNotes(notes)) { bodyErrors.push(VALIDATION_MESSAGES.orderNotes.invalid) } } if (bodyErrors.length > 0) { throw new Error(bodyErrors.join(', ')) } const updateData = {} if (status) { updateData.status = status } if (masterId) { updateData.master = masterId } if (notes) { updateData.notes = notes } updateData.updated = new Date().toISOString() const order = await OrderModel.findByIdAndUpdate( id, updateData, { new: true } ) if (!order) { throw new Error(VALIDATION_MESSAGES.order.notFound) } res.status(200).send({ success: true, body: order }) } const deleteOrder = async (req, res) => { const { id } = req.params if (!mongoose.Types.ObjectId.isValid(id)) { throw new Error(VALIDATION_MESSAGES.orderId.invalid) } const order = await OrderModel.findByIdAndDelete(id, { new: true, }) if (!order) { throw new Error(VALIDATION_MESSAGES.order.notFound) } res.status(200).send({ success: true, body: order }) } router.post('/create', createOrder) router.get('/:id', getOrder) router.patch('/:id', updateOrder) router.delete('/:id', deleteOrder) module.exports = router