0
Utilisateurs
76 entrées UserPII
0
Événements
77 actifs · 10 propositions
0
Inscriptions
EventsRegistration
0
Messages forum
MessageForum
0
Notifications
Push envoyées
0
Logs système
system_logs
0
Relations users
User2User
0
Memberships
User2Team
🏗️
Aperçu de la stack Supabase
Projet vpqcodafosjurzttxhvx · Créé 27/04/2024
Base de données
🗄️
28 tables PostgreSQL, 10 vues SQL complexes avec logique métier, RLS activé sur 20/28 tables, 18 triggers actifs.
PG 15.828 tables10 vues
Authentification
🔐
Auth Supabase + mapping via
AuthMember view. Fonction current_member_uuid() utilisée dans toutes les RLS policies. 76 users actifs.JWTauth.uid()76 users
Automation
⚡
4 cron jobs pg_cron à 3h/jour + 1 chaque heure. 2 triggers DB → BuildShip webhook sur
notifications. 10 edge functions Supabase actives.4 crons10 edge fnBuildShip
⚠️ Points d'attention
🔍
8 tables sans RLS · Policies
debug_allow_all_select actives en prod · Table notifications (3035 lignes) sans RLS · Doublons de policies sur Events/User.8 tables sans RLSdebug policies
Stripe Integration
💳
Webhook Stripe →
system_logs. 84 événements Stripe loggés (54 info + 30 success + 5 erreurs). Champ premium_status sur User, reset par cron quotidien.Stripe webhook84 events5 errors
Données PII
🛡️
Séparation propre User / UserPII. Policies RLS granulaires : soi-même, contacts (status=1), admin. Email/phone/dob exposés conditionnellement dans
UserContactView.RGPD-readyRLS strict
📊
Activité & Distribution des données
Données extraites en live
Distribution — Statuts d'inscription (EventsRegistration · 1 097 entrées)
invited
572
52.1%
present
357
32.5%
absent
157
14.3%
waiting
5
0.5%
null
6
0.5%
Distribution — Statuts événements (87 événements)
actif (1)
77
88.5%
proposition (0)
10
11.5%
⚠️ Aucun événement récurrent actif (is_recurring=true) — les crons generate_recurring_events_daily ne génèrent rien actuellement.
Logs système (287 entrées) — par type & sévérité
cron/info
167
58.2%
stripe/info
54
18.8%
stripe/success
30
10.5%
Stripe/info
14
4.9%
StripeWebhook
13
4.5%
Stripe/error
5
1.7%
⚠️ Incohérence de casse : stripe, Stripe, StripeWebhook → préférer un event_type normalisé pour faciliter les requêtes analytiques.
Croissance utilisateurs — 12 derniers mois (total: 76)
Oct 2024: 2
Nov 2024: 2
Jan 2025: 1
Mar 2025: 1
Juin 2025: 2
Juil 2025: 1
Août 2025: 2
Sept 2025: 23 ⬆️ Pic
Oct 2025: 19
Nov 2025: 5
Déc 2025: 1
Jan 2026: 6
📈 Pic d'acquisition en septembre 2025 (23 users) et octobre 2025 (19 users). Ralentissement en fin d'année. Reprise en janvier 2026.
📋
Volume par table
Toutes les tables · triées par lignes
| Table | Lignes | Colonnes | RLS | Domaine |
|---|---|---|---|---|
| notifications | 3 035 | 9 | ❌ OFF | Push |
| EventsRegistration | 1 097 | 10 | ✅ ON | Events |
| MessageForum | 514 | 7 | ✅ ON | Messagerie |
| system_logs | 287 | 10 | ❌ OFF | Admin |
| User2User | 243 | 11 | ✅ ON | Contacts |
| User2Team | 176 | 8 | ✅ ON | Équipes |
| MessageForumRead | 98 | 7 | ❌ OFF | Messagerie |
| Events | 87 | 32 | ✅ ON | Events |
| User | 76 | 23 | ✅ ON | Auth |
| UserPII | 76 | 5 | ✅ ON | Auth/PII |
| feature_usage_log | 60 | 5 | ❌ OFF | Abonnements |
| Place | 21 | 13 | ✅ ON | Lieux |
| cities | 11 | 3 | ❌ OFF | Référentiel |
| EventStats | 10 | 14 | ✅ ON | Events |
| User2Place | 5 | 4 | ✅ ON | Lieux |
| Team2Place | 4 | 4 | ✅ ON | Équipes |
| subscription_features | 2 | 7 | ✅ ON | Subs |
| AppVersioning | 1 | 5 | ✅ ON | Système |
| teams | 1 | 8 | ✅ ON | Équipes |
| Activity · Leagues · PlayerStats · PlayerVotes · PublicNews · Team2Event · VoteCategory · message · subscription · subscription_access | 0 | — | — | Non peuplées |
⚠️ Plusieurs tables clés non peuplées :
Activity, subscription, subscription_access, PlayerStats, PlayerVotes, teams (1 seule équipe) — indique que certains modules sont en cours d'activation ou pas encore utilisés.🗃️
Tables & Schéma détaillé
28 tables · schéma public
Cliquer sur une table pour voir/masquer ses colonnes.
User
76 lignes
RLS ON
Entité centrale
▼
Member_UUID PK
uuid
unique gen_random_uuid()
User_UUID
uuid
= auth.uid()
name / LastName
text
default: [NO NAME]
email / phone
text
⚠️ Dupliqué dans UserPII
fcm_token / fcm_last_date
text / timestamptz
Push notifications
premium_status / premium_plan / premium_until
bigint / text / timestamp
Abonnement Stripe
stripe_customer_id / stripe_subscription_id
text
IDs Stripe
admin_uuid
uuid
→ User.Member_UUID (parent/tuteur)
Activities_id / city_ids
array int8 / array uuid
Préférences
Build / status
bigint / boolean
Version app · compte actif
UserPII
76 lignes
RLS ON
RGPD
▼
member_uuid PK/FK
uuid
→ User.Member_UUID (1:1)
email / phone
text
Données sensibles isolées
dateofbirth
date
Sensible — RLS strict
updated_at
timestamptz
Auto-updated
Events
87 lignes
RLS ON
32 colonnes
▼
UID unique
uuid
Clé de référence
id PK
bigint
identity
status
int4
0=prop · 1=actif · 2=annulé · 3=template · 4=récurrent
start / stop / meeting_time
timestamptz
Horodatages événement
is_recurring / model_event_uuid / byWeekDay
bool / uuid / text[]
Récurrence — actuellement non utilisé
teamsIDs / teamsOpponentsIDs
uuid[]
Équipes participantes
Max_players / Level / Mindset
bigint / int4 / text
Paramètres match
IsOpenEvent / payment_link / Price
bool / text / float8
Événements payants
reminder
bigint
default: 24h · check > 0
Admin_Member_UID
uuid
→ User.Member_UUID
EventsRegistration
1 097 lignes
RLS ON
Table principale
▼
uid PK
uuid
event_uid
uuid
→ Events.UID
contact_uid
uuid
→ User.Member_UUID
registrationStatus
text
present · invited · absent · waiting
External_Registration_Status
text
Pour invités externes
status_order
int4
Priorité · géré par trigger
start
timestamptz
Copié depuis Events.start via trigger
User2User
243 lignes
RLS ON
Graphe social
▼
user1_ID / user2_ID
uuid
→ User × User
status
bigint
1 = contact confirmé (utilisé dans RLS)
IsBlocked / IsDisplayed
boolean
Contrôle visibilité
IsAutorityParent
boolean
Relation parent/tuteur
favorite_level
bigint
Niveau de favori
notifications
3 035 lignes
RLS OFF
⚠️ Exposition totale
▼
user_uuid
uuid
→ User.Member_UUID
title / message
text
is_read
boolean
default: false
route / params / linked_uuid
text
Deep-link navigation
⚠️ RLS désactivé — tout utilisateur authentifié peut lire/modifier les notifications de n'importe quel user_uuid. À corriger en priorité.
👁️
Vues SQL
10 vues — complexité élevée
| Vue | Complexité | Rôle | Auth-aware |
|---|---|---|---|
| AuthMember | Simple | Mappe auth.uid() → Member_UUID. Utilisée dans RLS. | ✅ auth.uid() |
| whoami | Simple | Debug — retourne jwt_role, auth_uid, member_uuid du user courant. | ✅ |
| UserPublicView | Simple | Profil public : Member_UUID, name, photo_url, admin_uuid, ActivityIDs. | — |
| user_view | Moyenne | Profil complet admin avec sous-requête JSON des équipes imbriquées. | ✅ |
| UserContactView | Très complexe | Vue contacts enrichie : statut relation, PII exposées conditionnellement, teams communes, âge public/privé, card_number. | ✅ Conditionnelle |
| user2team_view | Moyenne | Membership enrichi avec nom/image équipe, rôle, compteur messages non lus du forum. | ✅ |
| user2event | Simple | EventsRegistration + user_details JSON (name, photo, admin_uuid). | — |
| events_full_details | Très complexe | Vue maîtresse des événements : 48 colonnes, scores, statuts, comptes participants, forum non lus, liens Google/Apple Calendar, statut admin/membre. | ✅ Conditionnelle |
| MessageForumView | Moyenne | Messages forum enrichis : sender_name/photo, is_read, is_after_join. | ✅ |
| place_view | Simple | Lieux avec ville, IsFavorite calculé selon l'utilisateur courant. | ✅ |
| user_feature_usage | Très complexe | Calcule les droits d'accès aux features selon premium_status : used/remaining/has_access par feature_key. | — |
| user_feature_usage_json_cache | Simple | Agrège user_feature_usage en JSON par utilisateur (cache lecture rapide). | — |
La vue
events_full_details est la plus critique — elle génère les liens Google Calendar et Apple Calendar (.ics) à la volée. Elle contient des sous-requêtes corrélées qui peuvent impacter les performances à grande échelle.⚠️ La vue
UserContactView expose les données PII (email, phone, dateofbirth) de manière conditionnelle dans la même requête. La logique CASE WHEN is_contact est correcte mais complexe à maintenir.⚡
Triggers
18 triggers actifs
Les triggers assurent la cohérence des données automatiquement — propagation de dates, synchronisation PII, compteurs, dispatch notifications.
Table: User (3 triggers)
User
INSERT/UPDATE
AFTER
_sync_user_to_pii() — Synchronise email/phone/dob vers UserPII à chaque modification
User
UPDATE
BEFORE
forbid_change_user_keys() — Empêche la modification des clés primaires (Member_UUID, User_UUID)
User
UPDATE
BEFORE
moddatetime('updated_at') — Mise à jour automatique du timestamp
Table: EventsRegistration (4 triggers)
EventsReg.
INS/UPD
BEFORE
er_fill_start_from_event() — Copie Events.start dans EventsRegistration.start pour tri rapide
EventsReg.
INS/UPD
BEFORE
update_order_priority() — Calcule status_order pour l'affichage priorisé des inscrits
EventsReg.
UPDATE
AFTER
trg_eventsregistration_log_open_join() — Logge les inscriptions sur événements ouverts (IsOpenEvent)
EventsReg.
UPDATE
BEFORE
moddatetime('updated_at')
Table: Events (1 trigger)
Events
UPDATE
AFTER
events_propagate_start_to_reg() — Propage Events.start vers toutes les EventsRegistration liées
Table: MessageForum (2 triggers)
MessageForum
INS/UPD
BEFORE
sync_messageforum_ids() — Synchronise teamID/EventID entre les formats text et uuid
Table: notifications (2 triggers → BuildShip)
notifications
INSERT
AFTER
→ BuildShip Workflow 1 — HTTP POST vers fus88z.buildship.run (workflow 9dOqNJGu…) · timeout 5s
notifications
INSERT
AFTER
→ BuildShip Workflow 2 — HTTP POST vers fus88z.buildship.run (workflow Ied4rei6…) · timeout 5s · 2 workflows déclenchés sur chaque INSERT
⚠️ Chaque INSERT dans
notifications déclenche 2 appels BuildShip simultanément. Vérifier si les deux workflows ont des rôles distincts ou si l'un est obsolète.Autres
User2Team
DELETE
AFTER
app.delete_team_if_no_admins_after_admin_row_deleted() — Supprime l'équipe si le dernier admin est retiré
Activity / User2User
UPDATE
BEFORE
storage.update_updated_at_column() — Mise à jour updated_at
⚙️
Fonctions SQL (21)
PL/pgSQL · SQL · C (moddatetime)
Fonctions Trigger (retournent trigger)
| Fonction | Langage | Rôle |
|---|---|---|
| _sync_user_to_pii | PLPGSQL | Synchronise User.email/phone/dob → UserPII après INSERT/UPDATE |
| adusersteams | PLPGSQL | Gère la promotion/rétrogradation des admins dans les équipes |
| er_fill_start_from_event | PLPGSQL | Copie Events.start → EventsRegistration.start sur INSERT/UPDATE |
| events_propagate_start_to_reg | PLPGSQL | Propage le changement de date d'un événement vers toutes ses inscriptions |
| forbid_change_user_keys | PLPGSQL | Lève une exception si tentative de modification des UUID clés d'un user |
| moddatetime | C | Extension PostgreSQL — met à jour automatiquement le champ updated_at |
| sync_messageforum_ids | PLPGSQL | Normalise teamID/EventID (conversion text ↔ uuid) dans MessageForum |
| trg_eventsregistration_log_open_join | PLPGSQL | Logge dans feature_usage_log les inscriptions sur événements ouverts |
| update_c_participants | PLPGSQL | Mise à jour du compteur de participants (potentiellement dénormalisé) |
| update_order_priority | PLPGSQL | Calcule status_order pour priorité d'affichage des inscriptions |
Fonctions Utilitaires
| Fonction | Retour | Langage | Description |
|---|---|---|---|
| current_member_uuid | uuid | SQL | Retourne le Member_UUID de l'utilisateur connecté via auth.uid(). Utilisée dans toutes les policies RLS. |
| notify_dispatcher | integer | PLPGSQL | Dispatcher centralisé de notifications — insère dans la table notifications et déclenche les webhooks |
| update_user_fcm | boolean | PLPGSQL | Met à jour le token FCM d'un utilisateur, appelée depuis l'app via Dart action |
| reset_expired_premium | void | PLPGSQL | Remet premium_status à 0 pour les users dont premium_until est dépassé. Appelée par cron 3h/jour. |
| generate_next_event_occurrences | void | PLPGSQL | Génère la prochaine occurrence d'un événement récurrent spécifique |
| generate_all_event_occurrences | void | PLPGSQL | Génère toutes les occurrences de tous les événements récurrents actifs |
| clean_orphan_user2team | void | PLPGSQL | Supprime les relations User2Team sans User ou Team correspondant |
| clean_orphan_user2team_uuid | integer | PLPGSQL | Variante UUID — retourne le nombre de lignes nettoyées |
| clean_orphan_user2user_by_memberuuid | integer | PLPGSQL | Supprime les relations User2User dont l'une des deux parties n'existe plus |
| clean_unused_user2team | integer | PLPGSQL | Supprime les memberships inactifs (status != 1) depuis longtemps |
| list_public_columns | record | SQL | Introspection — liste les colonnes accessibles publiquement |
⏰
Cron Jobs & Edge Functions
4 crons pg_cron · 10 edge functions
Cron Jobs (pg_cron)
| Job | Schedule | Action | Impact actuel |
|---|---|---|---|
| generate_recurring_events_daily | 0 3 * * * | Appelle generate_next_event_occurrences(uid) pour chaque événement récurrent |
⚠️ Aucun événement récurrent actif — cron tourne à vide |
| generate_all_occurrences_daily | 0 3 * * * | Appelle generate_all_event_occurrences() |
⚠️ Idem — doublon avec le job précédent |
| reset_expired_premium_job | 0 3 * * * | Appelle reset_expired_premium() — remet premium_status=0 si premium_until dépassé |
✅ Actif — important pour la gestion Stripe |
| send_event_reminders_job | 0 * * * * | Appelle edge function send_event_reminders via HTTP POST avec JWT service_role |
✅ Actif — rappels chaque heure |
⚠️ Le JWT service_role est hardcodé dans le cron
send_event_reminders_job. Ce token expire en 2029 mais est visible dans la table pg_cron. Il devrait être stocké dans supabase_vault (extension installée).Edge Functions (10 actives)
| Nom | JWT requis | Rôle | Déclencheur |
|---|---|---|---|
| send-invite-notification | ✅ JWT | Envoie une notification d'invitation à un utilisateur | App |
| send_event_reminders | ✅ JWT | Envoie les rappels d'événements (24h avant) à tous les participants | Cron / 1h |
| delete-my-account | ✅ JWT | Suppression RGPD du compte utilisateur (cascade) | App |
| super-task | ✅ JWT | Tâche générique admin (usage à documenter) | Admin |
| stripe_webhook | ❌ Pub | Réception des webhooks Stripe (paiements, abonnements) | Stripe |
| cancel_subscription | ❌ Pub | Annulation d'un abonnement Stripe | App/Stripe |
| create_checkout_session | ❌ Pub | Création d'une session de paiement Stripe Checkout | App |
| redirect_success | ❌ Pub | Redirection après paiement réussi (callback URL Stripe) | Stripe |
| redirect_cancel | ❌ Pub | Redirection après annulation de paiement | Stripe |
| generate_ics | ❌ Pub | Génère un fichier .ics pour l'export calendrier Apple/iOS | App/View |
Les edge functions Stripe (
stripe_webhook, cancel_subscription, create_checkout_session) sont sans JWT — c'est normal car Stripe ne peut pas s'authentifier avec un JWT Supabase. La sécurité repose sur la vérification de la signature Stripe (stripe-signature header).🔒
Politiques RLS
41 policies · analyse sécurité
🚨
CRITIQUE — Table notifications sans RLS (3 035 lignes)
Tout utilisateur authentifié peut lire et modifier toutes les notifications de tous les utilisateurs. Ajouter une policy RLS :
user_uuid = current_member_uuid()
⚠️
Policies debug actives en production
debug_allow_all_select est actif sur User2User, User2Team, et EventsRegistration. Ces policies permettent un SELECT total pour les rôles authenticated et anon. À supprimer ou désactiver en prod.
⚠️
8 tables sans RLS
notifications · system_logs · MessageForumRead · cities · feature_usage_log · PlayerVotes · VoteCategory · MessageForumRead. Certaines (cities, VoteCategory) sont des référentiels en lecture seule — acceptable. D'autres (feature_usage_log, MessageForumRead) contiennent des données utilisateur.
⚠️
Doublons de policies sur Events
La table Events a 3 policies dont 2 contradictoires :
all (qual: true) autorisant tout en lecture ET Enable read access for all users — redondance. La policy all rend les autres inutiles.
✅
UserPII — Modèle RLS exemplaire
4 policies bien conçues : soi-même (all), contacts confirmés (status=1), admin (via admin_uuid), insert admin. La logique de confidentialité des données personnelles est correctement implémentée.
✅
current_member_uuid() — Bonne pratique
La fonction SQL
current_member_uuid() est utilisée de manière cohérente dans toutes les policies, évitant les appels directs à auth.uid() dans les WHERE et assurant la traçabilité du mapping auth → member.
Détail des policies par table
UserPII (4 policies — modèle de référence)
pii_self_all
ALL
authenticated
member_uuid = current_member_uuid()
pii_contacts_read
SELECT
authenticated
EXISTS contact (status=1) entre current_member et target
pii_admin_read
SELECT
authenticated
User.admin_uuid = current_member_uuid()
pii_admin_write_update
UPDATE
authenticated
User.admin_uuid = current_member_uuid()
User (8 policies — avec doublons)
user_select_all
SELECT
authenticated
true — Lecture libre
user_self_update
UPDATE
authenticated
User_UUID = auth.uid()
user_update_self_or_admin
UPDATE
authenticated
User_UUID = auth.uid() OR admin_uuid = AuthMember
user_admin_update
UPDATE
authenticated
⚠️ admin_uuid = current_member_uuid() — doublon avec user_update_self_or_admin
User2User (3 policies)
read_my_links
SELECT
authenticated
user1_ID = me OR user2_ID = me — Correct
debug_allow_all_select
SELECT
anon + authenticated
⚠️ true — Policy debug active! Expose tout le graphe social
Policy with security definer
ALL
public
true — Permissive totale
📋
Audit & Recommandations
Synthèse des points d'amélioration
🚨
Priorité 1 — Sécurité
| # | Problème | Table/Objet | Action recommandée |
|---|---|---|---|
| P1 | RLS désactivé sur notifications (3 035 lignes) | notifications | Activer RLS + policy user_uuid = current_member_uuid() |
| P2 | debug_allow_all_select actif en prod | User2User, User2Team, EventsRegistration | Supprimer ces 3 policies (ou les désactiver) |
| P3 | JWT service_role hardcodé dans pg_cron | send_event_reminders_job | Déplacer le JWT dans supabase_vault (extension déjà installée) |
| P4 | email/phone dupliqués dans User ET UserPII | User | Supprimer les colonnes email/phone de User, les lire uniquement via UserPII |
⚡
Priorité 2 — Performance
| # | Problème | Objet | Recommandation |
|---|---|---|---|
| P1 | Vue events_full_details — sous-requêtes corrélées multiples | events_full_details | Ajouter des index sur EventsRegistration(event_uid, registrationStatus), MessageForum(EventID), MessageForumRead(memberUID, EventID) |
| P2 | Double webhook BuildShip sur chaque notification | notifications triggers | Vérifier si les 2 workflows ont des rôles distincts. Potentiellement supprimer l'un des deux. |
| P3 | 2 crons redondants pour les événements récurrents (qui ne génèrent rien) | pg_cron jobs 1 & 2 | Consolider en un seul cron ou désactiver jusqu'à activation de la récurrence |
🧹
Priorité 3 — Maintenabilité
| # | Problème | Recommandation |
|---|---|---|
| M1 | Incohérence de casse dans system_logs.event_type (stripe, Stripe, StripeWebhook) | Normaliser avec un ENUM ou un CHECK constraint |
| M2 | Policies en doublon sur User (user_admin_update vs user_update_self_or_admin) | Consolider en une seule policy |
| M3 | Colonnes teamID/EventID en text dans MessageForum (au lieu de uuid) | Migrer vers uuid — sync_messageforum_ids est un workaround |
| M4 | team2_event table vide (0 lignes) avec seulement 2 colonnes (id, created_at) | Compléter le schéma ou supprimer si remplacée par Events.teamsIDs[] |
| M5 | Pas de migrations CLI enregistrées | Commencer à versionner les changements de schéma avec supabase db diff pour traçabilité |