import AbortController from 'abort-controller'; import DebugInfo from '../models/DebugInfo.js'; import Vehicle from '../models/vehicle.js'; import Utils from '../utils/utils.js'; const baseUrl = 'https://vin01.ru/v2'; const reportBaseUrl = `${baseUrl}/gibddApp.php`; async function fetchWithTimeout(url, params, timeout) { const controller = new AbortController(); const timeoutId = setTimeout(() => { controller.abort(); }, timeout); let requestParams = params ?? {}; requestParams.signal = controller.signal; try { return await fetch(url, requestParams); } finally { clearInterval(timeoutId); } } class Vin01Provider { static async getVin(number, token) { let url = `${baseUrl}/getVin.php?key=${token}&gosNumber=${encodeURIComponent(number)}`; let result = await fetchWithTimeout(url, null, 14000); let json = await result.json(); if(json.success && json.code == 200) { return json.data.vin; } else { throw Error('Vin01 provider failed to get VIN'); } } static async runCheck(type, vin, token) { let result = await fetchWithTimeout(reportBaseUrl, { method: 'POST', body: new URLSearchParams({ typeCheckValue: type, vinValue: vin, key: token, token: null }) }, 25000); console.log('Vin01 response for: ', type); return await result.json(); } static checkErrors(result) { if(result.status == 'fulfilled') { let json = result.value; if(json.status == 200) { if('status' in json.data) { if(json.data.status == 200) { result.value = json.data; return result; } else { return { status: 'rejected', reason: Error(`[${json.data.status}] ${json.data.message}`) }; } } else { result.value = json.data; return result; } } else { return { status: 'rejected', reason: Error(`[${json.status}] ${json.code}`) }; } } else { return result; } } static async getReport(number, token) { try { let vin = await Vin01Provider.getVin(number, token); console.log('vin01 found VIN: ', vin); return await this.getReportVin(vin, number, token); } catch(ex) { ex.debugInfo = { vin01vin:DebugInfo.fromError(ex.message), vin01history: DebugInfo.fromError('Not applicable (VIN was not found)'), vin01base: DebugInfo.fromError('Not applicable (VIN was not found)') }; throw ex; } } static async getReportVin(vin, number, token) { let checks = [Vin01Provider.runCheck('base', vin, token), Vin01Provider.runCheck('history', vin, token)]; let [base, history] = (await Promise.allSettled(checks)).map(Vin01Provider.checkErrors); if(base.status == 'rejected' && history.status == 'rejected') { console.log('Vin01 base error: ', base.reason.message); console.log('Vin01 history error: ', history.reason.message); let vehicle = new Vehicle(); vehicle.vin1 = Utils.cyrillicToLatin(vin); vehicle.debugInfo = { vin01vin: new DebugInfo(), vin01history: DebugInfo.fromError(history.reason.message), vin01base: DebugInfo.fromError(base.reason.message) }; return vehicle; } else if(base.status == 'rejected') { console.log('vin01 found history'); let vehicle = Vehicle.fromVin01History(history.value); vehicle.number = number; Object.assign(vehicle.debugInfo, { vin01vin: new DebugInfo(), vin01base: DebugInfo.fromError(base.reason.message) }); return vehicle; } else if(history.status == 'rejected') { console.log('vin01 found base info'); let vehicle = Vehicle.fromVin01Base(base.value); vehicle.number = number; Object.assign(vehicle.debugInfo, { vin01vin: new DebugInfo(), vin01history: DebugInfo.fromError(history.reason.message) }); return vehicle; } else { // Both history and base reports were successfully received, merge them in one report let baseVehicle = Vehicle.fromVin01Base(base.value); let historyVehicle = Vehicle.fromVin01History(history.value); historyVehicle.brand.name.normalized = baseVehicle.brand.name.normalized; historyVehicle.model = baseVehicle.model; for(let period of historyVehicle.ownershipPeriods) { let basePeriod = baseVehicle.ownershipPeriods.find(p => p.from == period.from); if(basePeriod) { period.region = basePeriod.region; period.registrationRegion = basePeriod.registrationRegion; period.locality = basePeriod.locality; if(basePeriod.street) { period.street = basePeriod.street; } if(basePeriod.building) { period.building = basePeriod.building; } if(basePeriod.inn) { period.inn = basePeriod.inn; } if(basePeriod.code) { period.code = basePeriod.code; } } } Object.assign(historyVehicle.debugInfo, { vin01vin: new DebugInfo() }); Object.assign(historyVehicle.debugInfo, baseVehicle.debugInfo); historyVehicle.number = number; return historyVehicle; } } } export default Vin01Provider;