La Cajita TV - Backend Skill
Guía para desarrollo del backend FastAPI + PostgreSQL + Auth0.
Stack Tecnológico
- •Python 3.12 con type hints obligatorios
- •FastAPI 0.115+ como framework web
- •PostgreSQL 15+ con psycopg2
- •Auth0 JWT RS256 para autenticación
- •Sentry para monitoreo
Arquitectura de Servicios
code
Puerto 8000: App_Mobil.py → API Mobile (pública + autenticada) Puerto 8001: Api.py → API Admin Web (requiere Auth0)
Estructura del Proyecto
code
/opt/adm-caja-unified/fastapi-playlists/ ├── Lacajita/ │ ├── App_Mobil/ │ │ └── App_Mobil.py # API Mobile (~6500 líneas) │ └── Lacajita/ │ └── Api.py # API Admin Web ├── services/ # Servicios compartidos │ ├── jwplayer_service.py │ ├── database.py │ └── ... ├── img/ # Archivos estáticos │ └── livetv/ # Logos de canales └── requirements.txt
Autenticación Auth0
Decorador de Autenticación
python
from functools import wraps
from fastapi import HTTPException, Request
def require_auth(roles: list[str] = None):
def decorator(func):
@wraps(func)
async def wrapper(request: Request, *args, **kwargs):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if not token:
raise HTTPException(401, "Token requerido")
payload = verify_token(token) # Valida JWT RS256
if roles:
user_roles = payload.get("https://lacajita.tv/roles", [])
if not any(r in user_roles for r in roles):
raise HTTPException(403, "Permisos insuficientes")
request.state.user = payload
return await func(request, *args, **kwargs)
return wrapper
return decorator
Roles del Sistema
| Rol | Permisos |
|---|---|
admin | Acceso total, gestión usuarios |
producer | Gestión de noticias y contenido propio |
agency | Crear anuncios, ver reportes |
user | Consumo de contenido |
Patrones de Código
Endpoint con Autenticación
python
@app.get("/api/videos")
@app.get("/videos") # Dual routing para backward compatibility
@require_auth(roles=["admin", "producer"])
async def get_videos(request: Request):
"""Obtiene lista de videos del usuario."""
user_id = request.state.user.get("sub")
async with get_db_connection() as conn:
videos = await conn.fetch(
"SELECT * FROM videos WHERE user_id = $1",
user_id
)
return {"videos": [dict(v) for v in videos]}
Conexión a Base de Datos
python
import os
import psycopg2
from contextlib import contextmanager
PG_HOST = os.getenv("PG_HOST", "localhost")
PG_PORT = os.getenv("PG_PORT", "5432")
PG_DB = os.getenv("PG_DB", "lacajita_db")
PG_USER = os.getenv("PG_USER", "lacajita_app")
PG_PASSWORD = os.getenv("PG_PASSWORD")
@contextmanager
def get_db_connection():
conn = psycopg2.connect(
host=PG_HOST,
port=PG_PORT,
dbname=PG_DB,
user=PG_USER,
password=PG_PASSWORD
)
try:
yield conn
finally:
conn.close()
Manejo de Errores
python
from fastapi import HTTPException
from sentry_sdk import capture_exception
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
capture_exception(exc)
return JSONResponse(
status_code=500,
content={"detail": "Error interno del servidor"}
)
Dual Routing
Para backward compatibility, todos los endpoints deben tener doble decorador:
python
@app.get("/api/playlists") # Nueva ruta con prefijo /api
@app.get("/playlists") # Ruta legacy sin prefijo
async def get_playlists():
...
Base de Datos
Tablas Principales
| Tabla | Descripción |
|---|---|
lacajita_playlists | Playlists de contenido |
livetv_channels | Canales de TV en vivo |
news_feed | Noticias |
ad_requests | Solicitudes de anuncios |
users | Usuarios del sistema |
Migraciones
Archivos SQL en /opt/adm-caja-unified/db-migration/:
- •
schema_postgres.sql- Esquema principal - •
livetv_native_system.sql- Sistema LiveTV - •
news_interactions.sql- Interacciones de noticias
Comandos
bash
# Iniciar API Admin (puerto 8001) cd /opt/adm-caja-unified/fastapi-playlists/Lacajita source venv/bin/activate uvicorn Api:app --host 127.0.0.1 --port 8001 --reload # Iniciar API Mobile (puerto 8000) cd /opt/adm-caja-unified/fastapi-playlists/Lacajita/App_Mobil source venv/bin/activate uvicorn App_Mobil:app --host 127.0.0.1 --port 8000 --reload # Health check curl http://127.0.0.1:8001/health curl http://127.0.0.1:8000/health
Variables de Entorno
bash
# Base de datos PG_HOST=localhost PG_PORT=5432 PG_DB=lacajita_db PG_USER=lacajita_app PG_PASSWORD=*** # Auth0 AUTH0_DOMAIN=lacajita.us.auth0.com AUTH0_AUDIENCE=https://api.lacajita.tv # Servicios externos JWPLAYER_API_KEY=*** SENTRY_DSN=***
Checklist Pre-Commit
- • Type hints en todas las funciones
- • Autenticación verificada en endpoints sensibles
- • Dual routing implementado
- • Errores manejados con try-except
- • Conexiones DB cerradas correctamente