2025-01-18 23:02:45 +03:00
|
|
|
|
const mongoose = require("mongoose")
|
|
|
|
|
const router = require('express').Router()
|
2025-02-22 16:58:58 +03:00
|
|
|
|
const multer = require('multer')
|
2025-01-18 23:02:45 +03:00
|
|
|
|
const { MasterModel } = require('./model/master')
|
|
|
|
|
const { OrderModel } = require('./model/order')
|
2025-02-22 16:58:58 +03:00
|
|
|
|
const { OrderCarImgModel } = require('./model/order.car-img')
|
2025-01-18 23:02:45 +03:00
|
|
|
|
const { orderStatus } = require('./model/const')
|
2025-03-03 19:49:11 +03:00
|
|
|
|
const { getGigaToken } = require('./get-token')
|
2025-01-18 23:02:45 +03:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2025-02-22 16:58:58 +03:00
|
|
|
|
const allowedMimeTypes = ['image/jpeg', 'image/png']
|
|
|
|
|
const sizeLimitInMegaBytes = 5
|
|
|
|
|
|
2025-01-18 23:02:45 +03:00
|
|
|
|
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'
|
|
|
|
|
},
|
2025-02-22 16:58:58 +03:00
|
|
|
|
carImg: {
|
|
|
|
|
required: 'Car image file is required',
|
|
|
|
|
invalid: {
|
|
|
|
|
type: `Invalid car image file type. Allowed types: ${allowedMimeTypes}`,
|
|
|
|
|
size: `Invalid car image file size. Limit is ${sizeLimitInMegaBytes}MB`
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-01-18 23:02:45 +03:00
|
|
|
|
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'
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
router.post('/create', async (req, res, next) => {
|
|
|
|
|
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(', '))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
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 })
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
next(error)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
router.get('/:id', async (req, res, next) => {
|
|
|
|
|
const { id } = req.params
|
|
|
|
|
if (!mongoose.Types.ObjectId.isValid(id)) {
|
|
|
|
|
throw new Error(VALIDATION_MESSAGES.orderId.invalid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const order = await OrderModel.findById(id)
|
|
|
|
|
if (!order) {
|
|
|
|
|
throw new Error(VALIDATION_MESSAGES.order.notFound)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.status(200).send({ success: true, body: order })
|
|
|
|
|
} catch (error) {
|
|
|
|
|
next(error)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
router.patch('/:id', async (req, res, next) => {
|
|
|
|
|
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 {
|
|
|
|
|
try {
|
|
|
|
|
const master = await MasterModel.findById(masterId)
|
|
|
|
|
if (!master) {
|
|
|
|
|
bodyErrors.push(VALIDATION_MESSAGES.master.notFound)
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
next(error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { notes } = req.body
|
|
|
|
|
if (notes) {
|
|
|
|
|
if (!isValidOrderNotes(notes)) {
|
|
|
|
|
bodyErrors.push(VALIDATION_MESSAGES.orderNotes.invalid)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bodyErrors.length > 0) {
|
|
|
|
|
throw new Error(bodyErrors.join(', '))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
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 })
|
|
|
|
|
} catch (error) {
|
|
|
|
|
next(error)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
router.delete('/:id', async (req, res, next) => {
|
|
|
|
|
const { id } = req.params
|
|
|
|
|
if (!mongoose.Types.ObjectId.isValid(id)) {
|
|
|
|
|
throw new Error(VALIDATION_MESSAGES.orderId.invalid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
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 })
|
|
|
|
|
} catch (error) {
|
|
|
|
|
next(error)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2025-02-22 16:58:58 +03:00
|
|
|
|
const storage = multer.memoryStorage()
|
|
|
|
|
const upload = multer({
|
|
|
|
|
storage: storage,
|
|
|
|
|
limits: { fileSize: sizeLimitInMegaBytes * 1024 * 1024 },
|
|
|
|
|
fileFilter: (req, file, cb) => {
|
|
|
|
|
if (allowedMimeTypes.includes(file.mimetype)) {
|
|
|
|
|
cb(null, true)
|
|
|
|
|
} else {
|
|
|
|
|
cb(new Error(VALIDATION_MESSAGES.carImg.invalid.type), false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2025-03-03 18:21:32 +03:00
|
|
|
|
const { v4: uuidv4 } = require("uuid")
|
|
|
|
|
const axios = require('axios')
|
|
|
|
|
|
2025-03-03 19:49:11 +03:00
|
|
|
|
const GIGA_CHAT_ACCESS_TOKEN = 'MTQwMmNmZjgtZjA5OC00OGMxLWI0OTUtNWU3ZTU4YzMzZjdjOmU5OGFiYmNiLThmMDItNGVmOC1hNjhhLTA4Y2QxYjVmOGRmMA=='
|
2025-03-03 18:21:32 +03:00
|
|
|
|
const GIGA_CHAT_OAUTH = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth'
|
|
|
|
|
const GIGA_CHAT_API = 'https://gigachat.devices.sberbank.ru/api/v1'
|
|
|
|
|
|
|
|
|
|
const getToken = async (req, res) => {
|
2025-03-03 19:49:11 +03:00
|
|
|
|
const gigaToken = await getGigaToken()
|
|
|
|
|
|
2025-03-03 18:21:32 +03:00
|
|
|
|
const rqUID = uuidv4()
|
|
|
|
|
const body = new URLSearchParams({
|
|
|
|
|
scope: "GIGACHAT_API_PERS",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const response = await fetch(GIGA_CHAT_OAUTH, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
2025-03-03 19:49:11 +03:00
|
|
|
|
Authorization: `Basic ${gigaToken}`,
|
2025-03-03 18:21:32 +03:00
|
|
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
|
|
|
Accept: "application/json",
|
|
|
|
|
RqUID: rqUID,
|
|
|
|
|
},
|
|
|
|
|
body,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const errorData = await response.json()
|
|
|
|
|
console.error("Ошибка запроса: ", errorData)
|
|
|
|
|
return res.status(response.status).json(errorData)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await response.json()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uploadImage = async (file, accessToken) => {
|
|
|
|
|
const formData = new FormData()
|
|
|
|
|
const blob = new Blob([file.buffer], { type: file.mimetype })
|
|
|
|
|
formData.append('file', blob, file.originalname)
|
|
|
|
|
formData.append('purpose', 'general')
|
|
|
|
|
|
|
|
|
|
const config = {
|
|
|
|
|
maxBodyLength: Infinity,
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'multipart/form-data',
|
|
|
|
|
'Accept': 'application/json',
|
|
|
|
|
'Authorization': `Bearer ${accessToken}`
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.post(`${GIGA_CHAT_API}/files`, formData, config)
|
|
|
|
|
return response.data.id
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const analyzeImage = async (fileId, token) => {
|
|
|
|
|
const response = await fetch(`${GIGA_CHAT_API}/chat/completions`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
Accept: "application/json",
|
|
|
|
|
Authorization: `Bearer ${token}`,
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
model: "GigaChat-Max",
|
|
|
|
|
stream: false,
|
|
|
|
|
update_interval: 0,
|
|
|
|
|
messages: [
|
|
|
|
|
{
|
2025-03-03 19:49:11 +03:00
|
|
|
|
role: "system",
|
2025-03-03 18:21:32 +03:00
|
|
|
|
content:
|
2025-03-03 19:49:11 +03:00
|
|
|
|
`Ты эксперт по оценке степени загрязнения автомобилей. Твоя задача — анализировать фотографии машин и определять степень их загрязнения.
|
|
|
|
|
ВАЖНО: Твой ответ ДОЛЖЕН быть СТРОГО в формате JSON и содержать ТОЛЬКО следующие поля:
|
|
|
|
|
{
|
|
|
|
|
"value": число от 0 до 10 (целое или с одним знаком после запятой),
|
|
|
|
|
"description": "текстовое описание на русском языке"
|
|
|
|
|
}
|
|
|
|
|
Правила:
|
|
|
|
|
1. Поле "value":
|
|
|
|
|
- Должно быть числом от 0 до 10
|
|
|
|
|
- 0 = машина абсолютно чистая
|
|
|
|
|
- 10 = машина максимально грязная
|
|
|
|
|
2. Поле "description":
|
|
|
|
|
- Должно содержать 2-3 предложения на русском языке
|
|
|
|
|
- Обязательно указать конкретные признаки загрязнения
|
|
|
|
|
- Объяснить почему выставлен именно такой балл
|
|
|
|
|
НЕ ДОБАВЛЯЙ никаких дополнительных полей или комментариев вне JSON структуры.
|
|
|
|
|
НЕ ИСПОЛЬЗУЙ markdown форматирование.
|
|
|
|
|
ОТВЕТ ДОЛЖЕН БЫТЬ ВАЛИДНЫМ JSON.`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
role: "user",
|
|
|
|
|
content: 'Дай оценку для приложенного файла изображения согласно структуре, ответ должен быть на русском языке',
|
2025-03-03 18:21:32 +03:00
|
|
|
|
attachments: [fileId],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const data = await response.json()
|
2025-03-03 19:49:11 +03:00
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(data.choices[0].message.content)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error)
|
|
|
|
|
return { description: data.choices[0].message.content }
|
|
|
|
|
}
|
2025-03-03 18:21:32 +03:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-22 16:58:58 +03:00
|
|
|
|
const convertFileToBase64 = (file) => {
|
|
|
|
|
const base64Image = file.buffer.toString('base64')
|
|
|
|
|
return base64Image
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-03 19:49:11 +03:00
|
|
|
|
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
|
|
|
|
|
|
2025-02-22 16:58:58 +03:00
|
|
|
|
router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => {
|
|
|
|
|
const { id: orderId } = req.params
|
|
|
|
|
if (!mongoose.Types.ObjectId.isValid(orderId)) {
|
|
|
|
|
throw new Error(VALIDATION_MESSAGES.orderId.invalid)
|
|
|
|
|
}
|
|
|
|
|
const order = await OrderModel.findById(orderId)
|
|
|
|
|
if (!order) {
|
|
|
|
|
throw new Error(VALIDATION_MESSAGES.order.notFound)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!req.file) {
|
|
|
|
|
throw new Error(VALIDATION_MESSAGES.carImg.required)
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-03 19:49:11 +03:00
|
|
|
|
try {
|
|
|
|
|
const { access_token } = await getToken(req, res)
|
2025-03-03 18:21:32 +03:00
|
|
|
|
|
2025-03-03 19:49:11 +03:00
|
|
|
|
const fileId = await uploadImage(req.file, access_token)
|
|
|
|
|
const { value, description } = await analyzeImage(fileId, access_token) ?? {}
|
2025-03-03 18:21:32 +03:00
|
|
|
|
|
2025-03-03 19:49:11 +03:00
|
|
|
|
const orderCarImg = await OrderCarImgModel.create({
|
|
|
|
|
image: convertFileToBase64(req.file),
|
|
|
|
|
imageRating: value,
|
|
|
|
|
imageDescription: description,
|
|
|
|
|
orderId,
|
|
|
|
|
created: new Date().toISOString(),
|
|
|
|
|
})
|
2025-02-22 16:58:58 +03:00
|
|
|
|
|
2025-03-03 19:49:11 +03:00
|
|
|
|
res.status(200).send({ success: true, body: orderCarImg })
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error)
|
|
|
|
|
}
|
2025-02-22 16:58:58 +03:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
router.use((err, req, res, next) => {
|
|
|
|
|
if (err instanceof multer.MulterError) {
|
|
|
|
|
switch (err.message) {
|
|
|
|
|
case 'File too large':
|
|
|
|
|
return res.status(400).json({ success: false, error: VALIDATION_MESSAGES.carImg.invalid.size })
|
|
|
|
|
default:
|
|
|
|
|
return res.status(400).json({ success: false, error: err.message })
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-03 18:21:32 +03:00
|
|
|
|
|
2025-02-22 16:58:58 +03:00
|
|
|
|
throw new Error(err.message)
|
|
|
|
|
})
|
|
|
|
|
|
2025-01-18 23:02:45 +03:00
|
|
|
|
module.exports = router
|