diff --git a/.env b/.env index 0e45aa0..926a3f0 100644 --- a/.env +++ b/.env @@ -2,4 +2,10 @@ GOOGLE_API_KEY="AIzaSyCTrRFGKCZSspHRmTWQiclmIEOg-LROgyo" API_URL="https://plutao.geogridmaps.com.br/vale/api/v3/viabilidade/raio" API_KEY="6d717e972ba17c7cf0ab731801b8bbeac2f281e5" COOKIE="PHPSESSID=6d717e972ba17c7cf0ab731801b8bbeac2f281e5" -PORT="3000" \ No newline at end of file +PORT="3000" + +DATABASE_HOST = 'crash.sothistelecom.com' +DATABASE_PORT = '3306' +DATABASE_NAME = 'viabilidade_data' +DATABASE_USER = 'sothis.viabilidade' +DATABASE_USER_PASSWORD = '4Hn&i$fs71hMoJwy' \ No newline at end of file diff --git a/app.js b/app.js index 7898970..c16d1d5 100644 --- a/app.js +++ b/app.js @@ -8,6 +8,7 @@ const cors = require("cors"); const { fetchJson } = require("./services/jsonService"); const { getMinDistance } = require("./services/distanceService"); const { geocodeWithGoogle } = require("./services/geocodeService"); +const { insertConsultaData } = require("./models/databaseModel"); function createApp() { const app = express(); @@ -49,47 +50,47 @@ function createApp() { const lat = Number(geo.lat); const lon = Number(geo.lon); const result = await getMinDistance(lat, lon); - if (result && result.dist !== undefined) { - // preciso criar 2 campos: Link Dedicado e Link Não Dedicado em que o dedicado é viável até 1000m e o não dedicado até 500m - if (result.dist <= 500) { - return res.json({ - endereco, - latitude: lat, - longitude: lon, - distancia: result.dist, - dedicado: "Viável", - naoDedicado: "Viável", - }); - } else if (result.dist <= 1000) { - return res.json({ - endereco, - latitude: lat, - longitude: lon, - distancia: result.dist, - dedicado: "Viável", - naoDedicado: "Não viável", - }); - } else { - return res.json({ - endereco, - latitude: lat, - longitude: lon, - distancia: result.dist, - dedicado: "Não viável", - naoDedicado: "Não viável", - }); - } + + // determinar valores de retorno padrão + let distancia = null; + let dedicado = "Não viável"; + let naoDedicado = "Não viável"; + + if (result && result.dist !== undefined && result.dist !== null) { + distancia = result.dist; + // preciso criar 2 campos: Link Dedicado e Link Não Dedicado em que o dedicado é viável até 1000m e o não dedicado até 500m + if (distancia <= 500) { + dedicado = "Viável"; + naoDedicado = "Viável"; + } else if (distancia <= 1000) { + dedicado = "Viável"; + naoDedicado = "Não viável"; + } else { + dedicado = "Não viável"; + naoDedicado = "Não viável"; } - // quando a consulta não retorna um resultado válido, responder como 'Não viável' - // isso evita que a UI fique presa em "Consultando..." esperando uma resposta - return res.json({ - endereco, - latitude: lat, - longitude: lon, - distancia: null, - dedicado: "Não viável", - naoDedicado: "Não viável", - }); + } else { + dedicado = "Não viável"; + naoDedicado = "Não viável"; + } + + const response = { + endereco, + latitude: lat, + longitude: lon, + distancia, + dedicado, + naoDedicado, + }; + + // Inserção no banco: usar fire-and-forget para não atrasar a resposta ao usuário. + // Se preferir garantir que a gravação ocorreu antes de responder, troque a chamada abaixo por: + // await insertConsultaData(cep, uf, cidade, bairro, logradouro); + insertConsultaData(cep, uf, cidade, bairro, logradouro, dedicado, naoDedicado) + .then(() => console.info(`[INFO] Consulta salva para CEP ${cep}`)) + .catch((err) => console.error(`[ERROR] Falha ao salvar consulta para CEP ${cep}:`, err)); + + return res.json(response); } catch (err) { console.error(err); return res.status(500).json({ error: "erro na consulta" }); diff --git a/config/databaseConfig.js b/config/databaseConfig.js new file mode 100644 index 0000000..7e43600 --- /dev/null +++ b/config/databaseConfig.js @@ -0,0 +1,24 @@ +const dotenv = require("dotenv"); + +dotenv.config(); + +const mysql = require("mysql2/promise"); + +const pool = mysql.createPool({ + host: process.env.DATABASE_HOST, + port: process.env.DATABASE_PORT, + user: process.env.DATABASE_USER, + password: process.env.DATABASE_USER_PASSWORD, + database: process.env.DATABASE_NAME, +}); + +pool.getConnection().then((connection) => { + console.info( + "[INFO] Conexão com o banco de dados estabelecida com sucesso." + ); + connection.release(); +}).catch((error) => { + console.error("[ERROR] Falha ao conectar ao banco de dados:", error); +}); + +module.exports = pool; diff --git a/models/databaseModel.js b/models/databaseModel.js new file mode 100644 index 0000000..fa2b7aa --- /dev/null +++ b/models/databaseModel.js @@ -0,0 +1,51 @@ +const pool = require("../config/databaseConfig"); + +async function insertConsultaData(cep, estado, cidade, bairro, logradouro, dedicado, naoDedicado) { + const query = `INSERT INTO consultas (cep, estado, cidade, bairro, logradouro, dedicado, nao_dedicado, data_consulta) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())`; + const values = [cep, estado, cidade, bairro, logradouro, dedicado, naoDedicado]; + + try { + // Executa a query de inserção e retornar se foi inserido com sucesso e o id da coluna id_consulta + const [result] = await pool.execute(query, values); + if (result.affectedRows === 1) { + const id = await getConsultaId(); + console.info( + `[INFO] Dados de consulta inseridos com sucesso. ID: ${id}` + ); + return; + } + } catch (error) { + console.error("[ERROR] Falha ao inserir dados de consulta:", error); + throw error; + } +} + +async function getConsultaId() { + const query = `SELECT LAST_INSERT_ID() AS id_consulta`; + + try { + const [rows] = await pool.execute(query); + return rows[0].id_consulta; + } catch (error) { + console.error("[ERROR] Falha ao buscar ID da última consulta:", error); + throw error; + } +} + +async function getConsultaData(id) { + const query = `SELECT * FROM consultas WHERE id_consulta = ?`; + const values = [id]; + + try { + const [rows] = await pool.execute(query, values); + return rows[0]; + } catch (error) { + console.error("[ERROR] Falha ao buscar dados de consulta:", error); + throw error; + } +} + +module.exports = { + insertConsultaData, + getConsultaData +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 99e5c57..373cda5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,8 @@ "express": "^4.18.2", "fast-csv": "^4.3.6", "ipaddr.js": "^2.2.0", - "multer": "*" + "multer": "*", + "mysql2": "^3.15.3" } }, "node_modules/@fast-csv/format": { @@ -94,6 +95,15 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/axios": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", @@ -274,6 +284,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -514,6 +533,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/generator-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.0.tgz", @@ -646,6 +674,12 @@ "node": ">= 10" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", @@ -687,6 +721,36 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -790,6 +854,54 @@ "node": ">= 10.16.0" } }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -975,6 +1087,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -1062,6 +1179,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/package.json b/package.json index 95aabd7..eae4bf9 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "express": "^4.18.2", "fast-csv": "^4.3.6", "ipaddr.js": "^2.2.0", - "multer": "*" + "multer": "*", + "mysql2": "^3.15.3" } } diff --git a/public/main.js b/public/main.js index 32e9cd3..b439c8a 100644 --- a/public/main.js +++ b/public/main.js @@ -9,7 +9,7 @@ document.getElementById('btnConsultaCep').addEventListener('click', async () => try { const resp = await fetch(`/consulta-cep?cep=${encodeURIComponent(cep)}&numero=${encodeURIComponent(numero)}`); const data = await resp.json(); - if (data.distancia) { + if (data && data.endereco !== undefined) { // colocar o card-results__container (resultados) com display block endereco.innerHTML = `Endereço: ${data.endereco}.`; resultados.style.display = 'block'; @@ -38,6 +38,8 @@ document.getElementById('btnConsultaCep').addEventListener('click', async () => } } else if (data.error) { endereco.innerText = 'Erro: ' + data.error; + } else { + endereco.innerText = 'Erro na consulta'; } } catch (e) { endereco.innerText = 'Erro na consulta';