import { Router } from 'express'; import Jwt from 'jsonwebtoken'; import cors from 'cors'; import AvtocodProvider from '../data_providers/avtocod.js'; import Vin01Provider from '../data_providers/vin01.js'; import Constants from '../data_providers/constants.js'; import RsaProvider from '../data_providers/rsa.js'; import NomerogramProvider from '../data_providers/nomerogram.js'; import Utils from '../utils/utils.js'; import { ObjectId } from 'mongodb'; import DebugInfo from '../models/DebugInfo.js'; import Vehicle from '../models/vehicle.js'; import { TGProvider } from '../data_providers/tgprovider.js'; const router = Router(); const makeError = (error) => ({ success: false, error }); router.post('/check', async (req, res) => { const number = req.body.number.replace(/ /g, '').toUpperCase(); const { googleIdToken, forceUpdate, notes, events } = req.body; const { email } = req.user; console.log( `=== checking number: ${number} ====================================`, ); let collection = req.db.collection('vehicles'); let vehicles = await collection.find({ number }).toArray(); if (vehicles.length > 0 && !forceUpdate) { console.log('vehicle found in database'); let updatedDate = Date.now(); if (notes) { Vehicle.mergeNotes(vehicles[0], notes); } if (events) { Vehicle.mergeEvents(vehicles[0], events); } await collection.updateOne( { number }, { $set: { updatedDate, events: vehicles[0].events, notes: vehicles[0].notes, }, }, ); vehicles[0].updatedDate = updatedDate; res.send({ success: true, data: vehicles[0] }); } else { try { let autocodPromise = AvtocodProvider.getReport(number); let all = [autocodPromise]; if (googleIdToken) { let vin01Promise = Vin01Provider.getReport(number, googleIdToken); all.push(vin01Promise); } else { all.push( Promise.reject({ debugInfo: { vin01vin: DebugInfo.fromError('No auth token'), vin01history: DebugInfo.fromError('No auth token'), vin01base: DebugInfo.fromError('No auth token'), }, }), ); } all.push(NomerogramProvider.getGroups(number)); let [autocod, vin01, nomerogram] = await Promise.allSettled(all); let vehicle = null; if (autocod.status == 'rejected' && vin01.status == 'rejected') { throw autocod.reason; } else if (vin01.status == 'rejected') { vehicle = autocod.value; Object.assign(vehicle.debugInfo, vin01.reason.debugInfo); } else if (autocod.status == 'rejected') { vehicle = vin01.value; Object.assign(vehicle.debugInfo, autocod.reason.debugInfo); if (!vehicle.brand?.name?.normalized) { throw autocod.reason; } } else { vehicle = autocod.value; let vinMatch = false; if (vin01.value.vin1 && vehicle.vin1) { vinMatch = vin01.value.vin1.match( RegExp(vehicle.vin1.replace(/\*/g, '.')), ); } if (vehicle.vin1 && vinMatch) { vehicle.vin1 = vin01.value.vin1; vehicle.vin2 = vin01.value.vin2; vehicle.color = vin01.value.color; vehicle.ownershipPeriods = vin01.value.ownershipPeriods; } else if (!vinMatch) { let vin01data = `${vin01.value.vin1} - ${vin01.value?.brand?.name?.original}`; let autocodData = `${autocod.value.vin1} - ${autocod.value.brand.name.original}`; let warning = `Vin01 VIN (${vin01data}) doesn't match with avtocod's one (${autocodData})`; vin01.value.debugInfo.vin01vin.setWarning(warning); //vin01.value.debugInfo.vin01history.setWarning(warning); //vin01.value.debugInfo.vin01base.setWarning(warning); } Object.assign(vehicle.debugInfo, vin01.value.debugInfo); } if (nomerogram.status == 'fulfilled') { let photos = nomerogram.value; vehicle.photos = vehicle.photos?.concat(photos) ?? photos; Object.assign(vehicle.debugInfo, { nomerogram: new DebugInfo(), }); } else { Object.assign(vehicle.debugInfo, nomerogram.reason.debugInfo); } vehicle.addedBy = email; // In case of force update of existing vehicle, transfer all events to the new DB record if (vehicles.length > 0) { vehicle.addedBy = vehicles[0].addedBy; vehicle.addedDate = vehicles[0].addedDate; vehicle.events = vehicles[0].events; vehicle.osagoContracts = vehicles[0].osagoContracts; vehicle.notes = vehicles[0].notes; } // Merge new notes/events with old ones if (notes) { vehicle.mergeNotes(notes); } if (events) { vehicle.mergeEvents(events); } await collection.replaceOne({ number }, vehicle, { upsert: true }); await Utils.vehicleRemoveEventEmails(vehicle, req); res.status(201).send({ success: true, data: vehicle }); } catch (ex) { res.send(makeError(ex.message)); console.error(ex); } } }); router.get('/', async (req, res) => { const { email } = req.user; const { pageToken, sortBy, sortOrder } = req.query; let pageSize = parseInt(req.query.pageSize); if (isNaN(pageSize)) { pageSize = 50; } try { let finalQuery = Utils.makeVehiclesSearchQuery(req.query, email); let collection = req.db.collection('vehicles'); let isAscending = sortOrder == 'ascending'; let response = {}; if (!pageToken) { response.count = await collection.countDocuments(finalQuery); } else { let compareOperator = isAscending ? '$gt' : '$lt'; let lastVehicle = await collection.findOne({ _id: new ObjectId(pageToken), }); finalQuery.$or = [ { [sortBy]: { [compareOperator]: lastVehicle[sortBy] } }, { [sortBy]: lastVehicle[sortBy], _id: { [compareOperator]: new ObjectId(pageToken) }, }, ]; } let vehicles = await collection .find(finalQuery) .sort({ [sortBy]: isAscending ? 1 : -1 }) .limit(pageSize) .toArray(); if (vehicles.length == pageSize) { response.pageToken = vehicles[vehicles.length - 1]._id; } response.items = await Utils.vehiclesRemoveEventEmails(vehicles, req); res.send({ success: true, data: response }); } catch (ex) { res.send(makeError('Error reading vehicles from DB')); console.error(ex); } }); router.get('/report', async (req, res) => { const { number } = req.query; try { let collection = req.db.collection('vehicles'); let vehicle = await collection.findOne({ number }); if (vehicle) { await Utils.vehicleRemoveEventEmails(vehicle, req); res.send({ success: true, data: vehicle }); } else { res.send(makeError('Vehicle not found')); } } catch (ex) { res.send(makeError(ex.message)); console.error(ex); } }); router.get('/brands', async (req, res) => { try { let collection = req.db.collection('vehicles'); let brands = await collection.distinct('brand.name.normalized'); res.send({ success: true, data: brands.filter(Boolean) }); } catch (ex) { res.send(makeError('Error reading vehicle brands from DB')); console.error(ex); } }); router.get('/models', async (req, res) => { try { const { brand } = req.query; let collection = req.db.collection('vehicles'); let models = await collection.distinct('model.name.normalized', { 'brand.name.normalized': brand, }); res.send({ success: true, data: models.filter(Boolean) }); } catch (ex) { res.send(makeError('Error reading vehicle models from DB')); console.error(ex); } }); router.get('/colors', async (req, res) => { try { let collection = req.db.collection('vehicles'); let colors = await collection.distinct('color'); res.send({ success: true, data: colors.filter(Boolean) }); } catch (ex) { res.send(makeError('Error reading vehicle colors from DB')); console.error(ex); } }); router.get('/regions', (req, res) => { res.send({ success: true, data: Constants.regions }); }); router.get('/years', async (req, res) => { try { let collection = req.db.collection('vehicles'); let colors = await collection.distinct('year'); res.send({ success: true, data: colors.filter(Boolean) }); } catch (ex) { res.send(makeError('Error reading vehicle years from DB')); console.error(ex); } }); router.get( '/shared_report', cors({ origin: 'https://auto.aliencat.pro' }), async (req, res) => { try { let { plateNumber } = Jwt.verify( req.query.token, process.env.JWT_SECRET_SHARED_REPORT, ); let collection = req.db.collection('vehicles'); let vehicles = await collection.find({ number: plateNumber }).toArray(); if (vehicles.length > 0) { let vehicle = await Utils.vehicleRemoveEventEmails(vehicles[0], req); res.send({ success: true, data: vehicle }); } else { res.send(makeError('Vehicle not found')); } } catch (ex) { res.send(makeError(ex.message)); console.error(ex); } }, ); router.post('/checkOsago', async (req, res) => { try { const { number, vin, date, token } = req.body; let result = await RsaProvider.checkOsago(number, vin, date, token); let collection = req.db.collection('vehicles'); let filter = number ? { number } : { vin1: vin }; await collection.updateOne(filter, { $push: { osagoContracts: result }, }); let vehicle = await collection.findOne(filter); if (vehicle) { await Utils.vehicleRemoveEventEmails(vehicle, req); res.send({ success: true, data: vehicle }); } else { res .status(204) .send(makeError('There is no vehicle with such plate number or VIN')); } } catch (ex) { res.send(makeError(ex.message)); console.error(ex); } }); router.post('/checkGbTg', async (req, res) => { let tgProvider = null; try { const number = req.body.number.replace(/ /g, '').toUpperCase(); const token = req.body.token; tgProvider = new TGProvider(); await tgProvider.init(); let report = await tgProvider.getReport(number, token); await tgProvider.close(); if (!report?.generalInfo && !report?.insurance) { res.send(makeError('No data found')); return; } let collection = req.db.collection('vehicles'); let vehicle = await collection.findOne({ number }); if (vehicle) { let vinRegex = RegExp(vehicle.vin1.replace(/\*/g, '.')); if (report?.generalInfo?.vin?.match(vinRegex)) { let updatedFields = { vin1: report.generalInfo.vin, updatedDate: Date.now(), }; if(report.generalInfo.color) { updatedFields.color = report.generalInfo.color; } if(report.generalInfo.sts) { updatedFields.sts = report.generalInfo.sts; } if (report.insurance) { let osagoFound = vehicle.osagoContracts?.some((elem) => { return (elem.number = report.insurance.number); }) ?? false; if (!osagoFound) { await collection.updateOne( { number }, { $push: { osagoContracts: report.insurance } }, ); } } if (report.ownershipPeriods) { updatedFields.ownershipPeriods = report.ownershipPeriods; } await collection.updateOne({ number }, { $set: updatedFields }); vehicle = await collection.findOne({ number }); await Utils.vehicleRemoveEventEmails(vehicle, req); res.send({ success: true, data: Object.assign(vehicle, updatedFields), }); } else { res.send(makeError('Vehicle doesn\'t match')); } } else { res.send(makeError('Vehicle not found')); } } catch (ex) { res.send(makeError(ex.message)); console.error(ex); await tgProvider.close(); } }); export default router;