diff --git a/.env.example b/.env.example index 917e222..20ead78 100644 --- a/.env.example +++ b/.env.example @@ -10,10 +10,11 @@ PRIVATE_KEY_PATH = "/path/to/privkey.pem" # Database +DB_NAME = "" DB_USER_NAME = "" DB_USER_PASSWORD = "" DB_SERVER = "" -MONGO_CONNECTION_STRING = "mongodb+srv://$DB_USER_NAME:$DB_USER_PASSWORD@$DB_SERVER/autocatdev?retryWrites=true&w=majority" +MONGO_CONNECTION_STRING = "mongodb+srv://$DB_USER_NAME:$DB_USER_PASSWORD@$DB_SERVER" # JWT secrets diff --git a/.eslintrc.js b/.eslintrc.js index edb4c4e..b3d1e5a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,5 +25,8 @@ module.exports = { 'always' ], 'no-console': 0 + }, + 'parserOptions': { + 'sourceType': 'module' } }; diff --git a/.gitignore b/.gitignore index 9e2efae..15bb672 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +_td_database/ node_modules/ -.env \ No newline at end of file +.env diff --git a/data_providers/avtocod.js b/data_providers/avtocod.js index baa55fc..5be80ee 100644 --- a/data_providers/avtocod.js +++ b/data_providers/avtocod.js @@ -1,13 +1,13 @@ -const crypto = require('crypto'); -const Vehicle = require('../models/vehicle'); -const DebugInfo = require('../models/DebugInfo'); -const { Centrifuge } = require('centrifuge'); -const WebSocket = require('ws'); +import { createHash, createDecipheriv } from 'crypto'; +import Vehicle from '../models/vehicle.js'; +import DebugInfo from '../models/DebugInfo.js'; +import { Centrifuge } from 'centrifuge'; +import WebSocket from 'ws'; const baseUrl = 'https://avtocod.ru/api/v3'; const tokenRefreshUrl = 'https://avtocod.ru/api/centrifuge/refresh'; -let deviceToken = crypto.createHash('sha256').update(Date.now().toString()).digest().toString('hex'); +let deviceToken = createHash('sha256').update(Date.now().toString()).digest().toString('hex'); const myWs = function (options) { return class wsClass extends WebSocket { @@ -37,9 +37,9 @@ function decryptReport(report, hash) { let iv = Buffer.from(encryptedObject.iv, 'base64'); let data = Buffer.from(encryptedObject.value, 'base64'); - let key = crypto.createHash('sha256').update(hash).digest(); + let key = createHash('sha256').update(hash).digest(); - let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + let decipher = createDecipheriv('aes-256-cbc', key, iv); let decrypted = decipher.update(data); decrypted = Buffer.concat([decrypted, decipher.final()]); @@ -147,4 +147,4 @@ class AvtocodProvider { } } -module.exports = AvtocodProvider; \ No newline at end of file +export default AvtocodProvider; \ No newline at end of file diff --git a/data_providers/constants.js b/data_providers/constants.js index 1b22a8f..8af69eb 100644 --- a/data_providers/constants.js +++ b/data_providers/constants.js @@ -255,6 +255,6 @@ const regions = [ { name: 'Чеченская республика', codes: [20, 95]}, ]; -module.exports = { +export default { organs, ogr, typeAuto, typeOperation, regions }; \ No newline at end of file diff --git a/data_providers/nomerogram.js b/data_providers/nomerogram.js index e120aa7..85b5c35 100644 --- a/data_providers/nomerogram.js +++ b/data_providers/nomerogram.js @@ -1,7 +1,7 @@ -const crypto = require('crypto'); -const Utils = require('../utils/utils'); -const utf8 = require('utf8'); -const DebugInfo = require('../models/DebugInfo'); +import { createHash } from 'crypto'; +import Utils from '../utils/utils.js'; +import Utf8 from 'utf8'; +import DebugInfo from '../models/DebugInfo.js'; const baseUrl = 'https://www.nomerogram.ru/api/v1.1'; @@ -17,13 +17,13 @@ class NomerogramProvider { let timestamp = Math.floor(Date.now() / 1000); let secretSource = appId + number + deviceId + from + timestamp + secretSuffix; - let hash = crypto.createHash('sha256'); + let hash = createHash('sha256'); hash.update(secretSource); let secret = hash.digest('hex'); console.log('secret: ', secret); let url = `${baseUrl}/group/list?from=${from}&carplate=${number}×tamp=${timestamp}&secret=${secret}&app_id=${appId}&device_id=${deviceId}`; - let result = await fetch(utf8.encode(url), { + let result = await fetch(Utf8.encode(url), { timeout: 5000, headers: { 'User-Agent': userAgent } }); @@ -57,4 +57,4 @@ class NomerogramProvider { } } -module.exports = NomerogramProvider; \ No newline at end of file +export default NomerogramProvider; \ No newline at end of file diff --git a/data_providers/rsa.js b/data_providers/rsa.js index 2ebe197..88d9cfa 100644 --- a/data_providers/rsa.js +++ b/data_providers/rsa.js @@ -1,7 +1,7 @@ -const https = require('https'); -const parser = require('node-html-parser'); +import { Agent } from 'https'; +import { parse } from 'node-html-parser'; -const httpsAgent = new https.Agent({ +const httpsAgent = new Agent({ rejectUnauthorized: false }); @@ -50,7 +50,7 @@ class RsaProvider { } static parseReport(html) { - let root = parser.parse(html); + let root = parse(html); if(!root) { throw Error('Failed to parse dkbm response'); } @@ -91,4 +91,4 @@ class RsaProvider { } } -module.exports = RsaProvider; \ No newline at end of file +export default RsaProvider; \ No newline at end of file diff --git a/data_providers/tgclient.js b/data_providers/tgclient.js new file mode 100644 index 0000000..dae4db4 --- /dev/null +++ b/data_providers/tgclient.js @@ -0,0 +1,108 @@ +import tdl from 'tdl'; +import { platform } from 'node:process'; + +class TGClient { + + static { + let libdir = null; + if(platform == 'linux') { + libdir = './node_modules/prebuilt-tdlib/prebuilds/tdlib-linux-x64'; + } else if(platform == 'darwin') { + libdir = './node_modules/prebuilt-tdlib/prebuilds/tdlib-macos'; + } + + tdl.configure({ libdir }); + } + + constructor(apiId, apiHash) { + + this.client = tdl.createClient({ + apiId: apiId, + apiHash: apiHash + }); + + this.client.on('error', console.error); + + this.client.on('update', update => { + //console.log('update: ', update); + switch(update._) { + case 'updateMessageSendSucceeded': + this.sendTasks.get(update.old_message_id)?.resolve(update.message); + this.sendTasks.delete(update.old_message_id); + break; + case 'updateMessageSendFailed': + this.sendTasks.get(update.old_message_id)?.reject(Error(update.error_message)); + this.sendTasks.delete(update.old_message_id); + break; + case 'updateNewMessage': + this.newMessageCallback?.(update.message); + break; + } + }); + + this.sendTasks = new Map(); + } + + onNewMessage(callback) { + this.newMessageCallback = callback; + } + + waitAsync(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + async login() { + await this.client.login(); + } + + async close() { + await this.client.close(); + } + + async getMe() { + return await this.client.invoke({ _: 'getMe'}); + } + + async findChat(name) { + return await this.client.invoke({ + _: 'searchPublicChat', + username: name + }); + } + + async getSavedMessagesChat() { + let me = await this.getMe(); + return await this.client.invoke({ + _: 'createPrivateChat', + user_id: me.id + }); + } + + async sendMessage(chat, text) { + let message = await this.client.invoke({ + _: 'sendMessage', + chat_id: chat.id, + input_message_content: { + _: 'inputMessageText', + text: { + _: 'formattedText', + text + } + } + }); + + let sendStatusTask = Promise.withResolvers(); + this.sendTasks.set(message.id, sendStatusTask); + return await sendStatusTask.promise; + } + + async viewMessage(id, chatId) { + await this.client.invoke({ + _: 'viewMessages', + chat_id: chatId, + message_ids: [ id ] + }); + } +} + +export { TGClient }; \ No newline at end of file diff --git a/data_providers/tgprovider.js b/data_providers/tgprovider.js new file mode 100644 index 0000000..5f2b369 --- /dev/null +++ b/data_providers/tgprovider.js @@ -0,0 +1,80 @@ +import { TGClient } from './tgclient.js'; + +class TGProvider { + + constructor() { + this.tgClient = new TGClient(869171, 'c3731f831b212a314f71172daf233f9d'); + this.checksMap = new Map(); + + this.tgClient.onNewMessage(message => { + if(message.chat_id == this.chat.id) { + let messageText = message?.content?.text?.text; + if(messageText && messageText.includes('#️⃣')) { + let number = this.findFirst(messageText, /Номер: (.*)\n/); + if(number) { + this.checksMap.get(number).resolve({ + id: message.id, + text: messageText + }); + } + } + } + }); + + //this.init(); + } + + findFirst(string, regex) { + let result = string.match(regex); + if(result && result.length > 1) { + return result[1]; + } else { + return null; + } + } + + async init() { + await this.tgClient.login(); + this.chat = await this.tgClient.findChat('@l0r3m1psum_bot'); + } + + async close() { + await this.tgClient.close(); + } + + async getReport(number) { + try { + await this.tgClient.sendMessage(this.chat, number); + let waitResponseTask = Promise.withResolvers(); + this.checksMap.set(number, waitResponseTask); + setTimeout(() => { + waitResponseTask.reject(Error('TG report timeout')); + }, 10000); + let { id, text } = await waitResponseTask.promise; + await this.tgClient.viewMessage(id, this.chat.id); + return this.parseReport(text); + } finally { + this.checksMap.delete(number); + } + } + + parseReport(text) { + let number = this.findFirst(text, /Номер: (.*)\n/); + let vin = this.findFirst(text, /VIN: (.*)\n/); + let owner = this.findFirst(text, /Владелец: (.*)\n/); + let year = this.findFirst(text, /Год: (.*)\n/); + let color = this.findFirst(text, /Цвет: (.*)\n/); + let sts = this.findFirst(text, /СТС: (.*)\n/); + + return { + number, + vin, + owner, + year, + color, + sts + }; + } +} + +export { TGProvider }; \ No newline at end of file diff --git a/data_providers/vin01.js b/data_providers/vin01.js index cb51a49..f626b67 100644 --- a/data_providers/vin01.js +++ b/data_providers/vin01.js @@ -1,7 +1,7 @@ -const AbortController = require('abort-controller'); -const DebugInfo = require('../models/DebugInfo'); -const Vehicle = require('../models/vehicle'); -const Utils = require('../utils/utils'); +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`; @@ -148,7 +148,7 @@ class Vin01Provider { } } catch(ex) { ex.debugInfo = { - vin01vin: DebugInfo.fromError(ex.message), + vin01vin:DebugInfo.fromError(ex.message), vin01history: DebugInfo.fromError('Not applicable (VIN was not found)'), vin01base: DebugInfo.fromError('Not applicable (VIN was not found)') }; @@ -157,4 +157,4 @@ class Vin01Provider { } } -module.exports = Vin01Provider; \ No newline at end of file +export default Vin01Provider; \ No newline at end of file diff --git a/index.js b/index.js index e4e1560..19674fe 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,21 @@ -const express = require('express'); -const bodyParser = require('body-parser'); -const expressMongoDb = require('./middleware/mongo'); -const users = require('./routes/user'); -const vehicles = require('./routes/vehicles'); -const events = require('./routes/events'); -const notes = require('./routes/notes'); -const app = express(); -const bearerToken = require('express-bearer-token'); -const jwt = require('./middleware/jwt'); -const https = require('https'); -const fs = require('fs'); -const compression = require('compression'); -const dotenv = require('dotenv'); -const dotenvExpand = require('dotenv-expand'); -const responseTime = require('response-time'); +import express from 'express'; +import expressMongoDb from './middleware/mongo.js'; +import users from './routes/user.js'; +import vehicles from './routes/vehicles.js'; +import events from './routes/events.js'; +import notes from './routes/notes.js'; +import bearerToken from 'express-bearer-token'; +import jwt from './middleware/jwt.js'; +import { createServer } from 'https'; +import { readFileSync } from 'fs'; +import compression from 'compression'; +import { config } from 'dotenv'; +import dotenvExpand from 'dotenv-expand'; +import responseTime from 'response-time'; -let dotenvConf = dotenv.config(); +const app = express(); + +let dotenvConf = config(); dotenvExpand(dotenvConf); app.use(responseTime(function (req, res, time) { @@ -23,7 +23,7 @@ app.use(responseTime(function (req, res, time) { })); app.use(compression()); -app.use(bodyParser.json()); +app.use(express.json()); app.use(expressMongoDb(process.env.MONGO_CONNECTION_STRING)); app.use(bearerToken()); app.use(jwt({ secret: process.env.JWT_SECRET_AUTH, exclude: ['/user/signup', '/user/login', '/vehicles/shared_report'] })); @@ -39,9 +39,9 @@ app.use((req, res) => { }); if(process.env.NODE_ENV == 'production') { - const httpsServer = https.createServer({ - key: fs.readFileSync(process.env.PRIVATE_KEY_PATH), - cert: fs.readFileSync(process.env.CERT_PATH) + const httpsServer = createServer({ + key: readFileSync(process.env.PRIVATE_KEY_PATH), + cert: readFileSync(process.env.CERT_PATH) }, app); httpsServer.listen(8443); } else { diff --git a/middleware/jwt.js b/middleware/jwt.js index 887bbbf..8248ed5 100644 --- a/middleware/jwt.js +++ b/middleware/jwt.js @@ -1,6 +1,6 @@ -const jsonwebtoken = require('jsonwebtoken'); +import Jwt from 'jsonwebtoken'; -module.exports = function (options) { +export default function (options) { return function jwt(req, res, next) { if('exclude' in options && options.exclude.includes(req.path)) { next(); @@ -13,7 +13,7 @@ module.exports = function (options) { let scheme = parts[0]; let token = parts[1]; if (/^Bearer$/i.test(scheme)) { - jsonwebtoken.verify(token, options.secret, (error, decoded) => { + Jwt.verify(token, options.secret, (error, decoded) => { if(error) { res.status(401).send({ success: false, error: error.message }); console.error(error); @@ -35,4 +35,4 @@ module.exports = function (options) { console.error('Missing authorization header'); } }; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/middleware/mongo.js b/middleware/mongo.js index 5e6c78d..9c23302 100644 --- a/middleware/mongo.js +++ b/middleware/mongo.js @@ -1,6 +1,6 @@ -let MongoClient = require('mongodb').MongoClient; +import { MongoClient } from 'mongodb'; -module.exports = function (uri) { +export default function expressMongoDb(uri) { if (typeof uri !== 'string') { throw new TypeError('Expected uri to be a string'); } @@ -14,7 +14,7 @@ module.exports = function (uri) { connection .then(function (client) { - req['db'] = client.db('autocat'); + req['db'] = client.db(process.env.DB_NAME); next(); }) .catch(function (err) { @@ -22,4 +22,4 @@ module.exports = function (uri) { next(err); }); }; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/models/DebugInfo.js b/models/DebugInfo.js index e188fc8..4952fc0 100644 --- a/models/DebugInfo.js +++ b/models/DebugInfo.js @@ -1,7 +1,7 @@ class DebugInfo { - status - fields - error + status; + fields; + error; constructor() { this.status = 0; @@ -29,4 +29,4 @@ class DebugInfo { } } -module.exports = DebugInfo; \ No newline at end of file +export default DebugInfo; \ No newline at end of file diff --git a/models/engine.js b/models/engine.js index 99f13be..43d53bf 100644 --- a/models/engine.js +++ b/models/engine.js @@ -1,9 +1,9 @@ class Engine { - number - volume - type - power - fuelType + number; + volume; + type; + power; + fuelType; } -module.exports = Engine; \ No newline at end of file +export default Engine; \ No newline at end of file diff --git a/models/event.js b/models/event.js index 4b2f582..fae7ff4 100644 --- a/models/event.js +++ b/models/event.js @@ -1,11 +1,11 @@ class Event { - id - date - latitude - longitude - speed - direction - address + id; + date; + latitude; + longitude; + speed; + direction; + address; } -module.exports = Event; \ No newline at end of file +export default Event; \ No newline at end of file diff --git a/models/user.js b/models/user.js index 3b15553..586bf0d 100644 --- a/models/user.js +++ b/models/user.js @@ -1,8 +1,8 @@ -const crypto = require('crypto'); -const { v4: uuidv4 } = require('uuid'); +import { createHash } from 'crypto'; +import { v4 as uuidv4 } from 'uuid'; const hash = Symbol(); -const sha256 = text => crypto.createHash('sha256').update(text).digest('base64'); +const sha256 = text => createHash('sha256').update(text).digest('base64'); class User { constructor(email = '', password = '') { @@ -33,4 +33,4 @@ class User { } } -module.exports = User; \ No newline at end of file +export default User; \ No newline at end of file diff --git a/models/vehicle.js b/models/vehicle.js index b24d3b7..ae45659 100644 --- a/models/vehicle.js +++ b/models/vehicle.js @@ -1,28 +1,28 @@ -const Constants = require('../data_providers/constants'); -const Utils = require('../utils/utils'); -const DebugInfo = require('./DebugInfo'); +import Constants from '../data_providers/constants.js'; +import Utils from '../utils/utils.js'; +import DebugInfo from './DebugInfo.js'; class Vehicle { - brand - model - color - year - category - engine - number - currentNumber - vin1 - vin2 - sts - pts - isRightWheel - isJapanese - photos - addedDate - addedBy - ownershipPeriods - events - notes + brand; + model; + color; + year; + category; + engine; + number; + currentNumber; + vin1; + vin2; + sts; + pts; + isRightWheel; + isJapanese; + photos; + addedDate; + addedBy; + ownershipPeriods; + events; + notes; static fromAvtocod(report) { //console.log(JSON.stringify(report)); @@ -197,4 +197,4 @@ class Vehicle { } } -module.exports = Vehicle; +export default Vehicle; diff --git a/package-lock.json b/package-lock.json index f26d3f5..292f806 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", - "body-parser": "^1.19.0", "centrifuge": "^3.1.0", "compress": "^0.99.0", "compression": "^1.7.4", @@ -21,8 +20,10 @@ "express-bearer-token": "^2.4.0", "jsonwebtoken": "^9.0.2", "mongodb": "^6.3.0", - "node-html-parser": "^2.0.0", + "node-html-parser": "^6.0.0", + "prebuilt-tdlib": "^0.1008026.0", "response-time": "^2.3.2", + "tdl": "^7.4.1", "utf8": "^3.0.0", "uuid": "^8.3.0", "ws": "^8.12.0" @@ -405,28 +406,10 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -673,6 +656,32 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -732,6 +741,57 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", @@ -766,6 +826,17 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -983,6 +1054,11 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -1802,14 +1878,41 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" + }, + "node_modules/node-gyp-build": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-html-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-2.2.1.tgz", - "integrity": "sha512-Vccqb62t6t7DkMVwqPQgb0NWO+gUMMDm+1X3LzqbtXLqjilCTtUYTlniKk08yuA1zIhEFVzu/dozpqs5KZbRFQ==", + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.12.tgz", + "integrity": "sha512-/bT/Ncmv+fbMGX96XG9g05vFt43m/+SYKIs9oAemQVYyVcZmDAI2Xq/SbNcpOA35eF0Zk2av3Ksf+Xk8Vt8abA==", "dependencies": { + "css-select": "^5.1.0", "he": "1.2.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1953,6 +2056,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/prebuilt-tdlib": { + "version": "0.1008026.0", + "resolved": "https://registry.npmjs.org/prebuilt-tdlib/-/prebuilt-tdlib-0.1008026.0.tgz", + "integrity": "sha512-LLlaWPgvTttArop9FnFbjZ+fi5+s7TNiEyil7bTXAdgSBpNvzT1eboNA2Qcej6B9fglPewhtjyu8m91jky564Q==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2049,20 +2157,6 @@ "node": ">= 0.6" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2317,6 +2411,39 @@ "node": ">=8" } }, + "node_modules/tdl": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/tdl/-/tdl-7.4.1.tgz", + "integrity": "sha512-7EkVpgAndBAd+jMeRR+FI3v/Y0iy0bmcTDNACqp9Dtg+4AWdLb7fdjbMCEOuL2+ezjoslInXKx9SNWJ1vBT11A==", + "hasInstallScript": true, + "dependencies": { + "debug": "^4.3.4", + "eventemitter3": "^4.0.7", + "node-addon-api": "^6.0.0", + "node-gyp-build": "^4.6.0" + } + }, + "node_modules/tdl/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/tdl/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index 3ab2231..66ea2d2 100644 --- a/package.json +++ b/package.json @@ -3,16 +3,16 @@ "version": "1.0.0", "description": "AutoCat app backend", "main": "index.js", + "type": "module", "scripts": { - "server": "node --async-stack-traces --trace-warnings index.js", - "migration": "node --async-stack-traces --trace-warnings migration.js", + "server": "node --js-promise-withresolvers --async-stack-traces --trace-warnings index.js", + "migration": "node --js-promise-withresolvers --async-stack-traces --trace-warnings migration.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Selim Mustafaev", "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", - "body-parser": "^1.19.0", "centrifuge": "^3.1.0", "compress": "^0.99.0", "compression": "^1.7.4", @@ -23,8 +23,10 @@ "express-bearer-token": "^2.4.0", "jsonwebtoken": "^9.0.2", "mongodb": "^6.3.0", - "node-html-parser": "^2.0.0", + "node-html-parser": "^6.0.0", + "prebuilt-tdlib": "^0.1008026.0", "response-time": "^2.3.2", + "tdl": "^7.4.1", "utf8": "^3.0.0", "uuid": "^8.3.0", "ws": "^8.12.0" diff --git a/routes/events.js b/routes/events.js index a402ddb..68ab79e 100644 --- a/routes/events.js +++ b/routes/events.js @@ -1,7 +1,7 @@ -const express = require('express'); -const router = express.Router(); -const { v4: uuidv4 } = require('uuid'); -const Utils = require('../utils/utils'); +import { Router } from 'express'; +const router = Router(); +import { v4 as uuidv4 } from 'uuid'; +import Utils from '../utils/utils.js'; const makeError = error => ({ success: false, error }); @@ -99,4 +99,4 @@ router.get('/', async (req, res) => { } }); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/routes/notes.js b/routes/notes.js index 50a14f2..eb65ddc 100644 --- a/routes/notes.js +++ b/routes/notes.js @@ -1,6 +1,6 @@ -const express = require('express'); -const router = express.Router(); -const { makeError } = require('../utils/errors'); +import { Router } from 'express'; +const router = Router(); +import { makeError } from '../utils/errors.js'; router.post('/', async (req, res) => { const { number, notes } = req.body; @@ -67,4 +67,4 @@ router.delete('/', async (req, res) => { } }); -module.exports = router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/routes/user.js b/routes/user.js index 5bf0cfc..0d31c0a 100644 --- a/routes/user.js +++ b/routes/user.js @@ -1,8 +1,9 @@ -const express = require('express'); -const router = express.Router(); -const jwt = require('jsonwebtoken'); -const User = require('../models/user'); -const { errorCodes, makeError } = require('../utils/errors'); +import { Router } from 'express'; +import Jwt from 'jsonwebtoken'; +import User from '../models/user.js'; +import { errorCodes, makeError } from '../utils/errors.js'; + +const router = Router(); router.post('/signup', async (req, res) => { const { email, password } = req.body; @@ -13,7 +14,7 @@ router.post('/signup', async (req, res) => { if(users.length == 0) { let user = new User(email, password); await collection.insertOne(user.toDB()); - user.token = jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); + user.token = Jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); res.send({ success: true, data: user }); } else { res.send(makeError('User already exists')); @@ -40,7 +41,7 @@ router.post('/login', async (req, res) => { return; } - me.token = jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); + me.token = Jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); res.send({ success: true, data: me }); } else { res.send(makeError('Incorrect login or password', errorCodes.invalidLoginOrPassword)); @@ -67,12 +68,12 @@ router.post('/signIn', async (req, res) => { return; } - me.token = jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); + me.token = Jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); res.send({ success: true, data: me }); } else { let user = new User(email, password); await users.insertOne(user.toDB()); - user.token = jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); + user.token = Jwt.sign({ email }, process.env.JWT_SECRET_AUTH, { expiresIn: process.env.JWT_EXPIRATION_TIME }); res.send({ success: true, data: user }); } } catch(ex) { @@ -106,4 +107,4 @@ router.get('/find', async (req, res) => { res.status(code).send({ success: true, data: { users } }); }); -module.exports = router; +export default router; diff --git a/routes/vehicles.js b/routes/vehicles.js index 5bd9507..c62d239 100644 --- a/routes/vehicles.js +++ b/routes/vehicles.js @@ -1,16 +1,18 @@ -const express = require('express'); -const router = express.Router(); -const jwt = require('jsonwebtoken'); -const cors = require('cors'); -const AvtocodProvider = require('../data_providers/avtocod'); -const Vin01Provider = require('../data_providers/vin01'); -const { regions } = require('../data_providers/constants'); -const RsaProvider = require('../data_providers/rsa'); -const NomerogramProvider = require('../data_providers/nomerogram'); -const Utils = require('../utils/utils'); -const ObjectId = require('mongodb').ObjectId; -const DebugInfo = require('../models/DebugInfo'); -const Vehicle = require('../models/vehicle'); +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 }); @@ -231,7 +233,7 @@ router.get('/colors', async (req, res) => { }); router.get('/regions', (req, res) => { - res.send({ success: true, data: regions }); + res.send({ success: true, data: Constants.regions }); }); router.get('/years', async (req, res) => { @@ -247,7 +249,7 @@ router.get('/years', async (req, res) => { 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 { 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) { @@ -282,4 +284,43 @@ router.post('/checkOsago', async (req, res) => { } }); -module.exports = router; +router.post('/checkGbTg', async (req, res) => { + try { + const number = req.body.number.replace(/ /g, '').toUpperCase(); + + let tgProvider = new TGProvider(); + await tgProvider.init(); + let report = await tgProvider.getReport(number); + await tgProvider.close(); + + let collection = req.db.collection('vehicles'); + let vehicle = await collection.findOne({ number }); + + if(vehicle) { + let vinRegex = RegExp(vehicle.vin1.replace(/\*/g, '.')); + //let stsRegex = RegExp(vehicle.sts.replace(/\*/g, '.')); + + if(report.vin.match(vinRegex) /*&& report.sts.match(stsRegex)*/) { + let updatedFields = { + vin1: report.vin, + color: report.color, + sts: report.sts, + updatedDate: Date.now() + } + + await collection.updateOne({ number }, { $set: updatedFields }); + 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); + } +}); + +export default router; diff --git a/utils/errors.js b/utils/errors.js index b8760a1..f735847 100644 --- a/utils/errors.js +++ b/utils/errors.js @@ -27,4 +27,4 @@ const makeError = (error, code) => { return result; }; -module.exports = { errorCodes, makeError }; \ No newline at end of file +export { errorCodes, makeError }; \ No newline at end of file diff --git a/utils/utils.js b/utils/utils.js index fa949e0..de1b487 100644 --- a/utils/utils.js +++ b/utils/utils.js @@ -80,14 +80,14 @@ class Utils { let mainQuery = { number: { $regex: number } }; switch(scope) { - case 'plateNumber': - break; - case 'vin': - mainQuery = { vin1: { $regex: number } }; - break; - case 'notes': - mainQuery = { 'notes.text': { $regex: RegExp(number, 'i') } }; - break; + case 'plateNumber': + break; + case 'vin': + mainQuery = { vin1: { $regex: number } }; + break; + case 'notes': + mainQuery = { 'notes.text': { $regex: RegExp(number, 'i') } }; + break; } let regionsQuery = null; @@ -135,4 +135,4 @@ class Utils { } } -module.exports = Utils; \ No newline at end of file +export default Utils; \ No newline at end of file