AutoCatBackend/routes/vehicles.js

391 lines
11 KiB
JavaScript

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 });
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 = vehicles;
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) {
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) {
res.send({ success: true, data: vehicles[0] });
} 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) {
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,
color: report.generalInfo.color,
sts: report.generalInfo.sts,
updatedDate: Date.now(),
};
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 });
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;