"""Health, session bootstrap, freight quoting and the address of a CEP.""" import os import re import httpx from fastapi import APIRouter, HTTPException, Request, Response from ..core import db from ..core.auth import client_ip, owner, new_session, rate_limit from ..core.limits import upload_limit_bytes from ..core.models import FreightEstimate from ..runtime import (ENVIRONMENT, GUEST_SESSION_LIMIT, PART_BYTES, freight, payment, require_delivery_available, storage) router = APIRouter() @router.get('/health') @router.get('/api/health') def health(): try: with db.connect() as c: c.execute('SELECT 1') storage.health() except Exception: raise HTTPException(503, 'Database or storage unavailable') return {'status': 'ok', 'environment': ENVIRONMENT, 'storage': 'minio' if ENVIRONMENT == 'local' else 'r2', 'integrations': 'fake'} @router.get('/api/session') def session(request: Request, response: Response): try: session_id = owner(request) except HTTPException: # Per source, not per deployment: keyed on the environment name this was a # single global bucket, so ~8 new visitors a minute exhausted it site-wide. rate_limit('guest-sessions', client_ip(request), GUEST_SESSION_LIMIT, 900) with db.connect() as c: session_id = new_session(c, response) return {'environment': ENVIRONMENT, 'cart_scope': str(session_id), 'part_bytes': PART_BYTES, 'max_upload_bytes': upload_limit_bytes(), 'payment_provider': payment.name, # Public by design: Mercado Pago's card form needs it in the browser. 'payment_public_key': os.environ.get('MP_PUBLIC_KEY', '') if payment.name == 'mercadopago' else '', # The delivery service the cart quotes, or none: pickup only. 'freight_service': 'jadlog' if freight.name == 'jadlog' else 'mock-standard' if ENVIRONMENT == 'local' else None} @router.post('/api/freight') def quote_freight(body: FreightEstimate): require_delivery_available(body.service) try: return freight.quote(body.service, body.postal_code, body.metres, body.declared_cents) except ValueError as exc: raise HTTPException(422, str(exc)) # The address of a CEP, so the cart fills it in. Looked up here rather than in # the browser, which keeps the Site's CSP to its own origin. VIACEP = 'https://viacep.com.br/ws/{}/json/' CEP_LIMIT = 120 @router.get('/api/cep/{cep}') def cep_address(cep: str, request: Request): if not re.fullmatch(r'[0-9]{8}', cep): raise HTTPException(422, 'CEP must have eight digits') rate_limit('cep-lookup', client_ip(request), CEP_LIMIT, 900) try: response = httpx.get(VIACEP.format(cep), timeout=5) data = response.json() if response.status_code == 200 else {} except (httpx.HTTPError, ValueError): raise HTTPException(503, 'Consulta de CEP indisponível') if not data or data.get('erro'): raise HTTPException(404, 'CEP não encontrado') return {'street': data.get('logradouro') or '', 'district': data.get('bairro') or '', 'city': data.get('localidade') or '', 'state': data.get('uf') or ''}