feat(infra): auto-commit con 8 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 10:30:26 +02:00
parent 588d092858
commit 909290ddbf
9 changed files with 764 additions and 1 deletions
@@ -0,0 +1,72 @@
---
name: sign_metabase_embed_jwt
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def sign_metabase_embed_jwt(secret: str, resource_type: str, resource_id: int, base_url: str, params: dict | None = None, exp_seconds: int = 3600, theme: str | None = None, bordered: bool = True, titled: bool = True) -> dict"
description: "Firma un JWT de static-embedding de Metabase (HS256 con PyJWT) y construye la URL del iframe (/embed/<type>/<token>#opciones). Soporta question y dashboard, params locked/enabled, TTL configurable y tema opcional."
tags: [metabase, embed, jwt]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: ["jwt", "time"]
tested: true
tests: ["test_firma_y_decodifica_round_trip", "test_resource_type_invalido_lanza_valueerror", "test_embed_url_dashboard"]
test_file_path: "python/functions/infra/sign_metabase_embed_jwt_test.py"
file_path: "python/functions/infra/sign_metabase_embed_jwt.py"
params:
- name: secret
desc: "Secret de embedding de Metabase (Settings > Embedding). NUNCA va al cliente."
- name: resource_type
desc: '"question" o "dashboard" — tipo del recurso a embeber.'
- name: resource_id
desc: "Id numerico de la card o dashboard en Metabase."
- name: base_url
desc: 'URL base de la instancia, ej. "https://reports.autingo.es".'
- name: params
desc: "Parametros de embedding locked/enabled (dict). Default {}."
- name: exp_seconds
desc: "TTL del token en segundos desde ahora. Default 3600 (1h)."
- name: theme
desc: 'Tema opcional: "night" | "transparent" | None.'
- name: bordered
desc: "Si el iframe muestra borde. Default True."
- name: titled
desc: "Si el iframe muestra titulo. Default True."
output: 'dict con {"token": jwt str, "embed_url": url completa /embed/<type>/<token>#opciones, "exp": unix int}.'
---
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from infra import sign_metabase_embed_jwt
# El secret lo pasa el caller (p.ej. desde pass: metabase/aurgi-embed-secret)
result = sign_metabase_embed_jwt(
secret=os.environ["MB_EMBED_SECRET"],
resource_type="question",
resource_id=8048,
base_url="https://reports.autingo.es",
)
print(result["embed_url"])
# https://reports.autingo.es/embed/question/<jwt>#bordered=true&titled=true
print(result["exp"]) # unix timestamp de expiracion
```
## Cuando usarla
Cuando necesites embeder una card/dashboard de Metabase en un iframe sin exponerla publicamente: firma server-side en cada carga, nunca hardcodees el token.
## Gotchas
- El `secret` NUNCA va al cliente — la firma se hace server-side y solo viaja el token al iframe.
- El token expira (`exp_seconds`, default 1h). Refirma en cada carga; no caches el token.
- La card/dashboard necesita `enable_embedding=true` en Metabase, o el endpoint de embed devuelve error.
- Los `params` deben estar registrados en `embedding_params` como `locked` o `enabled` en Metabase, o se ignoran silenciosamente.
- Verificado contra reports.autingo.es: el endpoint `/api/embed/card/<token>/query` devuelve 202 + filas con token valido, 400 con token manipulado.