Modello Dati
Lo schema del portale è definito in supabase/migrations/. RLS è abilitata su tutte le tabelle relative a contributor, documenti, sessioni, compensation, payout, ricevute, cap, eccezioni e audit.
Ordine delle Migrazioni
20260430233000_initial_portal_schema.sql
20260501133000_add_onboarding_document_acceptance_evidence.sql
20260501193000_add_validation_rewards_payout_flow.sql
20260501194500_add_playroll_time_milestones.sql
20260501200000_add_payout_receiving_methods.sql
20260501201000_lock_payout_receiving_method_confirmation.sql
20260501204000_validate_payout_receiving_iban.sql
20260511161000_add_contributor_app_links.sql
Shortcut in package.json:
npm run deploy:schema
npm run deploy:schema:validation-payout
npm run deploy:schema:time-milestones
npm run deploy:schema:payout-receiving
npm run deploy:schema:payout-iban-validation
Tabelle Scritte dal Browser
public.applications
Scritta da insertVerifiedApplication.
Campi principali:
auth_user_id
legal_name
email
email_verified_at
city
university
discord_handle
weekly_availability
notes
privacy_notice_version
referral_code
source
status
metadata.city
metadata.university
Confine di sicurezza:
- solo insert autenticate
auth_user_id = auth.uid()- l'email inviata deve coincidere con l'email verificata su Supabase
email_verified_atdeve essere presente- il contributor può leggere solo le proprie righe
public.onboarding_submissions
Scritta da insertOnboardingSubmission.
Campi principali:
auth_user_id
application_id
invite_code
legal_name
email
city
contact_handle
tax_status
accepted_documents
document_acceptance_evidence
metadata.city
metadata.sensitive_data_collection
metadata.payout
metadata.recorder_access
La tabella ha ancora colonne di compatibilità per date_of_birth, residential_address, codice_fiscale, account_holder_name e iban. Il portale attualmente visibile non raccoglie questi valori prima dell'accesso al recorder.
public.payout_receiving_methods
Scritta da savePayoutReceivingMethod (INSERT, più UPDATE a livello di colonna su account_holder_name/manual_review_note/metadata). Le colonne status, email_confirmation_status, email_confirmed_at e confirmation_token sono scrivibili soltanto attraverso le RPC SECURITY DEFINER mint_payout_confirmation_token(p_method_id) e confirm_payout_receiving_method_with_token(p_token) (audit 2026-05-16, EC-231 M1+M2).
Campi principali:
auth_user_id
verified_email
method_type
account_holder_name
iban
iban_last4
manual_review_note
email_confirmation_status
email_confirmation_sent_at
email_confirmed_at
status
confirmation_token
confirmation_token_expires_at
metadata
Metodi consentiti:
sepa_iban
manual_review
Stati operativi:
pending_email_confirmation
confirmed
manual_review
disabled
Le funzioni di database normalizzano e validano gli IBAN SEPA supportati:
public.normalize_iban(value text)
public.is_supported_sepa_iban(value text)
public.normalize_payout_receiving_method_iban()
La policy di insert richiede email_confirmation_status = pending e limita gli stati inseribili:
method_type = sepa_iban -> status = pending_email_confirmation
method_type = manual_review -> status = manual_review
Gli update sono consentiti soltanto finché la riga attiva dell'utente è ancora pending_email_confirmation o manual_review.
Tabelle di Reward e Payout
Lo schema include il modello di reward e di pagamento a valle, ma il portale attuale non lo gestisce interamente dal browser.
Source of truth della validazione:
public.session_validation_results
Regola importante:
public.session_validation_results.status = validated
La validazione non deve calcolare direttamente gli EUR pagabili. payable_eur è deprecato e deve rimanere a 0.
Source of truth del time-credit:
public.playroll_time_ledger
Source of truth dei milestone:
public.reward_milestones
public.contributor_milestone_unlocks
Source of truth dei valori:
public.compensation_items
Vista del saldo denaro/reward:
public.contributor_reward_balances
Vista di esportazione del receiving-method:
public.confirmed_payout_receiving_methods
Viste Principali
Viste di saldo visibili al contributor:
public.contributor_playroll_time_balances
public.contributor_milestone_progress
public.contributor_reward_balances
Queste viste sono create con security_invoker = true e concesse agli utenti authenticated. La RLS continua a controllare la visibilità delle righe attraverso la relazione contributor/user sottostante.
Identity Link con l'App Playroll
La tabella del portale che funge da source per il ponte app Playroll/contributor è:
public.contributor_app_links
Campi importanti:
id
contributor_id
portal_auth_user_id
app_project
app_user_id
steam_id
steam_username
status
intent_issued_at
intent_expires_at
intent_nonce
pending_expires_at
linked_at
revoked_at
Stati:
pending_activation
active
revoked
Vincoli e indici garantiscono:
one active link per app_project + app_user_id
one active link per app_project + contributor_id
one pending link per app_project + app_user_id + portal_auth_user_id
one recorded intent_nonce per app_project
Helper di attivazione:
public.has_completed_contributor_activation_gate()
public.ensure_current_contributor()
Il gate di attivazione richiede tutte le accettazioni dei documenti necessarie e un receiving-method SEPA IBAN confermato. manual_review non è sufficiente per l'attivazione automatica.
Il progetto dell'app Playroll possiede la proiezione di lettura:
playroll_supa_new.public.contributor_status_projection
Campi importanti:
app_user_id
contributor_id
link_status
source_link_id
steam_id
linked_at
source_updated_at
last_synced_at
La RLS consente agli utenti authenticated dell'app di leggere solo la propria riga di proiezione. La proiezione è volutamente minimale e non deve contenere evidenze di documenti legali, dati IBAN, dettagli del metodo di payout o altre PII del portale.
Helper RLS
La migrazione iniziale definisce:
public.current_contributor_id()
public.is_ec_domain_user()
public.current_contributor_id() supporta l'accesso own-row del contributor. public.is_ec_domain_user() è riservato per una futura admin surface e non deve essere usato per limitare l'accesso alla pagina pubblica di candidatura.
Confine Admin/Service-Role
Le seguenti operazioni non devono essere di proprietà del browser:
- creazione di record canonici di contributor al di fuori di
ensure_current_contributor()dopo il gate di attivazione - assegnazione di sessioni del recorder
- scrittura dei risultati di validazione
- creazione di voci nel PlayRoll time ledger
- sblocco di milestone
- creazione di compensation item a partire dai milestone
- creazione di richieste di payout
- approvazione e invio dei pagamenti
- generazione di ricevute o value sheet
- calcolo degli snapshot di cap
- risoluzione delle eccezioni