- ConditionalGetMiddleware added to MIDDLEWARE (ETag/304 for all views)
- @condition(etag_func, last_modified_func) on MateriaLegislativa and
NormaJuridica detail views — skips view execution on cache hit via
data_ultima_atualizacao (auto_now=True) as freshness signal
- nginx /static/: expires 90m + Cache-Control public, max-age=5400
- nginx: removed upload-endpoint special-casing (location ~* ^/(protocoloadm/criar-protocolo|...))
- plan/RATE-LIMITER-PLAN.md updated to reflect all Phase 7 changes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nginx:
- /media/ proxied through Gunicorn (sapl_general rate limit) instead of
direct alias — Django middleware now runs on every media request
- /_accel/media/ internal location serves file bytes via X-Accel-Redirect
sapl/base/media.py (new):
- serve_media() gate: path traversal guard, auth redirect for
documentos_privados/, per-path Redis counter, content-type metadata
cache, X-Accel-Redirect response; falls back to Django serve() in DEBUG
sapl/middleware/ratelimit.py:
- RL_PATH_REQUESTS, RL_UA_BLOCKLIST, FILE_META_KEY constants
- _incr_with_ttl() extracted to module level (reused by media.py)
- Runtime UA deny list: _refresh_ua_blocklist() fetches rl:bot:ua:blocked
SET from Redis (SMEMBERS, cached per worker, TTL=RATE_LIMITER_UA_BLOCKLIST_REFRESH);
_is_redis_blocked_ua() tokenises UA and checks sha256 of each token
sapl/settings.py:
- RATE_LIMITER_UA_BLOCKLIST_REFRESH, MEDIA_PATH_COUNTER_TTL,
MEDIA_FILE_CACHE_TTL added (all env-tunable via config())
plan/RATE_LIMITER_PLAN.md:
- Key schema table updated; media file serving section added;
decision flow documented; UA deny list seed section expanded
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AnonCachePageMixin (sapl/middleware/page_cache.py) that stores full
view responses in the default Redis cache for anonymous (unauthenticated)
GET requests only. Authenticated users always bypass the cache so CSRF
tokens and user-specific UI controls are never served stale.
Applied to:
- ParlamentarCrud.ListView / DetailView — TTL 600 s (changes each term)
- AudienciaCrud.ListView — TTL 120 s (hearings added infrequently)
- ComissaoCrud.ListView — TTL 300 s (committees change rarely)
Also:
- Add PAGE_CACHE_TTL_LIST/DETAIL/STABLE settings (env-configurable)
- Add bingbot + SERankingBacklinksBot to nginx UA blocklist (were already
in BOT_UA_FRAGMENTS / robots.txt; nginx map was the only gap)
- Remove unused ratelimit/method_decorator/RATE_LIMITER_RATE imports from
audiencia/views.py that crept in during Phase 2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove a analise de vínculos cíclicos na construção inicial
do form do filterset.
- O item anterior deve resolver o timeout causado na abertura da
anexação em lote, no entanto os timeouts do nginx e gunicorn foram
aumentados.