- Python 59.8%
- HTML 40.2%
| data/documents/PT-000001/2026/patient | ||
| static/img | ||
| templates | ||
| tests | ||
| .gitignore | ||
| __init__.py | ||
| access_control.py | ||
| access_log.py | ||
| api_auth.py | ||
| api_v1.py | ||
| app.py | ||
| audit_log.py | ||
| auth_service.py | ||
| cartclin.db | ||
| CHANGELOG.md | ||
| clinical_events.py | ||
| clinical_exam_catalog.py | ||
| clinical_exam_labs.py | ||
| clinical_exams.py | ||
| db.py | ||
| db_backup.py | ||
| diary.py | ||
| document_store.py | ||
| documents.py | ||
| failed_login_log.py | ||
| fhir_api.py | ||
| fhir_mapping.py | ||
| glycemia.py | ||
| LICENSE | ||
| log_archive.py | ||
| login_security_policy.py | ||
| logoGazzinet.png | ||
| medication_administration.py | ||
| medications.py | ||
| models.py | ||
| NOTICE | ||
| nutrition.py | ||
| nutrition_api.py | ||
| patient_service.py | ||
| patients.py | ||
| pressure.py | ||
| ps_transfer.py | ||
| rate_limit.py | ||
| README.md | ||
| README_PRONTO_SOCCORSO.md | ||
| receipts.py | ||
| security.py | ||
| ui_labels.py | ||
| user_service.py | ||
| utils.py | ||
CartClin
Documentazione tecnica dell'applicazione CartClin.
Versione documentata corrente: 1.1
Storico modifiche:
CartClin e' una web app Flask per la gestione di schede cliniche, pazienti, terapie, alimentazione assistita, diario, eventi sanitari, esami clinici, glicemia, pressione, documenti farmacia, invio in Pronto Soccorso e API protette JSON/FHIR.
Modulo Invio in Pronto Soccorso
La versione 1.1 aggiunge un modulo dedicato alla preparazione del trasferimento di un paziente verso il Pronto Soccorso.
Il modulo include:
- checklist sintetica stampabile A4
- scheda accompagnamento paziente PS
- export Markdown
- collegamento documenti PDF via document store
- campi paziente abituali utili al trasferimento
- API REST CRUD
/api/v1/patients/<id>/ps-transfers
Guida operativa breve: vedi README_PRONTO_SOCCORSO.md.
Questa documentazione descrive struttura, tabelle, sicurezza, permessi e aree funzionali. Non documenta come l'applicazione e' in esecuzione o come viene pubblicata.
Storia del programma / Program story
Italiano
Cartclin nasce prima di tutto da una storia personale. Non da un'idea commerciale, ma da un bisogno reale, vissuto ogni giorno accanto a una persona amata.
Quando mio padre, Amilcare Gozzi, si e ammalato gravemente, mi sono trovato ad affrontare una realta fatta di controlli continui, farmaci, crisi improvvise, valori da monitorare, pressioni, annotazioni e decisioni delicate da prendere rapidamente. Ogni giornata diventava una corsa contro il tempo, dove nulla poteva essere dimenticato.
Da tecnico informatico, ho sentito il bisogno di creare uno strumento che mi aiutasse a tenere traccia di tutto: eventi clinici, somministrazioni, variazioni dello stato di salute, parametri vitali e momenti critici. All'inizio era un programma semplice, nato esclusivamente per lui. Un modo per stargli vicino meglio. Un modo per prendermi cura di lui con piu attenzione, piu ordine e piu serenita.
Con il tempo, pero, quel progetto ha assunto un significato molto piu profondo.
Dopo la scomparsa di mio padre, ho deciso di non lasciare che quel lavoro restasse qualcosa di privato. Ho capito che la difficolta che avevo vissuto io era la stessa che affrontano ogni giorno famiglie, operatori sanitari e strutture che assistono persone anziane o fragili.
Cosi quel software, nato dal dolore ma anche dall'amore, e diventato Cartclin: un sistema pensato per aiutare chi si prende cura degli altri, semplificando la gestione quotidiana dell'assistenza e mettendo al centro la persona.
Perche' a volte le idee piu importanti non nascono in un ufficio. Nascono accanto a un letto, in silenzio, mentre si cerca semplicemente di fare il meglio possibile per qualcuno che si ama.
English
Cartclin was born first and foremost from a personal story. Not from a commercial idea, but from a real need, lived every day beside a loved one.
When my father, Amilcare Gozzi, became seriously ill, I found myself facing a reality made of constant checks, medications, sudden crises, values to monitor, blood pressure readings, notes, and delicate decisions to make quickly. Every day became a race against time, where nothing could be forgotten.
As an IT technician, I felt the need to create a tool that would help me keep track of everything: clinical events, administrations, changes in health status, vital parameters, and critical moments. At first it was a simple program, created exclusively for him. A way to stay closer to him. A way to take care of him with more attention, more order, and more peace of mind.
Over time, however, that project took on a much deeper meaning.
After my father's passing, I decided not to let that work remain something private. I realized that the difficulty I had experienced was the same one faced every day by families, healthcare workers, and facilities caring for elderly or fragile people.
So that software, born from pain but also from love, became Cartclin: a system designed to help those who care for others, simplifying the daily management of care and keeping the person at the center.
Because sometimes the most important ideas are not born in an office. They are born beside a bed, in silence, while simply trying to do the best possible for someone you love.
Licenza
CartClin e' distribuito sotto licenza Apache License 2.0.
File inclusi nel repository per la licenza:
In sintesi:
- il software e' distribuito "AS IS"
- e' consentito uso, modifica e ridistribuzione secondo i termini Apache 2.0
- i file modificati dovrebbero mantenere le note richieste dalla licenza
- il file
NOTICEva mantenuto nelle distribuzioni dove richiesto
Note di rilascio repository
Nel rilascio del codice vanno considerati come dati/runtime locali e non come artefatti da pubblicare:
cartclin.db- eventuali
farma_*.dblegacy non piu' usati dal modello attuale .flask_secret- contenuti di
venv/ - cache locali e
__pycache__/
Il repository rilasciabile deve contenere il codice, i template, le risorse statiche, la documentazione e i file di licenza, ma non i database reali o i segreti d'ambiente.
Aree funzionali
- autenticazione locale con sessione Flask
- gestione utenti, ruoli e stato attivo
- permessi per ruolo con bitmask
READ,WRITE,EXEC - anagrafica pazienti
- assegnazione pazienti a utenti non amministratori
- terapie/farmaci per paziente
- somministrazione farmaci giornaliera e opzionale con storico
- alimentazione assistita con prescrizioni strutturate, vista cucina, vista reparto, alert e report
- diario clinico
- eventi sanitari
- esami clinici e catalogo parametri
- document store PDF unificato su filesystem con metadati nel DB principale
- laboratori esami clinici
- rilevazioni glicemia
- rilevazioni pressione
- scontrini/farmacia con PDF e report
- esportazione dati paziente
- invio email per export paziente e reset password
- tema e lingua interfaccia per utente
- API v1 con token Bearer
- endpoint FHIR R4 in sola lettura per interoperabilita'
- rate limit su endpoint sensibili
- MFA via email configurabile per singolo utente
- OTP/TOTP compatibile con Google Authenticator come fattore aggiuntivo
- whitelist IP per singolo utente con blocco login web e API
- blacklist IP per bloccare login da sorgenti non autorizzate
- protezioni web CSRF e security headers
- soft delete clinico con motivo obbligatorio e ripristino admin
- audit log strutturato con storico e formato compatibile syslog
- access log separato per web/API/FHIR
- rilevazione tentativi di hacking su login, MFA/OTP, blacklist/whitelist IP e probing anonimo sospetto
- limitazione dei body HTTP, dei CSV clinici e dei file di import backup per ridurre i rischi di resource exhaustion
- storico dedicato dei tentativi di login falliti
Componenti principali
| File | Responsabilita' |
|---|---|
app.py |
Entry point Flask, route globali, registrazione blueprint, sicurezza HTTP, configurazione, login e reset password |
db.py |
Inizializzazione SQLite, tabelle principali, seed configurazioni e permessi admin |
security.py |
Sessione utente, CSRF, decorator admin e permessi |
auth_service.py |
Bitmask permessi e gestione autorizzazioni |
user_service.py |
Utenti, password, reset token, preferenze utente |
patient_service.py |
CRUD pazienti e assegnazioni utente-paziente |
api_v1.py |
API JSON v1 con autenticazione Bearer |
api_auth.py |
Creazione, validazione e revoca token API |
fhir_api.py |
Endpoint FHIR R4 |
fhir_mapping.py |
Mapping tra dati CartClin e risorse FHIR |
rate_limit.py |
Rate limit persistito su SQLite |
audit_log.py |
Audit strutturato, query storico e messaggi syslog-like |
access_log.py |
Storico accessi applicativi separato dall'audit |
failed_login_log.py |
Storico dedicato dei login falliti |
patients.py |
Blueprint pazienti, assegnazioni, export e invio email |
medications.py |
Blueprint farmaci/terapie |
medication_administration.py |
Blueprint somministrazione farmaci giornaliera e opzionale |
nutrition.py |
Blueprint alimentazione assistita, prescrizioni, cucina, reparto, alert e report |
nutrition_api.py |
API REST del modulo alimentazione assistita |
diary.py |
Blueprint diario paziente |
clinical_events.py |
Blueprint eventi sanitari |
clinical_exams.py |
Blueprint esami clinici e import CSV |
documents.py |
Blueprint documenti PDF paziente con filtri per modulo/record |
document_store.py |
Storage filesystem PDF, metadati DB e validazioni upload |
clinical_exam_catalog.py |
Catalogo parametri esami e range |
clinical_exam_labs.py |
Laboratori selezionabili negli esami |
glycemia.py |
Rilevazioni e report glicemia |
pressure.py |
Rilevazioni e report pressione |
receipts.py |
Scontrini farmacia su DB principale, PDF nel document store, export e report |
ui_labels.py |
Label UI multilingua |
utils.py |
Hash e verifica password |
models.py |
Dataclass applicative |
Database principale
Il database principale e' SQLite e viene inizializzato in modo idempotente da init_db().
Percorso applicativo:
cartclin.db
Tabelle rilevate:
api_tokens
app_config
assignments
authorizations
audit_logs
access_logs
allergens
failed_login_logs
clinical_events
clinical_exam_labs
clinical_exam_parameters
clinical_exam_reference_ranges
clinical_exams
diary
glicemie
integration_messages
medications
medication_administrations
meal_administrations
meal_deliveries
meal_rounds
nutrition_alerts
nutrition_meals
nutrition_prescription_history
nutrition_prescription_items
nutrition_prescriptions
patient_assignments
patient_identifiers
patients
pressioni
rate_limits
users
Nota: assignments e patient_assignments rappresentano entrambe il concetto di assegnazione paziente-utente. Nel codice corrente la tabella usata dai servizi applicativi e' assignments.
Tabelle
users
Contiene gli utenti applicativi locali.
| Campo | Note |
|---|---|
id |
Primary key |
username |
Univoco, normalizzato in minuscolo |
password |
Hash PBKDF2 con salt |
email |
Univoca, usata anche per reset password |
role |
Ruolo applicativo, ad esempio admin o user |
password_expiry |
Data scadenza password |
active |
Abilita/disabilita login |
reset_token |
Token reset password hashato |
reset_expiry |
Scadenza reset password |
theme |
Preferenza UI light/dark |
patient_export_mail_template |
Template personale export paziente |
ui_language |
Lingua interfaccia |
mfa_email_enabled |
Abilitazione MFA email per singolo utente |
mfa_email |
Email dedicata MFA; se vuota viene usata email |
mfa_totp_enabled |
Abilitazione OTP/TOTP per singolo utente |
mfa_totp_secret |
Secret TOTP persistito |
mfa_totp_enrolled_at |
Timestamp prima registrazione OTP |
login_ip_whitelist_enabled |
Abilita il vincolo IP per il singolo utente |
login_ip_whitelist |
Lista multilinea di IP o CIDR autorizzati |
authorizations
Permessi per ruolo e pagina.
| Campo | Note |
|---|---|
id |
Primary key |
role |
Ruolo applicativo |
page |
Area funzionale |
perm |
Bitmask intera 0..7 |
Bitmask:
| Bit | Costante | Valore |
|---|---|---|
| lettura | READ |
4 |
| scrittura | WRITE |
2 |
| esecuzione | EXEC |
1 |
| pieno accesso | `READ | WRITE |
La coppia (role, page) e' univoca.
patients
Anagrafica pazienti.
| Campo | Note |
|---|---|
id |
Primary key |
nome |
Nome paziente |
cognome |
Cognome paziente |
codice_fiscale |
Univoco, normalizzato in maiuscolo |
data_nascita |
Data di nascita |
reparto |
Reparto/area assistenziale |
camera |
Camera |
letto |
Letto/postazione |
attivo |
Paziente/ospite attivo |
medico |
Medico di riferimento |
contatto_medico |
Contatto medico |
email_medico |
Email medico |
audit_code |
Codice tecnico stabile per audit/log |
note |
Note libere |
medication_administration_enabled |
Abilita la somministrazione farmaci per il paziente |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
assignments
Associazione molti-a-molti tra pazienti e utenti.
| Campo | Note |
|---|---|
patient_id |
FK logica verso patients.id |
user_id |
FK logica verso users.id |
Primary key composta: (patient_id, user_id).
Gli utenti non admin vedono solo i pazienti assegnati.
patient_assignments
Tabella alternativa per assegnazioni paziente-utente, con foreign key ON DELETE CASCADE.
Nel codice applicativo corrente i servizi usano assignments; questa tabella va considerata legacy o parallela finche' non viene consolidata.
medications
Terapie/farmaci per paziente.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
nome_farmaco |
Nome farmaco |
modalita_uso |
Note uso |
prima_colazione |
Flag assunzione |
dopo_colazione |
Flag assunzione |
prima_pranzo |
Flag assunzione |
dopo_pranzo |
Flag assunzione |
prima_cena |
Flag assunzione |
dopo_cena |
Flag assunzione |
prima_dormire |
Flag assunzione |
libero |
Flag uso libero |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
diary
Diario clinico del paziente.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
date |
Data voce |
diary_time |
Ora opzionale della voce |
description |
Testo voce |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
medication_administrations
Registro delle somministrazioni farmaci standard e opzionali.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
medication_id |
Farmaco associato |
scheduled_date |
Data prevista |
scheduled_time |
Ora prevista |
slot_key |
Fascia tecnica, ad esempio prima_colazione o optional |
slot_label |
Etichetta della fascia |
recorded_at |
Data/ora effettiva di registrazione |
operator_user_id |
Utente che registra |
operator_username |
Username operatore |
outcome |
somministrato o non_somministrato |
note |
Nota opzionale o obbligatoria se non somministrato |
status |
Stato registrazione |
created_at |
Creazione record |
updated_at |
Ultimo aggiornamento |
Indice:
idx_medication_administrations_patient_dateidx_medication_administrations_unique_v2
allergens
Catalogo codificato di allergeni e intolleranze riusato dal modulo alimentazione assistita.
| Campo | Note |
|---|---|
id |
Primary key |
name |
Nome allergene |
code |
Codice univoco |
nutrition_prescriptions
Prescrizioni alimentari strutturate per paziente/ospite.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
feeding_status |
normale, assistita, digiuno_npo, enterale, parenterale |
diet_type |
normale, liquida, semiliquida, frullata, omogeneizzata, morbida, altro |
food_texture_level |
Livello consistenza cibo |
liquid_texture_level |
Livello consistenza liquidi |
dysphagia |
Flag disfagia |
thickened_liquids |
Flag liquidi addensati |
needs_assistance |
Flag necessita assistenza |
assistance_type |
autonoma, sorveglianza, imboccamento, postura_assistita |
clinical_notes |
Note sanitarie strutturate/libere |
valid_from |
Data inizio validita' |
valid_to |
Data fine validita' |
prescribed_by |
Operatore prescrittore |
validated_by |
Operatore validatore |
status |
active, suspended, expired |
created_at |
Creazione |
updated_at |
Ultimo aggiornamento |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
Indice: idx_nutrition_prescriptions_patient_dates.
nutrition_prescription_items
Elementi codificati associati alla prescrizione alimentare.
| Campo | Note |
|---|---|
id |
Primary key |
prescription_id |
Prescrizione collegata |
allergen_id |
Allergene/intolleranza codificato |
item_type |
allergen oppure intolerance |
severity |
Severita' clinica |
notes |
Note aggiuntive |
Indice: idx_nutrition_prescription_items_prescription.
nutrition_prescription_history
Storico completo delle modifiche alle prescrizioni alimentari.
| Campo | Note |
|---|---|
id |
Primary key |
prescription_id |
Prescrizione collegata |
patient_id |
Paziente |
action |
create o update |
old_value |
JSON precedente |
new_value |
JSON aggiornato |
changed_by |
Operatore che ha modificato |
created_at |
Data/ora modifica |
nutrition_meals
Anagrafica tecnica dei pasti selezionabili dal modulo, distinta dalla pianificazione menu.
| Campo | Note |
|---|---|
id |
Primary key |
name |
Nome pasto/preparazione |
meal_type |
breakfast, lunch, dinner, snack |
diet_type |
Tipo dieta compatibile |
food_texture_level |
Consistenza cibo |
liquid_texture_level |
Consistenza liquidi |
included_allergen_codes |
JSON con codici allergeni inclusi |
active |
Disponibilita' del pasto |
meal_rounds
Turni pasto giornalieri pianificati.
| Campo | Note |
|---|---|
id |
Primary key |
round_date |
Data turno |
meal_type |
breakfast, lunch, dinner, snack |
status |
Stato round |
created_at |
Creazione |
Vincolo univoco: (round_date, meal_type).
meal_deliveries
Pianificazione e consegna del pasto per singolo paziente e turno.
| Campo | Note |
|---|---|
id |
Primary key |
meal_round_id |
Turno pasto |
patient_id |
Paziente |
prescription_id |
Prescrizione usata |
meal_id |
Pasto/prodotto selezionato |
delivery_status |
planned, blocked, prepared, delivered, not_delivered, cancelled |
delivered_by |
Operatore consegna |
delivered_at |
Data/ora consegna |
anomaly_notes |
Anomalie consegna |
blocked_reason |
Motivo blocco NPO/conflitto |
created_at |
Creazione |
updated_at |
Ultimo aggiornamento |
Indice: idx_meal_deliveries_round_status.
meal_administrations
Registrazione del consumo e della somministrazione al paziente.
| Campo | Note |
|---|---|
id |
Primary key |
meal_delivery_id |
Consegna collegata, univoca |
administered_by |
Operatore che registra |
administered_at |
Data/ora registrazione |
consumed_percentage |
0, 25, 50, 75, 100 |
refused |
Rifiuto del pasto |
fluids_ml |
Liquidi assunti in ml |
problems |
JSON con problemi rilevati |
notes |
Note clinico-operative |
created_at |
Creazione |
updated_at |
Ultimo aggiornamento |
nutrition_alerts
Alert e non conformita' del modulo alimentazione assistita.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
meal_delivery_id |
Consegna correlata, se presente |
alert_type |
npo_block, missing_prescription, blocked_delivery, allergen_conflict, meal_refused, low_fluids, swallowing_risk, consecutive_no_intake |
severity |
high, medium, low |
message |
Messaggio operativo |
status |
open, resolved |
created_at |
Apertura alert |
resolved_at |
Chiusura |
resolved_by |
Operatore che chiude |
Indice: idx_nutrition_alerts_status_created.
clinical_events
Eventi sanitari del paziente.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
event_date |
Data evento |
event_type |
Tipo evento |
facility |
Struttura |
symptoms |
Sintomi |
diagnosis_summary |
Sintesi diagnosi |
outcome |
Esito |
therapy |
Terapia indicata |
doctor |
Medico |
priority |
Priorita', default media |
linked_event_id |
Evento collegato |
notes |
Note |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
Indice: idx_clinical_events_patient_date.
clinical_exams
Esami clinici del paziente.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
exam_date |
Data esame |
parameter_code |
Codice parametro da catalogo |
parameter_name |
Nome parametro |
parameter_value |
Valore rilevato |
unit |
Unita' di misura |
reference_range |
Range testuale |
status |
Stato interpretativo |
category |
Categoria |
laboratory |
Laboratorio |
clinical_relevance |
Rilevanza clinica |
document_ref |
Riferimento documento |
notes |
Note |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
Indici:
idx_clinical_exams_patient_dateidx_clinical_exams_parameter
clinical_exam_parameters
Catalogo parametri esami clinici.
| Campo | Note |
|---|---|
id |
Primary key |
code |
Codice univoco |
name_it |
Nome italiano |
name_en |
Nome inglese |
category |
Categoria |
default_unit |
Unita' predefinita |
aliases |
Alias JSON testuale |
enabled |
Abilitazione parametro |
Indice: idx_clinical_exam_parameters_name_it.
clinical_exam_reference_ranges
Range e label interpretative per parametro.
| Campo | Note |
|---|---|
id |
Primary key |
parameter_id |
FK verso catalogo parametri, univoca |
low_value |
Soglia bassa numerica |
high_value |
Soglia alta numerica |
reference_text |
Range testuale |
low_label_it |
Label italiana valore basso |
normal_label_it |
Label italiana valore normale |
high_label_it |
Label italiana valore alto |
low_label_en |
Label inglese valore basso |
normal_label_en |
Label inglese valore normale |
high_label_en |
Label inglese valore alto |
enabled |
Abilitazione range |
notes |
Note |
Indice: idx_clinical_exam_reference_parameter.
clinical_exam_labs
Laboratori selezionabili negli esami clinici.
| Campo | Note |
|---|---|
id |
Primary key |
code |
Codice univoco |
name |
Nome laboratorio |
enabled |
Abilitazione |
Indice: idx_clinical_exam_labs_name.
glicemie
Rilevazioni glicemia.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
date |
Data rilevazione |
prima_colazione |
Valore |
dopo_colazione |
Valore |
prima_pranzo |
Valore |
dopo_pranzo |
Valore |
prima_cena |
Valore |
dopo_cena |
Valore |
prima_dormire |
Valore |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
pressioni
Rilevazioni pressione e battiti.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente |
date |
Data rilevazione |
*_massima |
Pressione sistolica per fascia oraria |
*_minima |
Pressione diastolica per fascia oraria |
*_battiti |
Battiti per fascia oraria |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
Fasce gestite:
prima_colazionedopo_colazioneprima_pranzodopo_pranzoprima_cenadopo_cenaprima_dormire
app_config
Configurazione applicativa persistita.
| Campo | Note |
|---|---|
id |
Primary key |
section |
Area configurazione |
key |
Chiave |
value |
Valore |
description |
Descrizione |
value_type |
string, integer, boolean, secret, text |
updated_at |
Timestamp aggiornamento |
Sezioni seed:
mailpatient_export_mailui_labels_metaui_labels
I valori secret sono mascherati in UI e possono essere gestiti anche tramite store separato.
api_tokens
Token API persistiti solo come hash.
| Campo | Note |
|---|---|
id |
Primary key |
user_id |
Utente proprietario |
token_name |
Nome descrittivo |
token_hash |
SHA-256 del token |
created_at |
Creazione |
last_used_at |
Ultimo uso |
expires_at |
Scadenza |
active |
Token attivo/revocato |
Il token in chiaro viene restituito solo alla creazione.
patient_identifiers
Identificativi aggiuntivi per interoperabilita' esterna.
| Campo | Note |
|---|---|
id |
Primary key |
patient_id |
Paziente collegato |
identifier_system |
Sistema identificativo, ad esempio URI locale/FHIR |
identifier_value |
Valore identificativo |
identifier_type |
Tipo descrittivo |
assigning_authority |
Autorita' emittente |
active |
Identificativo attivo |
integration_messages
Inbox/outbox tecnica per integrazioni future HL7/FHIR.
| Campo | Note |
|---|---|
id |
Primary key |
direction |
inbound / outbound |
standard |
Standard, ad esempio FHIR o HL7v2 |
message_type |
Tipo messaggio o risorsa |
external_message_id |
ID esterno sorgente/destinatario |
patient_id |
Paziente correlato, se noto |
payload_raw |
Payload originale |
payload_normalized |
Payload normalizzato |
status |
Stato tecnico del messaggio |
error_text |
Errore di lavorazione |
created_at |
Ricezione o generazione |
processed_at |
Lavorazione completata |
rate_limits
Rate limit persistito per endpoint sensibili.
| Campo | Note |
|---|---|
id |
Primary key |
scope |
Ambito, per esempio web_login o api_auth_token |
actor |
Identificativo chiamante, di solito IP |
created_at |
Timestamp Unix |
Indice: idx_rate_limits_scope_actor_created.
scontrini
La gestione scontrini usa il database principale per i metadati strutturati e il document store PDF su filesystem per il file allegato.
| Campo | Note |
|---|---|
patient_id |
Paziente proprietario |
id |
Primary key |
data |
Data scontrino |
note |
Note |
importo |
Importo |
document_id |
Collegamento al PDF nel document store |
nome_file |
Nome file originale/sicuro |
data_insert |
Timestamp inserimento |
deleted_at |
Timestamp annullamento logico |
deleted_by |
Utente che ha annullato |
delete_reason |
Motivo annullamento |
Limite upload PDF:
30 MB
La ricerca scontrini lavora sulla tabella unica scontrini e filtra per paziente, anno, note, date e importo.
Le stampe applicative devono mostrare solo il contenuto del report: elementi UI globali come il pulsante help flottante vengono esclusi via CSS di stampa.
Autenticazione web
L'autenticazione web e' locale, basata sulla tabella users.
Flusso:
- verifica che l'IP sorgente non sia in blacklist
- username normalizzato in minuscolo
- ricerca utente attivo
- verifica password PBKDF2
- se
mfa_email_enabled=1, invio codice email e apertura challenge temporanea - verifica codice MFA entro 5 minuti
- salvataggio
uidnella sessione Flask - applicazione preferenze tema e lingua
La password viene salvata come:
salt_hex:hash_hex
Algoritmo:
PBKDF2-HMAC-SHA256, 100000 iterazioni, salt casuale 16 byte
Reset password
Il reset password usa token monouso:
- token random via
secrets.token_urlsafe(32) - salvataggio hash
sha256$...inusers.reset_token - scadenza in
users.reset_expiry - durata: 1 ora
- cancellazione token dopo reset riuscito
Il reset e' protetto da rate limit.
MFA via email
La MFA e' configurabile per singolo utente dai form di creazione/modifica utente.
Campi:
mfa_email_enabled: richiede codice email al loginmfa_email: indirizzo dedicato per il codice MFA
Comportamento:
- dopo username/password corretti, l'utente non viene ancora autenticato
- CartClin genera un codice numerico a 6 cifre
- il codice viene inviato via
send_email() - in sessione viene conservato solo l'hash SHA-256 del codice
- il codice scade dopo 5 minuti
- al completamento corretto viene creata la sessione utente
Se mfa_email e' vuoto, viene usata l'email principale dell'utente. Se la MFA e' attiva ma non esiste alcuna email utilizzabile, il login viene bloccato con errore.
OTP/TOTP
CartClin supporta anche OTP/TOTP compatibile con Google Authenticator come fattore aggiuntivo rispetto alla MFA email.
Campi:
mfa_totp_enabled: abilita OTP/TOTP per l'utentemfa_totp_secret: secret TOTP persistitomfa_totp_enrolled_at: data/ora di prima registrazione
Comportamento:
- se l'OTP e' abilitato ma non ancora registrato, al primo login viene mostrato il QR code
- l'utente deve confermare un codice OTP valido per completare il primo accesso
- dai login successivi il codice OTP viene richiesto dopo password e, se presente, dopo MFA email
Whitelist IP per utente
Ogni utente puo' avere una whitelist IP dedicata.
Campi:
login_ip_whitelist_enabled: attiva il controllo IP per l'utentelogin_ip_whitelist: lista multilinea di IP o CIDR autorizzati
Regole:
- una voce per riga
- sono supportati IP singoli e reti CIDR
- i commenti iniziano con
# - se la whitelist e' attiva e nessuna riga valida combacia, il login viene bloccato
- il controllo vale sia per il login web sia per
POST /api/v1/auth/token
Blacklist IP login
La blacklist IP viene salvata in app_config, sezione security, chiave login_ip_blacklist.
Formato:
203.0.113.10
198.51.100.0/24
# commento opzionale
Regole:
- una voce per riga
- sono supportati IP singoli e reti CIDR
- i commenti iniziano con
# - il controllo usa l'IP sorgente calcolato da
X-Forwarded-For,request.access_routeoremote_addr - se l'IP combacia,
POST /loginePOST /api/v1/auth/tokenrispondono403
Sessioni e CSRF
La sessione contiene:
| Chiave | Scopo |
|---|---|
uid |
ID utente autenticato |
_csrf_token |
Token CSRF web |
theme |
Tema UI |
ui_language |
Lingua UI |
Protezioni:
- cookie
HttpOnly - cookie
SameSite=Lax - cookie
Securequando la richiesta e' HTTPS o pubblicata come HTTPS - durata sessione permanente: 30 giorni se l'utente seleziona ricordami
- CSRF obbligatorio sui
POSTweb non API - confronto CSRF con
hmac.compare_digest
Le API sotto /api/ non usano CSRF web; usano token Bearer.
Timeout sessione:
- ogni utente puo' avere un timeout sessione dedicato
- se il valore utente e' assente, viene usato il default globale
session_default_hours - il default iniziale e'
9ore - superato il tempo massimo di inattivita', l'utente viene disconnesso automaticamente
Security headers
CartClin imposta header HTTP difensivi:
Content-Security-PolicyCross-Origin-Opener-Policy: same-originCross-Origin-Resource-Policy: same-originCache-Control: no-store
La CSP limita default, form, frame ancestors, immagini, font, script, stili e connessioni. Sono consentiti asset da cdn.jsdelivr.net dove necessario per l'interfaccia.
Permessi applicativi
I permessi sono basati su ruolo e pagina.
Pagine/aree principali:
dashboarduserspermissionspatientspatient_assignmentspatient_exportpatient_mailmailsettingmedicationsmedication_administrationnutrition_prescriptionsnutrition_kitchennutrition_wardnutrition_alertsnutrition_reportsdiaryclinical_eventsclinical_examsglycemiapressurereceiptssettingsconfiguration
Gli admin ricevono permessi pieni sulle aree seed principali.
Decorator usati:
@admin_required@require_perm(page, mask)@api_require_perm(page, mask)
Controlli paziente:
- gli admin accedono a tutti i pazienti
- gli utenti non admin accedono solo ai pazienti presenti in
assignments - le API applicano lo stesso vincolo su lettura e scrittura dei dati paziente
Soft delete clinico e ripristino
I record clinici e amministrativi sensibili non vengono piu' rimossi con DELETE FROM, ma annullati logicamente.
Moduli coperti:
- pazienti
- farmaci
- diario
- eventi sanitari
- esami clinici
- glicemia
- pressione
- scontrini farmacia
Meccanismo:
- scrittura di
deleted_at - scrittura di
deleted_by - motivo obbligatorio in
delete_reason - esclusione automatica dei record annullati da viste, report, export e API
- pagina admin di ripristino:
/configuration/deleted-records
Questo comportamento e' piu' coerente con tracciabilita', audit e contesto sanitario rispetto alla cancellazione fisica immediata.
API v1
Base logica:
/api/v1
Autenticazione:
Authorization: Bearer <token>
Endpoint principali:
| Metodo | Path | Scopo |
|---|---|---|
GET |
/api/v1/ |
Metadati API |
POST |
/api/v1/auth/token |
Crea token Bearer |
POST |
/api/v1/auth/revoke |
Revoca token corrente |
GET |
/api/v1/me |
Profilo utente token |
GET/POST |
/api/v1/patients |
Lista/creazione pazienti |
GET/PUT/DELETE |
/api/v1/patients/<id> |
Dettaglio/modifica/eliminazione paziente |
GET/PUT |
/api/v1/patients/<id>/assignments |
Assegnazioni paziente |
GET/POST |
/api/v1/patients/<id>/medications |
Farmaci |
PUT/DELETE |
/api/v1/patients/<id>/medications/<mid> |
Modifica/elimina farmaco |
GET/POST |
/api/v1/patients/<id>/medication-administrations |
Somministrazioni farmaci |
GET |
/api/v1/nutrition/allergens |
Catalogo allergeni |
GET/POST |
/api/v1/nutrition/patients/<id>/prescriptions |
Prescrizioni alimentari |
PUT |
/api/v1/nutrition/patients/<id>/prescriptions/<prescription_id> |
Modifica prescrizione alimentare |
GET |
/api/v1/nutrition/kitchen |
Vista cucina alimentazione assistita |
POST |
/api/v1/nutrition/deliveries/<delivery_id> |
Aggiorna consegna pasto |
POST |
/api/v1/nutrition/administrations/<delivery_id> |
Registra consumo/somministrazione pasto |
GET |
/api/v1/nutrition/alerts |
Lista alert nutrizione |
POST |
/api/v1/nutrition/alerts/<alert_id>/resolve |
Risoluzione alert |
GET |
/api/v1/nutrition/reports |
Report nutrizione |
GET/POST |
/api/v1/patients/<id>/diary |
Diario |
PUT/DELETE |
/api/v1/patients/<id>/diary/<did> |
Modifica/elimina diario |
GET/POST |
/api/v1/patients/<id>/clinical-events |
Eventi sanitari |
PUT/DELETE |
/api/v1/patients/<id>/clinical-events/<event_id> |
Modifica/elimina evento |
GET/POST |
/api/v1/patients/<id>/clinical-exams |
Esami clinici |
PUT/DELETE |
/api/v1/patients/<id>/clinical-exams/<exam_id> |
Modifica/elimina esame |
GET/POST |
/api/v1/patients/<id>/documents |
Documenti PDF paziente |
GET |
/api/v1/patients/<id>/documents/<document_id> |
Metadati documento |
GET |
/api/v1/patients/<id>/documents/<document_id>/download |
Download documento |
DELETE |
/api/v1/patients/<id>/documents/<document_id> |
Hide logico documento |
GET/POST |
/api/v1/patients/<id>/glycemia |
Glicemia |
PUT/DELETE |
/api/v1/patients/<id>/glycemia/<gid> |
Modifica/elimina glicemia |
GET/POST |
/api/v1/patients/<id>/pressure |
Pressione |
PUT/DELETE |
/api/v1/patients/<id>/pressure/<rid> |
Modifica/elimina pressione |
GET/POST |
/api/v1/patients/<id>/receipts |
Scontrini |
GET/PUT/DELETE |
/api/v1/patients/<id>/receipts/<year>/<receipt_id> |
Dettaglio/modifica/elimina scontrino |
GET |
/api/v1/patients/<id>/export.txt |
Export testuale paziente |
POST |
/api/v1/patients/<id>/export-email |
Invio export paziente via email |
Le route DELETE dell'API non eliminano piu' fisicamente il record: applicano il soft delete logico dove previsto.
I token API:
- sono creati dopo autenticazione username/password locale
- sono generati con prefisso
ctc_ - sono salvati solo come SHA-256
- hanno scadenza configurabile tra 1 e 365 giorni
- aggiornano
last_used_ata ogni uso valido - possono essere revocati impostando
active = 0
FHIR R4
Base logica:
/fhir/R4
Autenticazione:
Authorization: Bearer <token>
Endpoint iniziali:
| Metodo | Path | Scopo |
|---|---|---|
GET |
/fhir/R4/metadata |
CapabilityStatement minimale |
GET |
/fhir/R4/Patient |
Search Patient |
GET |
/fhir/R4/Patient/<id> |
Read Patient |
GET |
/fhir/R4/Observation |
Search Observation per paziente |
GET |
/fhir/R4/Observation/<id> |
Read Observation |
Mapping attuale:
patients->Patientclinical_exams->Observationglicemie->Observationpressioni->Observation
Note:
- il layer FHIR e' attualmente di sola lettura
- riusa i permessi CartClin e le assegnazioni paziente-utente
- le letture FHIR vengono registrate nell'audit con canale
fhir integration_messagesepatient_identifierssono la base dati per estensioni HL7/FHIR future
Rate limit
Endpoint protetti:
| Scope | Limite |
|---|---|
web_login |
10 tentativi ogni 600 secondi per IP |
api_auth_token |
10 tentativi ogni 600 secondi per IP |
password_reset |
5 tentativi ogni 1800 secondi per IP |
Nel pannello Configuration esiste ora una sezione dedicata ai rate limit di autenticazione, separata dalle altre impostazioni di sicurezza, cosi il sistemista puo adattare piu facilmente i limiti a studi, RSA o ospedali dietro IP condiviso.
I record vecchi vengono rimossi durante il consumo del rate limit.
Audit e tracciabilita'
CartClin registra eventi strutturati in audit_logs per:
- accessi amministrativi rilevanti
- export/import database
- import CSV esami clinici
- create/update/delete/restore nei moduli principali
- attivita' API JSON
- letture FHIR
Caratteristiche:
- pseudonimizzazione paziente tramite
patients.audit_code - messaggio
syslog_messagecompatibile con sistemi esterni - storico consultabile dalla UI admin
- dettagli minimizzati per ridurre esposizione di dati sanitari nel log
In parallelo esistono anche:
access_logs: storico separato di accessi web, API e FHIRfailed_login_logs: storico dedicato dei tentativi di login falliti, con username tentato, IP sorgente, stadio del fallimento e motivo tecnico
Configurazione applicativa
La tabella app_config conserva impostazioni modificabili da UI.
Sezione mail:
hostportusernamepasswordfrom_emailfrom_nameuse_tlsuse_sslreply_totimeout_seconds
Sezione patient_export_mail:
body_template
Sezioni UI:
ui_labels_metaui_labels
Sezione security:
login_ip_blacklist: IP o reti CIDR non autorizzate al login, una voce per rigasession_default_hours: timeout sessione globale usato quando l'utente non ha un valore dedicatoweb_login_rate_limit: numero massimo di login web per finestra temporaleweb_login_rate_window_seconds: finestra temporale del rate limit login webapi_auth_rate_limit: numero massimo di richieste token API per finestra temporaleapi_auth_rate_window_seconds: finestra temporale del rate limit autenticazione APIpassword_reset_rate_limit: numero massimo di richieste reset password per finestra temporalepassword_reset_rate_window_seconds: finestra temporale del rate limit reset password
Per il pannello dedicato ai rate limit di autenticazione, i valori consigliati in ambienti con IP pubblico condiviso sono:
| Scenario | Web login | API auth | Password reset |
|---|---|---|---|
20 utenti |
40 / 600s |
40 / 600s |
10 / 1800s |
40 utenti |
80 / 600s |
80 / 600s |
20 / 1800s |
80 utenti |
160 / 600s |
160 / 600s |
40 / 1800s |
Questi valori servono a ridurre i falsi positivi in strutture con molti operatori dietro lo stesso IP NAT, mantenendo comunque il controllo sugli accessi anomali.
I segreti di configurazione possono essere memorizzati in uno store JSON separato:
.config_secrets.json
Il file viene scritto con permessi 0600 quando possibile.
Dati sensibili
CartClin tratta dati sanitari e identificativi. Sono particolarmente sensibili:
- anagrafica pazienti
- codice fiscale
- note cliniche
- diario paziente
- eventi sanitari
- esami clinici
- rilevazioni glicemia e pressione
- PDF scontrini/farmacia
- email medico
- token API
- reset token
- codici MFA temporanei
- configurazioni SMTP
Indicazioni:
- non versionare database SQLite reali
- non versionare
.flask_secret - non versionare
.config_secrets.json - non inviare token API in log o chat
- proteggere backup dei database con lo stesso livello dei dati di produzione
- verificare periodicamente utenti inattivi e token scaduti
- limitare permessi degli utenti non admin con
assignmentseauthorizations
Note di coerenza
users.passwordcontiene hash PBKDF2 locali, non credenziali LDAP.- L'autenticazione applicativa web non dipende da LDAP.
authorizations.perme' la colonna moderna; alcune funzioni legacy citanopermission.assignmentse' la tabella usata dai servizi paziente correnti.patient_assignmentsesiste nello schema ma non e' la tabella principale usata dal codice.- Eventuali file
farma_*.dblegacy non fanno parte del modello scontrini corrente e non sono richiesti dal nuovo codice. - Le interpretazioni degli esami clinici derivano dal catalogo e dai range configurati; non sostituiscono valutazione medica.