162 lines
5.6 KiB
JavaScript
162 lines
5.6 KiB
JavaScript
const dotenv = require("dotenv");
|
|
|
|
dotenv.config();
|
|
|
|
const axios = require("axios");
|
|
const { sleep } = require("./sleepService");
|
|
const { normalizePartnerSigla } = require("./partnerSiglaService");
|
|
|
|
|
|
// Configure sua API_KEY e COOKIE aqui ou via variáveis de ambiente
|
|
const API_URL = process.env.API_URL;
|
|
const API_KEY = process.env.API_KEY;
|
|
const COOKIE = process.env.COOKIE;
|
|
|
|
const HEADERS = {
|
|
"api-key": API_KEY,
|
|
Cookie: COOKIE,
|
|
};
|
|
|
|
// small fetch wrapper for external services (ViaCEP etc.) with basic rate-limiting
|
|
|
|
const BASE_BACKOFF_MS = 10; // backoff inicial para retry
|
|
const MAX_RETRIES = 5;
|
|
|
|
async function getMinDistance(lat, lon) {
|
|
const radii = [5000, 10000, 20000];
|
|
for (const raio of radii) {
|
|
let attempt = 0;
|
|
while (attempt < MAX_RETRIES) {
|
|
try {
|
|
// observe que aqui enviamos itens[] como string simples (algumas APIs esperam isto)
|
|
const resp = await axios.get(API_URL, {
|
|
headers: HEADERS,
|
|
params: {
|
|
raio,
|
|
latitude: lat,
|
|
longitude: lon,
|
|
"itens[]": "caixa",
|
|
consultarPasta: "S",
|
|
},
|
|
timeout: 10000,
|
|
});
|
|
|
|
const data = resp.data || {};
|
|
const registros = data.registros || [];
|
|
console.info(
|
|
`[INFO] API resp. raio=${raio} lat=${lat} lon=${lon} registros=${registros.length}`
|
|
);
|
|
if (registros.length > 0) {
|
|
// same extraction logic as before
|
|
const candidates = registros
|
|
.map((r) => ({ raw: r, distanciaRaw: r && r.distancia }))
|
|
.map((o) => ({
|
|
raw: o.raw,
|
|
num:
|
|
o.distanciaRaw !== undefined &&
|
|
o.distanciaRaw !== null &&
|
|
o.distanciaRaw !== ""
|
|
? Number(o.distanciaRaw)
|
|
: null,
|
|
}))
|
|
.filter((x) => x.num !== null && !Number.isNaN(x.num));
|
|
if (candidates.length) {
|
|
candidates.sort((a, b) => a.num - b.num);
|
|
const best = candidates[0];
|
|
const r = best.raw || {};
|
|
let pastaSigla = null;
|
|
try {
|
|
if (r.pasta) {
|
|
if (typeof r.pasta === "string" && r.pasta.trim())
|
|
pastaSigla = r.pasta.trim();
|
|
else if (r.pasta.sigla && String(r.pasta.sigla).trim())
|
|
pastaSigla = String(r.pasta.sigla).trim();
|
|
else if (
|
|
r.pasta.cidade &&
|
|
r.pasta.cidade.sigla &&
|
|
String(r.pasta.cidade.sigla).trim()
|
|
)
|
|
pastaSigla = String(r.pasta.cidade.sigla).trim();
|
|
}
|
|
} catch (e) {
|
|
pastaSigla = null;
|
|
}
|
|
if (!pastaSigla) {
|
|
for (let j = 0; j < candidates.length; j++) {
|
|
const rr = candidates[j].raw || {};
|
|
try {
|
|
if (rr.pasta) {
|
|
if (typeof rr.pasta === "string" && rr.pasta.trim()) {
|
|
pastaSigla = rr.pasta.trim();
|
|
break;
|
|
}
|
|
if (rr.pasta.sigla && String(rr.pasta.sigla).trim()) {
|
|
pastaSigla = String(rr.pasta.sigla).trim();
|
|
break;
|
|
}
|
|
if (
|
|
rr.pasta.cidade &&
|
|
rr.pasta.cidade.sigla &&
|
|
String(rr.pasta.cidade.sigla).trim()
|
|
) {
|
|
pastaSigla = String(rr.pasta.cidade.sigla).trim();
|
|
break;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// continue
|
|
}
|
|
}
|
|
}
|
|
if (!pastaSigla)
|
|
console.warn(
|
|
`[WARN] Nenhuma pasta.sigla encontrada para coordenadas ${lat},${lon} (closest dist ${best.num})`
|
|
);
|
|
pastaSigla = normalizePartnerSigla(pastaSigla);
|
|
return { dist: best.num, pastaSigla };
|
|
}
|
|
} else {
|
|
// log mais detalhado para diagnóstico quando vazio
|
|
console.debug(
|
|
`[DEBUG] resposta vazia para raio=${raio} lat=${lat} lon=${lon} -> primeiroFragmento: ${JSON.stringify(
|
|
Array.isArray(data.registros) ? data.registros.slice(0, 3) : data
|
|
)}`
|
|
);
|
|
}
|
|
// sem distancias válidas para este raio -> tenta próximo raio
|
|
break;
|
|
} catch (err) {
|
|
attempt += 1;
|
|
if (err.response && err.response.status === 429) {
|
|
const ra =
|
|
err.response.headers &&
|
|
(err.response.headers["retry-after"] ||
|
|
err.response.headers["Retry-After"]);
|
|
let waitMs = BASE_BACKOFF_MS * Math.pow(2, attempt - 1);
|
|
if (ra) {
|
|
const raSec = parseInt(ra, 10);
|
|
if (!isNaN(raSec)) waitMs = raSec * 1000;
|
|
}
|
|
console.warn(
|
|
`[WARN] 429 recebido para ${lat},${lon} - aguardando ${waitMs}ms e tentando novamente (attempt ${attempt}/${MAX_RETRIES})`
|
|
);
|
|
await sleep(waitMs);
|
|
continue;
|
|
}
|
|
const waitMs = BASE_BACKOFF_MS * Math.pow(2, attempt - 1);
|
|
console.warn(
|
|
`[WARN] Erro ao consultar API para ${lat},${lon}: ${err.message} - backoff ${waitMs}ms (attempt ${attempt}/${MAX_RETRIES})`
|
|
);
|
|
await sleep(waitMs);
|
|
}
|
|
}
|
|
// pequena espera entre mudanças de raio
|
|
await sleep(50);
|
|
}
|
|
|
|
console.error(`[ERROR] Sem registros válidos para ${lat},${lon} após todos os raios`);
|
|
return null;
|
|
}
|
|
|
|
module.exports = { getMinDistance };
|