Skip to main content

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

PropiedadValor
Python3.13
ArquitecturaARM64
Timeout60s
Memoria256MB
AuthCognito JWT + API Key (externo)

Tipos de Origen (Origin)

Los perfiles tienen un tipo de origen que define su naturaleza:

  • userprofile — Jugador/usuario individual
  • team — Equipo
  • league — Liga
  • professional — Profesional

Sistema de Follow

POST /follow_profile (JWT):

  1. Autentica usuario via JWT
  2. Valida que follower y followed existan con el origin correcto
  3. Acepta id_followed como string (1 perfil) o array (N perfiles)
  4. Si ya sigue, retorna mensaje sin error
  5. Si follow exitoso, remueve de tabla de recomendaciones
  6. 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:

  1. Valida usuario via JWT
  2. Verifica que la relación de follow existe
  3. Si no sigue, retorna "El perfil no se sigue" (200, no error)
  4. 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:

  1. Búsqueda por nombre (username + table):

    • Match parcial con ILIKE
    • Incluye is_following si usuario autenticado
    • Verificación de avatar (verify_avatar)
  2. Recomendaciones (recommendation=true, requiere auth):

    • Basado en geolocalización (PostGIS clustering)
    • Excluye perfiles ya seguidos
    • Incluye métricas: transfer_market, is_player, has_played
  3. 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_follower
    • id_followed, origin_followed
    • bell — Estado de campana
    • created_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 equipo
  • league — 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 executeQueryNotInjection con 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