import * as fs from 'fs'; import path from "path"; import https from 'https'; import {fileURLToPath} from "url"; import { nanoid } from 'nanoid' const imhdData = path.dirname(path.dirname(fileURLToPath(import.meta.url))) + '/dist/imhd'; export default class ImhdClient { constructor() { fs.mkdirSync(imhdData + '/icons', { recursive: true }); this.cache = {}; this.loadCache(); } getVehicleData(vehicleId) { if (this.cache.hasOwnProperty(vehicleId)) { return this.cache[vehicleId]; } else { return null; } } async search(phrase) { let resp = await fetch('https://imhd.sk/ba/api/sk/vyhladavanie?q=' + encodeURIComponent(phrase)); if (!resp.ok) { return null; } try { return resp.json(); } catch (e) { return null; } } async downloadVehicleIcon(vehicleId) { if (this.cache.hasOwnProperty(vehicleId)) { if (this.cache[vehicleId].type) return this.cache[vehicleId].url; } const randId = nanoid(5); const iconPath = imhdData + '/icons/' + randId + '.png'; if (fs.existsSync(iconPath)) { return null; } let results = await this.search(vehicleId); if (!results) { console.log('Search for vehicle unsuccessful:', vehicleId); return null; } results = results.results || []; for (let obj of results) { if ((typeof obj.img) !== 'string' || obj.img.length < 10) continue; const urlParts = obj.url.trim().split('/'); if (urlParts.length < 3) continue; if (urlParts[2] !== 'vozidlo') continue; if (!obj.name.endsWith('#' + vehicleId)) continue; let matches = obj.img.match(/.*src="([^"]*)".*/); if (!matches) continue; const url = 'https://imhd.sk' + matches[1]; const existing = Object.values(this.cache).find(el => el.url === url); if (!existing) { // TODO: Path traversal vulnerability here! await this.downloadFile(url, iconPath); fs.appendFile( imhdData + '/names.txt', vehicleId + '|' + obj.name + "\n", err => { err && console.error(err); } ); } this.cache[vehicleId] = { img: existing ? existing.img : randId, url: url, type: obj.class, info: obj.url, }; this.saveCache(); return true; } } async downloadFile(url, target) { return new Promise ((resolve, reject) => { const file = fs.createWriteStream(target); https.get(url, resp => { resp.pipe(file); file.on('finish', () => { file.close(); console.log('Downloaded', url); resolve(); }); }); }); } loadCache() { try { let cacheFile = fs.readFileSync(imhdData + '/iconsdb.json'); if (cacheFile) { let parsed = JSON.parse(cacheFile); if (parsed) { this.cache = parsed; } } } catch (e) { console.log('Cannot read iconsdb.json for ImhdClient', e); } } saveCache() { fs.writeFile(imhdData + '/iconsdb.json', JSON.stringify(this.cache, null, 2), err => { if (err) { console.log('Cannot save cache for ImhdClient', err); } }); } }