Sostituisce i due bucket Supabase Storage con campi file PocketBase:
- avatar-giocatori: nuova collection avatar_giocatori separata da
giocatori_squadra, perché la policy originale ("chiunque autenticato
carica/sostituisce/elimina qualsiasi avatar, nessun controllo
proprietario") è più permissiva delle regole di giocatori_squadra —
mescolarle avrebbe indebolito le une o bloccato l'altra. File pubblico,
come il bucket originale;
- profili-giocatore: campi file su profili_giocatore stesso, con
protected: true (vedi sotto).
Trovato e corretto un problema di sicurezza reale: PocketBase rende i
file pubblici di default (si affida solo alla casualità del nome file),
a meno di impostare esplicitamente protected: true sul campo — i
documenti d'identità e i certificati medici sarebbero stati raggiungibili
senza autenticazione, in violazione di DD-016 regola 4. Verificato che
ora servono un token valido (404 senza, 200 con).
La scrittura dei campi testuali del profilo e il caricamento dei file
sono due operazioni separate (PocketBase rifiuta una stringa dove si
aspetta un file): verificato che caricare un documento non cancella i
dati testuali già salvati.
Rimossi da profili-core.ts RigaProfilo/daRigaProfilo/aRigaProfilo
(shape Postgres non più usata da nessun modulo di produzione) e
rimuoviFile (mai chiamato da nessun componente). Avatar.tsx non usa più
un URL deterministico per giocatore: PocketBase genera nomi file
casuali, quindi legge la mappa avatar tramite una query React Query
condivisa tra tutte le istanze del componente.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
28 lines
940 B
TypeScript
28 lines
940 B
TypeScript
/**
|
|
* Check dell'avatar giocatore: `bun test/unit/avatar-store.test.ts`.
|
|
* `urlAvatarDaRiga` è puro (il campo non è protetto, nessuna richiesta di rete): qui
|
|
* verifichiamo solo la forma dell'URL a partire da una riga finta, non serve un database.
|
|
*/
|
|
import assert from "node:assert/strict";
|
|
import { urlAvatarDaRiga } from "@/lib/avatar-store";
|
|
|
|
const riga = { id: "rec1", giocatore: "g1", foto: "avatar_abc123.jpg" };
|
|
|
|
const url = urlAvatarDaRiga(riga);
|
|
|
|
assert.ok(url.includes("avatar_giocatori"), "punta alla collection degli avatar");
|
|
assert.ok(url.includes("rec1"), "contiene l'id del record");
|
|
assert.ok(url.includes("avatar_abc123.jpg"), "contiene il nome del file");
|
|
assert.equal(
|
|
urlAvatarDaRiga(riga),
|
|
url,
|
|
"deterministico a parità di riga: nessuna chiamata di rete",
|
|
);
|
|
assert.notEqual(
|
|
urlAvatarDaRiga({ ...riga, foto: "altro.jpg" }),
|
|
url,
|
|
"file diverso -> URL diverso",
|
|
);
|
|
|
|
console.log("avatar-store: ok");
|