viabiliza/service/viabilidadeService.js
Rafael Lopes 827855295e FEAT: Novos tipos de arquivos são permitidos
- Excel, CSV(; , ou tabulação) e TXT são agora aceitos para upload.
- Formato de dados atualizado para arquivos que fogem do padrão pré definido.
2026-05-04 16:38:37 -03:00

124 lines
3.5 KiB
JavaScript

const axios = require('axios');
const fs = require('fs');
const readline = require('readline');
const path = require('path');
const XLSX = require('xlsx');
const { apiConfig, apiViabilidadeUrl, apiUrlBase } = require('../config/apiConfig');
function normalizeHeader(value) {
return String(value || '')
.trim()
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[_-]+/g, ' ')
.replace(/\s+/g, ' ');
}
function hasHeader(headers, aliases) {
const normalizedAliases = aliases.map(normalizeHeader);
return headers.some(header => normalizedAliases.includes(header));
}
function isExcelFile(filePath) {
return ['.xls', '.xlsx'].includes(path.extname(filePath).toLowerCase());
}
function detectDelimiter(line) {
const delimiters = [';', '\t', ','];
return delimiters
.map(delimiter => ({ delimiter, count: line.split(delimiter).length }))
.sort((a, b) => b.count - a.count)[0].delimiter;
}
function readExcelHeaders(filePath) {
const workbook = XLSX.readFile(filePath, { cellDates: false, raw: false });
const firstSheetName = workbook.SheetNames[0];
if (!firstSheetName) return [];
const rows = XLSX.utils.sheet_to_json(workbook.Sheets[firstSheetName], {
header: 1,
blankrows: false,
defval: ''
});
return (rows[0] || []).map(normalizeHeader);
}
async function readDelimitedHeaders(filePath) {
const instream = fs.createReadStream(filePath, { encoding: 'utf8' });
const rl = readline.createInterface({ input: instream, crlfDelay: Infinity });
for await (const rawLine of rl) {
const line = rawLine.replace(/^\uFEFF/, '').replace(/\r$/, '');
if (!line.trim()) continue;
rl.close();
return line.split(detectDelimiter(line)).map(normalizeHeader);
}
rl.close();
return [];
}
function hasCepHeader(headers) {
return headers.some(header => /\bcep\b/.test(header) || header === 'codigo postal');
}
function hasAddressOrNumberHeader(headers) {
return headers.some(header => ['numero', 'num', 'nº', 'n°'].includes(header)
|| header.includes('endereco')
|| header.includes('logradouro'));
}
async function consultarViabilidade(data) {
try {
const dataType = await discoverDataType(data);
let endpoint = apiUrlBase;
if (dataType === 'geolocalizacao') {
endpoint += 'viabilidade/lat-long';
} else {
endpoint += 'viabilidade';
}
const response = await axios.post(endpoint, data, {
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
});
console.log('Resposta da API de viabilidade:', response.data);
return (response.data);
} catch (error) {
throw error;
}
}
// Preciso de uma função para verificar se os dados vindos são de CEP ou de geolocalização
async function discoverDataType(input) {
if (typeof input === 'string') {
const headers = isExcelFile(input)
? readExcelHeaders(input)
: await readDelimitedHeaders(input);
const hasCepNumero = hasCepHeader(headers) && hasAddressOrNumberHeader(headers);
if (hasCepNumero) {
return 'cep';
} else if (headers.includes('latitude') && headers.includes('longitude')) {
return 'geolocalizacao';
} else {
return 'unknown';
}
} else if (typeof input === 'object') {
// Trata como objeto de dados
if (input.cep && input.numero) {
return 'cep';
} else if (input.latitude && input.longitude) {
return 'geolocalizacao';
} else {
return 'unknown';
}
} else {
return 'unknown';
}
}
module.exports = { consultarViabilidade, discoverDataType };