STACK_FOLLOWERS - Documentación para Desarrolladores
Descripción General
Stack del sistema de seguidores de MAS10. Gestiona relaciones de follow/unfollow entre perfiles (usuarios, equipos, ligas, profesionales), búsqueda de perfiles con recomendaciones basadas en geolocalización (PostGIS clustering), campana de notificaciones por seguido, y endpoint externo para integraciones Lambda-a-Lambda.
Arquitectura
STACK_FOLLOWERS/
├── followers.yaml # Template SAM
├── build_followers.sh
├── layer/
│ ├── database_followers/ # Lógica de dominio
│ │ └── database.py
│ ├── querys_followers/ # Templates SQL
│ │ └── query.py
│ └── utils_followers/
│ └── utils.py
└── services/
├── follow_profile/ # POST, DELETE, PUT, OPTIONS
│ ├── post.py # Seguir perfil(es) - JWT
│ ├── post_external.py # Seguir perfil(es) - API Key
│ ├── delete.py # Dejar de seguir
│ ├── put.py # Campana de notificaciones
│ └── options.py
├── search_profiles/ # GET, OPTIONS
│ ├── get.py # Búsqueda + recomendaciones
│ └── options.py
└── feedrecommendations/ # GET, OPTIONS
├── get.py # Feed de recomendados
└── options.py
Runtime y Configuración
| Propiedad | Valor |
|---|---|
| Python | 3.13 |
| Arquitectura | ARM64 |
| Timeout | 60s |
| Memoria | 256MB |
| Auth | Cognito JWT + API Key (externo) |
Tipos de Origen (Origin)
Los perfiles tienen un tipo de origen que define su naturaleza:
userprofile— Jugador/usuario individualteam— Equipoleague— Ligaprofessional— Profesional
Sistema de Follow
POST /follow_profile (JWT):
- Autentica usuario via JWT
- Valida que follower y followed existan con el origin correcto
- Acepta
id_followedcomo string (1 perfil) o array (N perfiles) - Si ya sigue, retorna mensaje sin error
- Si follow exitoso, remueve de tabla de recomendaciones
- Retorna resultado individual o array según input
POST /external/follow_profile (API Key):
- Mismo comportamiento que JWT pero usa API Key
- Rate limit: 10,000 req/día
- Para integraciones Lambda-a-Lambda
DELETE /follow_profile:
- Valida usuario via JWT
- Verifica que la relación de follow existe
- Si no sigue, retorna "El perfil no se sigue" (200, no error)
- Elimina la relación
PUT /follow_profile (Campana):
- Actualiza estado de la campana de notificaciones
- Requiere follow activo
- Params:
id_followed,origin_followed,bell - Retorna 201 (Created)
Búsqueda y Recomendaciones
GET /search_profiles:
Modos de operación:
-
Búsqueda por nombre (
username+table):- Match parcial con ILIKE
- Incluye
is_followingsi usuario autenticado - Verificación de avatar (verify_avatar)
-
Recomendaciones (
recommendation=true, requiere auth):- Basado en geolocalización (PostGIS clustering)
- Excluye perfiles ya seguidos
- Incluye métricas:
transfer_market,is_player,has_played
-
Detalle (
details=true):- Retorna contadores de followers/followed
- Lista completa de seguidores y seguidos
GET /feedrecommendations:
- Requiere autenticación
- Perfiles recomendados para el feed
- Excluye perfiles ya seguidos
- Incluye métricas de engagement
Tablas de Base de Datos
PostgreSQL:
follower— Relaciones de seguimiento (PK: follower_id + followed_id)id_follower,origin_followerid_followed,origin_followedbell— Estado de campanacreated_at
recommendation— Tabla de recomendaciones- Se alimenta por clustering geográfico
- Se limpia cuando se concreta un follow
userprofile— Perfiles de usuario (join para datos)team— Datos de equipoleague— Datos de liga
Queries geográficos:
Usa PostGIS para clustering de ubicación:
SELECT * FROM recommendation r
JOIN userprofile u ON r.id_recommended = u.id
WHERE ST_DWithin(u.location, ST_MakePoint(%(long)s, %(lat)s)::geography, %(distance)s)
Patrones de Código
Validación de follow duplicado:
# Verifica si ya sigue antes de insertar
existing = db.check_follow(id_follower, id_followed)
if existing:
return {"message": "Ya sigue a este perfil"}
Follow masivo (array):
id_followed = body.get('id_followed')
if isinstance(id_followed, list):
results = [db.follow(follower, fid) for fid in id_followed]
return results
else:
return db.follow(follower, id_followed)
Verify Avatar Helper:
# Verifica que el avatar exista en S3, sino retorna None
avatar = utils.verify_avatar(profile.get('avatar'))
Seguridad
- JWT: Endpoints principales (follow, unfollow, bell, feed)
- API Key: Endpoint externo con rate limit
- Público:
search_profiles(GET) funciona sin auth (resultados limitados) - SQL Injection: Usa
executeQueryNotInjectioncon placeholders
Build y Deploy
cd STACK_FOLLOWERS
sudo bash ./build_followers.sh 1 -d # Dev local
sudo bash ./build_followers.sh 2 -d # Dev deploy
sudo bash ./build_followers.sh 2 -q # QA deploy
sudo bash ./build_followers.sh 2 -p # Prod deploy