longevity: accesso del cliente creato dal gestionale #8
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
COSA: ogni cliente del centro è un utente (mail + password). Oggi l'account si crea con scripts/create-user.mjs e si lega con scripts/crea-fascicolo.ts. Il gestionale deve poter creare l'utente con ruolo cliente e legarlo al fascicolo, mostrando la password una volta.
FATTO QUANDO: da un fascicolo senza accesso si crea l'utente e il cliente vede il suo referto; test.
🤖 goldrake — preso in carico 2026-09-26T12:53:30Z
ramo:
lavoro/goldrake· capacità: docker, pubblica, sempre-accesa, systemdESITO: richiesta
RIF: gitea:Adriano/InsanityLab-web#8
COSA: analisi (passo 1). Dal gestionale si crea l'account del cliente (ruolo
cliente) e lo si lega al fascicolo; password mostrata una volta.RAMO: lavoro/goldrake-i8 — worktree goldrake:~/lavoro/InsanityLab-web-i8 da
af8afeb.Stato verificato
lib/db.tsgetDb);lib/auth.ts:createUser(db, username, password, role),randomPassword(),deleteUser,ROLESincludecliente.identity.clienti.user_id(indice UNIQUE), scritto oggi solo dacreaClientee dascripts/crea-fascicolo.ts.lib/longevity/anagrafica.tsè l'unico modulo che apre identity (singleton privatogetIdentityDb). Testtests/longevity/anagrafica.test.ts: fuori da anagrafica.ts e db.ts nessun file .ts/.astro contiene insieme «Identity» e «Longevity»./longevity/gestionale/*riservato a admin e trainer (auth.ts RULES).Modifiche
anagrafica.ts, accanto alle esistenti e nello stesso stile (variante con parametro per i test + variante sul singleton, comecodicePerUtente/codicePerUtenteLoggato):accessoDi(identity, code)/accessoDelCliente(code)→{ userId: number | null; email: string | null } | null(null = codice inesistente)collegaAccesso(identity, code, userId)/collegaAccessoCliente(code, userId)→ boolean:UPDATE clienti SET user_id = ? WHERE client_code = ? AND user_id IS NULL, true se changes = 1lib/longevity/accesso.ts:creaAccessoCliente(deps, { code, username })condeps = { utenti: Database, accessoDi, collega }iniettabili per i test (default: getDb e le varianti sul singleton). Ordine: username normalizzato minuscolo e valido (^[a-z0-9._@-]{3,64}$); fascicolo esistente e senza accesso (controllo prima di creare l'utente); username libero;createUser(..., randomPassword(), 'cliente');collega; se collega fallisce o lancia →deleteUser(compensazione, due DB = nessuna transazione unica, stesso schema dicreaCliente) ed errore. Esito{ ok: true; username; password } | { ok: false; errore }. La password non si scrive in log.pages/longevity/gestionale/cliente/[code]/accesso.astro, layoutLongevityconvariante="gestionale"(la variante esiste come prop; lo stile arriva con #2). GET: se il fascicolo ha già un accesso lo dice; altrimenti form conusernameprecompilato con l'email del fascicolo se c'è. POST sulla stessa pagina (Astro.request.method === 'POST', niente redirect: la password non deve passare da un URL): chiamacreaAccessoClientee mostra username e password una volta, con la frase «comunicala al cliente: non verrà mostrata di nuovo». HeaderCache-Control: no-storesu entrambe le risposte. Codice inesistente → 404. La pagina non nomina mai «Identity».TEST (
tests/longevity/accesso.test.ts, DB:memory:)clientecreato, legato,codicePerUtenteritrova il codice; password restituita ≠ hash salvato; login con quella password riesce (logindi auth.ts).collegache fallisce → utente cancellato (compensazione).no-store, non contieneIdentityné.prepare(.NON TOCCARE
auth.ts (usa quello che c'è), db.ts, motore, registro,
lib/longevity/gestionale.tse le altre pagine del gestionale (le sta scrivendo un altro agente),scripts/,apps/sito/.ESITO: fatto, in attesa di merge
RIF: gitea:Adriano/InsanityLab-web#8
COMMIT:
77829b2su lavoro/goldrake-i8 (pushato)PROVA: npx vitest run → 220 passed (212 + 8); tsc 0 errori
CONTROLLO FINALE (Opus): conforme all'analisi. Nota non bloccante: il pattern HTML [a-z0-9._@-] rifiuta nel browser un'email precompilata con maiuscole o '+' (il server normalizza in minuscolo).
PROSSIMO: merge in lavoro/goldrake dopo la 2b di #2 (conflitto atteso in anagrafica.ts, funzioni aggiunte in coda da entrambi i rami).
Soluzione
Accesso cliente al referto: anagrafica.ts accessoDelCliente/collegaAccessoCliente; lib/longevity/accesso.ts creaAccessoCliente con compensazione deleteUser; pagina cliente/[code]/accesso.astro (no-store, password mostrata una volta); link dalla scheda quando manca l'accesso. Nota aperta: il pattern del nome utente rifiuta email con maiuscole o '+'. Commit
77829b2,2c9a74f. Stato: sul ramo lavoro/goldrake (commit2c9a74f), NON ancora in main né in produzione. PROVA: vitest 259/259, tsc 0 errori, build ok.ESITO: fatto
COSA: in produzione su piattaforme.tielogic.xyz, 2026-09-26 13:17 UTC. main
12b2e91.PROVA: docker compose up -d --build → container Up; migra() → colonne verifica/verificata_da/verificata_at presenti; registro_test f_ = 24; misure 20 prima e dopo.
BACKUP: /opt/docker/insanitylab-platforms/backup/*-pre-gestionale (sqlite3 .backup, integrity ok).