fix: adiciona cache-busting nos arquivos estaticos (css/js) para evitar layout desatualizado apos deploy

This commit is contained in:
Gustavo
2026-08-20 13:52:07 -03:00
parent 48d17d15b5
commit ab8912fce7
5 changed files with 44 additions and 4 deletions
+29
View File
@@ -0,0 +1,29 @@
"""
Versão dos arquivos estáticos (CSS/JS) usada como cache-busting nos links
de app/templates/base.html (?v=...). Sem isso, um navegador que já tinha
carregado /static/estilo.css antes de um deploy pode continuar servindo a
cópia antiga em cache — a URL não muda, então não há sinal pro navegador
buscar de novo — mesmo numa aba anônima aberta antes do deploy terminar.
Calculada uma única vez, na inicialização do processo, a partir do mtime
mais recente entre os arquivos em app/static/. Cada novo deploy recria o
container com os arquivos atualizados (mtime novo), então o valor muda
sozinho a cada implantação, sem precisar editar nada aqui.
"""
import os
_DIR_STATIC = os.path.join(os.path.dirname(__file__), "static")
def _calcular_versao() -> str:
arquivos = [
os.path.join(_DIR_STATIC, nome)
for nome in os.listdir(_DIR_STATIC)
if os.path.isfile(os.path.join(_DIR_STATIC, nome))
]
if not arquivos:
return "0"
return str(int(max(os.path.getmtime(caminho) for caminho in arquivos)))
ASSET_VERSAO = _calcular_versao()
+2
View File
@@ -9,6 +9,7 @@ from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from . import session
from .asset_versao import ASSET_VERSAO
from .auth import NaoAutenticado, UsuarioLogado, exigir_login
from .csrf import CSRF_COOKIE_NAME, gerar_token
from .routers import auth, chave_manual, clientes, contatos, funcionarios, log, minha_conta
@@ -103,6 +104,7 @@ async def carregar_usuario_middleware(request: Request, call_next):
request.state.usuario = (
UsuarioLogado(login=dados["login"], nome=dados["nome"], perfil=dados["perfil"]) if dados else None
)
request.state.asset_versao = ASSET_VERSAO
return await call_next(request)
+3 -3
View File
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block titulo %}Painel de Clientes{% endblock %}</title>
<link rel="icon" type="image/png" href="/static/logo-gti-256x256.png">
<link rel="stylesheet" href="/static/estilo.css">
<link rel="stylesheet" href="/static/estilo.css?v={{ request.state.asset_versao }}">
<script>
(function () {
var tema = localStorage.getItem("tema");
@@ -84,7 +84,7 @@
</main>
</div>
</div>
<script src="/static/sidebar.js" defer></script>
<script src="/static/tema.js" defer></script>
<script src="/static/sidebar.js?v={{ request.state.asset_versao }}" defer></script>
<script src="/static/tema.js?v={{ request.state.asset_versao }}" defer></script>
</body>
</html>
+1 -1
View File
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Entrar — Painel de Clientes</title>
<link rel="icon" type="image/png" href="/static/logo-gti-256x256.png">
<link rel="stylesheet" href="/static/estilo.css">
<link rel="stylesheet" href="/static/estilo.css?v={{ request.state.asset_versao }}">
<style>
body {
display: flex; align-items: center; justify-content: center;
+9
View File
@@ -119,3 +119,12 @@ def test_logout_funciona_depois_de_visitar_clientes(monkeypatch):
resposta_logout = client.post("/logout", data={"csrf_token": token_cookie}, follow_redirects=False)
assert resposta_logout.status_code == 303
def test_estilo_css_tem_versao_de_cache_busting():
resposta = client.get("/login")
assert resposta.status_code == 200
import re
m = re.search(r'/static/estilo\.css\?v=(\d+)', resposta.text)
assert m is not None, "link do estilo.css deveria ter ?v=<versao>"
assert m.group(1) != "0"