Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ca9a3ca
chore(deps): bump the npm-runtime-patch-minor group with 2 updates (#…
dependabot[bot] Aug 10, 2026
251114c
chore(deps): bump fast-uri from 3.1.4 to 3.1.5 (#138)
dependabot[bot] Aug 10, 2026
299b95d
chore(deps): bump undici from 6.27.0 to 6.28.0 (#137)
dependabot[bot] Aug 10, 2026
239575d
chore(security): tighten Dependabot and add SECURITY.md
Japegomez Aug 10, 2026
b6c8df0
fix(security): patch transitive advisories and audit allowlist
Japegomez Aug 10, 2026
4f1b12f
feat(profile): add native contact creation from player phone (#139)
Japegomez Aug 10, 2026
bd14623
feat(stats): player statistics, ELO ranking, H2H insights and unified…
Japegomez Aug 10, 2026
31c0405
fix(ci): restrict GITHUB_TOKEN permissions in workflows
Japegomez Aug 10, 2026
ba75453
feat(leagues): ligas, stats de perfil y acceso admin a privados (#143)
Japegomez Aug 10, 2026
5bd6809
Merge branch 'main' into develop
Japegomez Aug 10, 2026
06ae20b
chore(deps): integrate Dependabot #140 and #141 (#145)
Japegomez Aug 10, 2026
3bf9c05
merge: sync main into develop
Japegomez Aug 11, 2026
5a1daba
fix(security): lock down league lifecycle and gate stats refresh
Japegomez Aug 11, 2026
f32c580
fix(security): harden migration ACL revokes for anon and helpers
Japegomez Aug 11, 2026
ecdeab1
docs: document leagues, stats and account-delete for v1.7
Japegomez Aug 11, 2026
592aad2
feat: friend system + match invitations (v1.8.0) (#157)
Japegomez Aug 12, 2026
218fa5f
fix(friends): harden invites UX and follow-up migrations
Japegomez Aug 12, 2026
bc7b0e1
fix(auth): extend background session timeout to 6h
Japegomez Aug 12, 2026
10751f6
merge: sync main hotfix v1.7.1 into develop
Japegomez Aug 12, 2026
99f8e25
fix(deps): pin react-test-renderer for npm ci
Japegomez Aug 12, 2026
536cb57
test: cover friends/invites hooks to restore coverage gates
Japegomez Aug 12, 2026
43adce5
fix(security): lockdown RLS queue and cron RPCs
Japegomez Aug 13, 2026
7dcdf0f
fix(security): pin search_path on tournament SQL helpers
Japegomez Aug 13, 2026
5e81189
feat(auth): add Turnstile on submit and stronger passwords
Japegomez Aug 13, 2026
8146f3a
chore(release): bump hotfix version to 1.8.1
Japegomez Aug 13, 2026
a293dcd
docs: close Turnstile operational checklist for v1.8.1
Japegomez Aug 13, 2026
7a59705
fix: apply PR review follow-ups for auth, invites, and profiles
Japegomez Aug 13, 2026
a298d19
merge: sync main v1.8.0 into develop
Japegomez Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ EXPO_PUBLIC_POSTHOG_API_KEY=phc_XXXXXXXXXXXXXXXXXXXX

### Invite HTTPS host (Firebase Hosting default domain, no protocol)
# Used for WhatsApp share links: https://HOST/m/{id} and https://HOST/t/{id}
# eas env:create --name EXPO_PUBLIC_INVITE_HOST --value "musapp-731e1.web.app" --environment production --visibility plain
# eas env:create --name EXPO_PUBLIC_INVITE_HOST --value "musapp-731e1.web.app" --environment production --visibility plaintext
EXPO_PUBLIC_INVITE_HOST=musapp-731e1.web.app

### Cloudflare Turnstile (Dashboard → Turnstile → widget → Site Key)
# Public site key only. The Secret Key goes in Supabase Auth → Bot and Abuse Protection, never here.
# Hostnames on the widget (no https://): localhost (Expo Go / web) and musapp-731e1.web.app (release).
# eas env:create production --name EXPO_PUBLIC_TURNSTILE_SITE_KEY --value "0x4AAAAA..." --type string --visibility plaintext
EXPO_PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAA_YOUR_SITE_KEY
# Optional override of the production native page host (default: EXPO_PUBLIC_INVITE_HOST)
# EXPO_PUBLIC_TURNSTILE_HOSTNAME=musapp-731e1.web.app

## CI/CD (secrets: NO van al bundle)

### GitHub Actions — repo secrets
Expand All @@ -39,7 +47,8 @@ EXPO_PUBLIC_INVITE_HOST=musapp-731e1.web.app

### EAS Build (cloud/CI)
# Crea las mismas variables en EAS (Environment = production/preview/...) para que el build release arranque:
# eas env:create production --name EXPO_PUBLIC_SUPABASE_URL --value "https://xxx.supabase.co" --type string --visibility plain
# eas env:create production --name EXPO_PUBLIC_SUPABASE_URL --value "https://xxx.supabase.co" --type string --visibility plaintext
# eas env:create production --name EXPO_PUBLIC_SUPABASE_ANON_KEY --value "..." --type string --visibility secret
# eas env:create production --name EXPO_PUBLIC_SENTRY_DSN --value "https://..." --type string --visibility plain
# eas env:create production --name EXPO_PUBLIC_POSTHOG_API_KEY --value "phc_..." --type string --visibility secret
# eas env:create production --name EXPO_PUBLIC_SENTRY_DSN --value "https://..." --type string --visibility plaintext
# eas env:create production --name EXPO_PUBLIC_POSTHOG_API_KEY --value "phc_..." --type string --visibility secret
# eas env:create production --name EXPO_PUBLIC_TURNSTILE_SITE_KEY --value "0x4AAAAA..." --type string --visibility plaintext
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ La aplicación sigue una **arquitectura cliente-servidor** con separación clara
┌────────────────────────────▼────────────────────────────────┐
│ SERVICIOS EXTERNOS E INFRAESTRUCTURA │
│ Sentry · PostHog · Expo EAS · GitHub Actions · Push (FCM/APNs)
│ Sentry · PostHog · Expo EAS · GitHub Actions · Push · Turnstile
└─────────────────────────────────────────────────────────────┘
```

Expand Down Expand Up @@ -149,6 +149,7 @@ La aplicación sigue una **arquitectura cliente-servidor** con separación clara
| 🛡️ **Sentry** | Monitorización de errores y rendimiento |
| 📊 **PostHog** | Analítica de producto |
| 🔔 **Expo Push Notifications** | Notificaciones en Android (FCM) e iOS (APNs) |
| 🧩 **Cloudflare Turnstile** | CAPTCHA en login, registro y recuperación de contraseña |
| 🌿 **GitFlow** | Estrategia de ramas: `main`, `develop`, `feature/*`, `release/*` |

---
Expand All @@ -160,12 +161,15 @@ La aplicación sigue una **arquitectura cliente-servidor** con separación clara
- Inicio de sesión con **Google**, **Apple ID** y **correo electrónico**
- Registro con aceptación de términos legales y política de privacidad
- Recuperación de contraseña y persistencia de sesión entre reinicios
- **Cloudflare Turnstile** al enviar login, registro o recuperación (no en Google/Apple)
- Contraseña con mayúscula, minúscula, dígito y símbolo; cambio desde el perfil con la contraseña actual
- **Eliminación de cuenta** conforme al RGPD, con anonimización del historial compartido (partidas, ligas y torneos)
- Cierre de sesión con confirmación explícita

### 👤 Perfil de usuario

- Datos personales: nombre, teléfono con validación internacional, localidad y foto de perfil
- Cambio de contraseña en edición de perfil (exige la actual)
- Preferencias granulares de **notificaciones push** por tipo de evento
- Historial personal de partidas con indicadores de victoria y derrota
- Resumen de **estadísticas / ELO**, logros destacados y pantalla de stats detallada
Expand Down Expand Up @@ -236,6 +240,7 @@ La aplicación sigue una **arquitectura cliente-servidor** con separación clara
### 🔒 Seguridad y cumplimiento

- Autenticación OAuth2 con flujo PKCE y tokens de corta duración
- **Turnstile** (Cloudflare) como desafío anti-bots en los formularios de email
- Políticas de acceso a datos por usuario y por rol en base de datos
- Protección de datos personales: teléfono visible solo entre participantes de la misma partida
- Cumplimiento RGPD: consentimiento, minimización de datos y derecho de supresión
Expand Down
17 changes: 12 additions & 5 deletions REQUIREMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ App móvil para jugadores de mus en España que permite encontrar contrincantes
### Decisiones clave

- **Marca y legal (may. 2026):** nombre comercial **jugaMUS** (`APP_DISPLAY_NAME`; nombre en launcher vía `app.json`). Deep link scheme `jugamus`. Términos y privacidad con texto estático y disclaimer «Texto legal definitivo pendiente de revisión jurídica.» hasta revisión legal.
- **CI/CD (jun.–ago. 2026):** workflow reutilizable `quality.yml` (job `Quality`: Gitleaks, `expo-doctor`, lint, tests, cobertura ≥60% líneas en `src/{utils,lib,services,hooks}`). `eas.yml` en push a `main`: quality → tag `v{version}-{YYYYMMDD.HHmm}` → build Android/iOS → submit Play + TestFlight. `GITHUB_TOKEN` con mínimo privilegio: `contents: read` por defecto; `contents: write` solo en el job de etiquetado de release (`eas.yml`). Dependabot (npm + github-actions): version updates a **`develop`**, sin majors ni patch/minor del stack Expo/RN (upgrade de SDK con `npx expo upgrade`), grupos prod/dev + security, cooldown. Política de vulnerabilidades en `SECURITY.md`. Secrets GitHub: `EXPO_TOKEN`, `GOOGLE_PLAY_SERVICE_KEY_JSON`. EAS `production`: `GOOGLE_SERVICES_JSON`, `SENTRY_AUTH_TOKEN`, **`EXPO_PUBLIC_SUPABASE_URL`** / **`EXPO_PUBLIC_SUPABASE_ANON_KEY`**, **`EXPO_PUBLIC_INVITE_HOST`**. iOS: `ascAppId` `6775626292`.
- **CI/CD (jun.–ago. 2026):** workflow reutilizable `quality.yml` (job `Quality`: Gitleaks, `expo-doctor`, lint, tests, cobertura ≥60% líneas en `src/{utils,lib,services,hooks}`). `eas.yml` en push a `main`: quality → tag `v{version}-{YYYYMMDD.HHmm}` → build Android/iOS → submit Play + TestFlight. `GITHUB_TOKEN` con mínimo privilegio: `contents: read` por defecto; `contents: write` solo en el job de etiquetado de release (`eas.yml`). Dependabot (npm + github-actions): version updates a **`develop`**, sin majors ni patch/minor del stack Expo/RN (upgrade de SDK con `npx expo upgrade`), grupos prod/dev + security, cooldown. Política de vulnerabilidades en `SECURITY.md`. Secrets GitHub: `EXPO_TOKEN`, `GOOGLE_PLAY_SERVICE_KEY_JSON`. EAS `production`: `GOOGLE_SERVICES_JSON`, `SENTRY_AUTH_TOKEN`, **`EXPO_PUBLIC_SUPABASE_URL`** / **`EXPO_PUBLIC_SUPABASE_ANON_KEY`**, **`EXPO_PUBLIC_INVITE_HOST`**, **`EXPO_PUBLIC_TURNSTILE_SITE_KEY`**. iOS: `ascAppId` `6775626292`.
- **Legal / Play (may. 2026):** URLs públicas de privacidad y eliminación de cuenta vía **GitHub Pages** (`docs/` en `main`, carpeta `/docs`). Contacto de soporte / seguridad: `japenago@gmail.com`. Package Android: `com.javiwacho.musapp`; slug EAS `musapp`.
- **Partidas (may. 2026):** el creador puede cancelar partidas en `planned` e `in_progress` desde la ficha (no hace falta ser participante). En web, las confirmaciones destructivas (cancelar, abandonar, aprobar resultado) usan **modales** en lugar de `Alert.alert`, que no es fiable en Expo Web. **Empezar partida (jul. 2026):** el creador puede pasar una partida `planned` a `in_progress` manualmente (sin modal de confirmación; sí aviso si la plantilla está incompleta); se fija `start_at` al instante actual.
- **Plantilla mixta (may. 2026):** en crear/editar se pueden añadir compañeros/rivales **por nombre** además de cuentas registradas; las plazas (UI, explore y cron) cuentan texto + confirmados (máx. 2 por equipo). El creador puede registrar marcador **sin validación rival** solo si no hay otros participantes con cuenta y la partida está **`in_progress`** (`record_match_result_direct`). Tras aprobar un resultado rival, un trigger en BD confirma el resultado y finaliza la partida (`018`).
- **Eliminación de cuenta (may.–ago. 2026):** derecho de supresión RGPD vía Edge Function `delete-account`. Se borran auth, perfil, avatar y datos personales (reportes, cola de notificaciones). El **historial compartido se anonimiza**, no se elimina: referencias de partidas, **ligas y torneos** (creador, parejas, retos, grants) pasan al perfil interno **Usuario eliminado** (sentinel) o a texto «Usuario eliminado» (mig. `023`–`025`, `105`). CORS de la función: orígenes de producción + loopback local (`localhost` / `127.0.0.1`) para desarrollo web.
- **Recuperación de contraseña (jul. 2026):** el email de reset redirige a `jugamus://auth/update-password` (debe estar en Redirect URLs de Supabase). Pantalla dedicada para nueva contraseña; errores visibles en web (sin depender de `Alert.alert`); tras éxito, cierre de sesión y CTA a login. No reutilizar la misma contraseña (`same_password` → 422).
- **Recuperación de contraseña (jul. 2026):** el email de reset redirige a `jugamus://auth/update-password` (debe estar en Redirect URLs de Supabase). Pantalla dedicada para nueva contraseña; errores visibles en web (sin depender de `Alert.alert`); tras éxito, cierre de sesión y CTA a login. No reutilizar la misma contraseña (`same_password` → 422). La sesión de recovery **no** exige contraseña actual.
- **Política de contraseña (ago. 2026):** mínimo 8 caracteres con mayúscula, minúscula, dígito y símbolo (Auth dashboard + `authPasswordSchema` / `AUTH_PASSWORD_HINT`). Cambio de contraseña en Editar perfil exige la actual (`current_password`) y mantiene la sesión.
- **CAPTCHA Turnstile (ago. 2026):** desafío al pulsar enviar en login, registro y recuperación (no OAuth). Site key `EXPO_PUBLIC_TURNSTILE_SITE_KEY` (EAS `production`); secret y CAPTCHA activos en Supabase Auth. Expo Go: hostname `localhost`. Release nativo: `https://musapp-731e1.web.app/turnstile.html` (Firebase Hosting + rebuild con `react-native-webview`).
- **Notificaciones en perfil (may./jul./ago. 2026):** preferencias **push** («Todas») y por **evento** en perfil; sin notificaciones por correo; enlaces legales (términos, privacidad) en la misma pantalla. Eventos: unión; partida/torneo inicio, edición y cancelación (separados); resultado pendiente de validar; resultado pendiente de enviar (aviso ~5 h en curso); recordatorios 24 h y/o 2 h antes (chips multi-selección); **solicitudes de amistad** e **invitaciones a partidas** (`notify_on_friend_request` / `notify_on_match_invitation`, mig. `110`). `enqueue_notification` y `process-notifications` respetan `notify_push` + `notify_on_*` (migraciones `077`–`079`, `110`).
- **Branding (may. 2026):** icono y splash con diseño minimalista de baraja española (basto); color de fondo `#1a5f4a` en splash e icono adaptativo Android.
- **UI Ultra Limpio (may. 2026):** rediseño visual con tokens en `src/theme/` (fondo blanco, verde `#1A5F4A`, tipografía DM Sans). Listas principales (Mis partidas, Descubrir) con filas y punto de estado; previews con `ciudad · lugar`; cabecera Mis partidas sin contador de activas; FAB speed-dial encima de la tab bar; tab bar activa en verde brand.
Expand All @@ -40,7 +42,7 @@ App móvil para jugadores de mus en España que permite encontrar contrincantes
- **PostHog producto (jul./ago. 2026):** eventos `user_signed_up`, `match_created`, `match_joined`, `match_completed` (este último solo al pasar a `finished`, idempotente por `match_id`); `friend_request_sent`, `friend_request_accepted` (auto-aceptación), `match_invite_sent`, `match_invite_accepted` (v1.8). KPIs de panel deben usar estos eventos / `Application Opened`, no `$pageview`.
- **Torneos — cuadro interactivo (jul. 2026):** cada pareja en tarjeta propia; tap en pareja para registrar resultado y avanzar (modal rápido o ficha de partido). Etiquetas de ronda (cuartos, semifinal, final). Confirmación destructiva al organizar cuadro. Al cancelar torneo se cancelan **todos** los partidos del cuadro (`082`). Fix auto-cancel al poblar la final (`081`). Notificación «Validar resultado» solo si el resultado queda `pending_validation` (`083`).
- **Amigos e invitaciones a partidas (ago. 2026, v1.8):** solicitudes de amistad con mensaje opcional (máx. 200) desde perfil ajeno o búsqueda por nombre; cooldown tras rechazo reciente; lista de amigos/solicitudes en perfil propio. Invitar amigos a pareja/rival al crear o editar equipo (solo partida standalone `planned`/`in_progress` del creador); pendientes ocupan plaza; compartir vía modal en ficha tras crear. Mis Partidas: Invitaciones; ficha: banner Aceptar/Rechazar. Rechazar en partida iniciada/finalizada (o con resultado confirmado) cancela y voidea resultado; rivales invitados fuerzan `pending_validation`. Migraciones `108`–`116`.
- **Versión app (ago. 2026):** **1.8.0** (`app.json`, `package.json`). Incluye hotfix de calidad v1.7.1 (characterization tests, umbrales de cobertura ≥60%, proceso TDD en `docs/testing.md`).
- **Versión app (ago. 2026):** **1.8.1** (`app.json`, `package.json`). Incluye v1.8.0 (amigos/invites) y este hotfix: Turnstile al enviar login/registro/recuperación, política de contraseña (mayúscula, minúscula, dígito y símbolo), cambio de contraseña en perfil, mig. `118`/`119`.
- **Security hardening ligas/stats (ago. 2026, mig. `106`):** `process_league_lifecycle` solo vía pg_cron (REVOKE a PUBLIC/authenticated). `enqueue_player_stats_recompute` no ejecutable por clientes. `get_player_stats` recalcula ELO/agregados solo para el propio usuario o admin; el resto lee stats cacheadas (mitiga amplificación cross-user).
- **UI responsive (jul. 2026):** helpers `useResponsiveLayout` / `ScrollableModalBody` para escalado tipográfico y modales con scroll seguro en pantallas pequeñas.
- **Crear partida — UX (jul.–ago. 2026):** título, ciudad y lugar **opcionales** al crear (defaults: «Partida», «Ciudad por definir», «Lugar por definir» si vacíos). Sin toggle «Lugar por definir» en creación. Fecha/hora por defecto **+10 min** respecto a ahora. Botón ✕ cierra a **Mis partidas** (no `back` a Descubrir). Sin autofocus/teclado al abrir el formulario. Etiquetas de campo en negrita; sin asteriscos ni «(opcional)» en labels de crear/editar. **Empezar partida:** sin modal de confirmación (sí aviso si plantilla incompleta). Edición de partida conserva validación anterior. Aviso si plantilla incompleta (auto-cancel al llegar `start_at`). **Partida ya jugada (ago. 2026):** si `start_at` es anterior a ahora, el formulario muestra el marcador al final (casillas vacías; nombres de equipo derivados como en ficha); exige plantilla completa (nombres o amigos invitados); al crear registra resultado (directo o `pending_validation` si hay rivales invitados) y abre la partida. Orden de pareja/equipo en formularios y tarjetas: **integrantes primero, nombre después**. **Invitar amigos (v1.8):** selector de amigos como compañero/rival al crear; pestaña «Añadir amigo» en editar pareja.
Expand Down Expand Up @@ -99,16 +101,17 @@ Solo dos roles en el MVP:

- Login con Google
- Login con Apple ID (obligatorio por requisitos de App Store)
- Login con email y contraseña
- Login con email y contraseña (Turnstile al enviar; complejidad: mayúscula, minúscula, dígito y símbolo)
- Registro con aceptación de términos y política de privacidad
- Recuperación de contraseña para usuarios de email (pantalla `auth/update-password` tras el enlace; login con la nueva contraseña)
- Recuperación de contraseña para usuarios de email (pantalla `auth/update-password` tras el enlace; login con la nueva contraseña; Turnstile al enviar el email)
- Gestión de sesiones con JWT y refresh tokens (gestionado por Supabase Auth)
- Estado de sesión persistente entre cierres de la app
- Eliminación de cuenta (RGPD): borrado de identidad + anonimización del historial en partidas, ligas y torneos

### F2 - Perfil de usuario (Fase 1)

- Nombre a mostrar (obligatorio)
- Cambio de contraseña en edición de perfil (contraseña actual + nueva que cumple la política)
- Teléfono (obligatorio, validación formato E.164; en la app: selector de prefijo por país + validación genérica ITU-T, ej. `+34612345678` u otros países del listado)
- Localidad/pueblo (opcional, informativo)
- Foto de perfil (opcional, comprimida automáticamente a ≤ 500KB)
Expand Down Expand Up @@ -569,6 +572,7 @@ Supabase (PostgreSQL + Auth + Storage)
- TLS extremo a extremo
- JWT con expiración corta + refresh tokens
- Rate limiting por IP y usuario (Supabase + Edge Functions)
- **Cloudflare Turnstile** en login, registro y recuperación de contraseña (site key en el cliente; secret en Auth)
- Protección CSRF/XSS/SSRF, validación exhaustiva de inputs
- Row Level Security (RLS) de PostgreSQL para aislar datos por usuario
- **Hardening (may. 2026, migraciones 038–047):**
Expand Down Expand Up @@ -633,6 +637,9 @@ Supabase (PostgreSQL + Auth + Storage)
- CA_AUTH2: Un usuario puede autenticarse con Google
- CA_AUTH3: Un usuario puede autenticarse con Apple ID
- CA_AUTH4: Un usuario puede recuperar su contraseña por email, fijar una nueva en `auth/update-password` e iniciar sesión después
- CA_AUTH4b: Login, registro y «olvidé contraseña» piden Turnstile al pulsar enviar (no al abrir la pantalla; no aplica a Google/Apple)
- CA_AUTH4c: El registro y el cambio de contraseña rechazan secretos sin mayúscula, minúscula, dígito y símbolo
- CA_AUTH4d: En Editar perfil, cambiar la contraseña exige la actual y no cierra la sesión
- CA_AUTH5: La sesión persiste entre cierres de la app (refresh token); tras ≥6 h en segundo plano, al volver a primer plano se cierra la sesión con aviso
- CA_AUTH6: Un usuario con `status: suspended` no puede iniciar sesión
- CA_AUTH7: Un usuario puede eliminar su cuenta; se borra la identidad y se anonimiza su huella en partidas, ligas y torneos (perfil sentinel «Usuario eliminado»)
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Include as much detail as you can:

In scope:

- Authentication / session issues (including OAuth)
- Authentication / session issues (including OAuth and CAPTCHA/Turnstile bypass)
- Unauthorized data access (RLS / API / Edge Functions)
- Injection, XSS, or similar client/server flaws
- Secrets exposure in the repo or client builds
Expand Down
Loading
Loading