All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 2m21s
Build and deploy / Secret scan and release gate (push) Successful in 7s
Build and deploy / Publish images (push) Successful in 1m47s
A complete CEP now looks up its address on the server (ViaCEP, rate limited, keeping the Site's CSP to its own origin) and fills street, district, city and state, moving the cursor to the first field left, and quotes the freight without pressing Calcular. The CEP keeps its mask when the cart is restored. Freight and its delivery time appear in the order summary instead of a line under the CEP, the delivery option says it ships with Jadlog, and the address reminder is a hint rather than an error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
3.2 KiB
Python
73 lines
3.2 KiB
Python
"""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 ''}
|