GapFill Flowee

This commit is contained in:
2026-06-18 11:24:05 +02:00
parent 64442af182
commit c68daf4313
25 changed files with 2965 additions and 48 deletions
+538
View File
@@ -0,0 +1,538 @@
# Integrazione Aeterna Archiviazione Sostitutiva PecHub
Documento tecnico sull'integrazione di PecHub con Aeterna,
piattaforma di conservazione digitale conforme E-ARK gestita da Idra Informatica.
---
## Indice
1. [Panoramica del provider](#1-panoramica-del-provider)
2. [Architettura Aeterna](#2-architettura-aeterna)
3. [Autenticazione JWT](#3-autenticazione-jwt)
4. [Ingest Upload SIP](#4-ingest--upload-sip)
5. [Formato SIP BagIt (RFC 8493)](#5-formato-sip-bagit-rfc-8493)
6. [Polling stato ingest](#6-polling-stato-ingest)
7. [Disseminazione (DIP)](#7-disseminazione-dip)
8. [Mapping stati Aeterna → PecHub](#8-mapping-stati-aeterna--pechub)
9. [Configurazione in PecHub](#9-configurazione-in-pechub)
10. [Esempi curl completi](#10-esempi-curl-completi)
11. [Note operative](#11-note-operative)
---
## 1. Panoramica del provider
| Campo | Valore |
|-------------------|-----------------------------------------------|
| Provider | Idra Informatica srl |
| Piattaforma | Aeterna v0.1.0 |
| URL applicazione | https://aeterna.idrainformatica.it |
| Endpoint API | https://api.aeterna.idrainformatica.it |
| Documentazione | https://api.aeterna.idrainformatica.it/docs |
| OpenAPI JSON | https://api.aeterna.idrainformatica.it/openapi.json |
| Standard | E-ARK CSIP 2.1.0, BagIt RFC 8493, PREMIS 3.0, METS 1.12 |
### Credenziali tenant PecHub
| Campo | Valore |
|-------------|---------------------------------|
| Org. name | pechub |
| Tenant slug | pechub |
| Username | matteo@idrainformatica.it |
| Password | (cifrata nel DB PecHub) |
| Tenant ID | 366d3d51-b25d-46fc-8f9c-9bc28d902620 |
---
## 2. Architettura Aeterna
Aeterna e' una piattaforma multi-tenant FastAPI (Python).
Ogni organizzazione (tenant) ha:
- Container MinIO dedicato per lo storage isolato
- Collection Apache Solr dedicata per la ricerca full-text
- RBAC (Role-Based Access Control) per-tenant
Il ciclo di vita di un documento archiviale in Aeterna:
```
SIP (upload) → AIP (ingest pipeline) → DIP (disseminazione)
| | |
BagIt ZIP PREMIS events ZIP scaricabile
multipart METS 1.12 generato
form-data validazione E-ARK
```
---
## 3. Autenticazione JWT
Aeterna usa JWT Bearer token, NON HTTP Basic.
Il token ha durata 3600 secondi (1 ora). Esiste un refresh token.
### Login
```
POST /api/v1/auth/login
Content-Type: application/json
```
Body:
```json
{
"email": "matteo@idrainformatica.it",
"password": "...",
"tenant_slug": "pechub"
}
```
Risposta 200:
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"user": {
"id": "e3cac60b-d942-4590-94fe-932c0e14e836",
"email": "matteo@idrainformatica.it",
"full_name": "Matteo Giustini",
"is_platform_admin": false,
"tenant_id": "366d3d51-b25d-46fc-8f9c-9bc28d902620",
"permissions": [
"ingest:submit", "ingest:manage", "packages:read",
"packages:create", "dissemination:read", "dissemination:download",
"preservation:manage", "audit:read", "settings:manage", ...
]
}
}
```
Utilizzo del token nelle richieste successive:
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```
### Refresh token
```
POST /api/v1/auth/refresh
Content-Type: application/json
{"refresh_token": "eyJhbGciOiJIUzI1NiIs..."}
```
### Verifica identita'
```
GET /api/v1/auth/me
Authorization: Bearer <token>
```
---
## 4. Ingest Upload SIP
L'endpoint di ingest accetta pacchetti SIP in due formati:
- **E-ARK CSIP v2.2.0** (ZIP con METS.xml in root)
- **BagIt RFC 8493** (ZIP con bagit.txt + data/) — **formato scelto per PecHub**
### Endpoint
```
POST /api/v1/ingest/upload
Authorization: Bearer <token>
Content-Type: multipart/form-data
```
### Campi form-data
| Campo | Tipo | Obbligatorio | Descrizione |
|---------------------|--------|:------------:|--------------------------------------|
| `file` | file | si | ZIP SIP (E-ARK CSIP o BagIt) |
| `title` | string | si | Titolo del pacchetto |
| `description` | string | no | Descrizione |
| `creator` | string | no | Nome del produttore |
| `submission_agreement` | string | no | ID o URL dell'accordo di versamento |
| `ead3_file` | file | no | EAD3 finding aid XML (GAP-09) |
| `eac_cpf_file` | file | no | EAC-CPF authority record XML |
### Risposta 202 Accepted
```json
{
"package_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"pid": "urn:pechub:aip:a1b2c3d4",
"status": "UPLOADED",
"task_id": "celery-task-uuid",
"message": "SIP uploaded successfully. Processing started.",
"submitted_at": "2026-06-18T09:00:00Z"
}
```
Il `package_id` e' l'identificatore da usare per polling e DIP.
---
## 5. Formato SIP BagIt (RFC 8493)
PecHub costruisce pacchetti BagIt in memoria (`build_bagit_sip` in `conservatore_client.py`).
### Struttura ZIP generata
```
pechub-pec-{message_id}/
bagit.txt # Dichiarazione BagIt (obbligatorio)
bag-info.txt # Metadati descrittivi (opzionale)
manifest-sha256.txt # Checksum SHA-256 dei file in data/
data/
{message_id}.eml # Messaggio PEC grezzo
```
### Contenuto bagit.txt
```
BagIt-Version: 1.0
Tag-File-Character-Encoding: UTF-8
```
### Contenuto bag-info.txt (esempio)
```
Bag-Software-Agent: PecHub Archival Module
Bagging-Date: 2026-06-18
External-Identifier: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Source-Organization: PecHub
Description: PEC oggetto del messaggio (max 500 char)
Contact-Email: mittente@pec.it
External-Description: PEC a destinatario@pec.it
Bag-Group-Identifier: 2026-06-18
```
### Contenuto manifest-sha256.txt
```
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/a1b2c3d4-e5f6.eml
```
### Rilevamento automatico BagIt da Aeterna
Aeterna rileva automaticamente il formato BagIt dalla presenza di `bagit.txt`
nella root del bag all'interno dello ZIP. Non e' necessario specificare il formato.
La pipeline verifica i checksum del manifest prima dell'ingest.
---
## 6. Polling stato ingest
Dopo l'upload, la pipeline di Aeterna processa il pacchetto in modo asincrono.
### Endpoint status
```
GET /api/v1/ingest/{package_id}/status
Authorization: Bearer <token>
```
### Risposta
```json
{
"package_id": "a1b2c3d4-...",
"pid": "urn:pechub:aip:a1b2c3d4",
"status": "PROCESSING",
"task_id": "celery-task-uuid",
"pipeline_stage": "format_identification",
"progress_pct": 40,
"steps": [
{"name": "validation", "status": "completed", ...},
{"name": "format_identification", "status": "running", ...},
{"name": "virus_scan", "status": "pending", ...}
],
"is_valid": null,
"validation_errors": 0,
"error_message": null,
"submitted_at": "2026-06-18T09:00:00Z",
"completed_at": null
}
```
### Stati possibili
| Status Aeterna | Significato | Stato PecHub |
|----------------|------------------------------------------|--------------|
| `UPLOADED` | SIP ricevuto, elaborazione non iniziata | `uploading` |
| `VALIDATING` | Validazione E-ARK/BagIt in corso | `uploading` |
| `PROCESSING` | Pipeline ingest in corso | `uploading` |
| `INGESTING` | AIP in fase di creazione | `uploading` |
| `ACTIVE` | AIP attivo, conservazione completata | `confirmed` |
| `FAILED` | Pipeline fallita con errori | `failed` |
| `REJECTED` | Pacchetto non conforme agli standard | `rejected` |
| `DELETED` | Soft-delete | `rejected` |
### Report completo
```
GET /api/v1/ingest/{package_id}/report
Authorization: Bearer <token>
```
---
## 7. Disseminazione (DIP)
### Richiesta generazione DIP
```
POST /api/v1/packages/{package_id}/disseminate
Authorization: Bearer <token>
Content-Type: application/json
{"note": "Richiesto da PecHub Archival Module"}
```
Risposta 202:
```json
{
"id": "dip-uuid",
"pid": "urn:pechub:dip:...",
"status": "PROCESSING",
"size_bytes": null,
"is_available": false
}
```
### Verifica stato DIP
```
GET /api/v1/packages/{package_id}/dip
Authorization: Bearer <token>
```
Quando `is_available: true`, il DIP e' scaricabile.
### Download DIP
```
GET /api/v1/packages/{package_id}/dip/download
Authorization: Bearer <token>
```
Risposta: stream ZIP del DIP.
---
## 8. Mapping stati Aeterna → PecHub
```python
_STATUS_MAP = {
"UPLOADED": "uploading",
"VALIDATING": "uploading",
"PROCESSING": "uploading",
"INGESTING": "uploading",
"ACTIVE": "confirmed", # conservazione completata
"FAILED": "failed",
"REJECTED": "rejected",
"DELETED": "rejected",
}
```
Il `versamento_id` in PecHub corrisponde al `package_id` di Aeterna (UUID v4).
---
## 9. Configurazione in PecHub
### Impostazioni tenant
Dalla pagina **Impostazioni → Archiviazione Sostitutiva**:
| Campo | Valore per Aeterna |
|-------------------------|---------------------------------------------|
| Modalita' | Produzione |
| Identificativo conservatore | `aeterna` |
| URL endpoint API | `https://api.aeterna.idrainformatica.it` |
| Tenant Slug | `pechub` |
| Username | `matteo@idrainformatica.it` |
| Password | (da fornire, viene cifrata AES-256-GCM) |
### Riconoscimento automatico del provider
Il factory `ConservatoreClient.from_tenant_credentials()` rileva Aeterna se:
- `conservatore_id == "aeterna"` (case-insensitive), OPPURE
- `"aeterna"` e' presente nell'URL endpoint, OPPURE
- `"idrainformatica"` e' presente nell'URL endpoint
Se rilevato come Aeterna, usa `AeternaConservatoreClient` (JWT + BagIt).
Altrimenti usa `ProductionConservatoreClient` (HTTP Basic, standard AgID legacy).
### Test connessione
Dopo aver configurato e salvato le impostazioni, usare il pulsante
**"Testa connessione"** in Impostazioni → Archiviazione Sostitutiva.
Oppure via API:
```
POST /api/v1/settings/test-conservatore
Authorization: Bearer <pechub-token>
```
Risposta:
```json
{
"success": true,
"message": "Connessione ad Aeterna riuscita (utente: matteo@idrainformatica.it)",
"latency_ms": 342,
"provider_info": {
"platform": "Aeterna",
"tenant_slug": "pechub",
"user_email": "matteo@idrainformatica.it",
"permissions_count": 20
}
}
```
---
## 10. Esempi curl completi
### Login
```bash
TOKEN=$(curl -s -X POST https://api.aeterna.idrainformatica.it/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"matteo@idrainformatica.it","password":"...","tenant_slug":"pechub"}' \
| jq -r .access_token)
echo "Token: $TOKEN"
```
### Upload SIP BagIt
```bash
# Crea un BagIt minimo di test
mkdir -p /tmp/testbag/data
echo "Hello PEC" > /tmp/testbag/data/test.eml
SHA=$(sha256sum /tmp/testbag/data/test.eml | awk '{print $1}')
cat > /tmp/testbag/bagit.txt <<EOF
BagIt-Version: 1.0
Tag-File-Character-Encoding: UTF-8
EOF
cat > /tmp/testbag/manifest-sha256.txt <<EOF
$SHA data/test.eml
EOF
# ZIP del bag
cd /tmp && zip -r testbag.zip testbag/
# Upload
curl -s -X POST https://api.aeterna.idrainformatica.it/api/v1/ingest/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@/tmp/testbag.zip;type=application/zip" \
-F "title=Test PEC da PecHub" \
-F "description=Messaggio di test" \
-F "creator=PecHub" \
| jq .
```
### Polling status
```bash
PACKAGE_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
curl -s https://api.aeterna.idrainformatica.it/api/v1/ingest/$PACKAGE_ID/status \
-H "Authorization: Bearer $TOKEN" \
| jq '{status:.status, stage:.pipeline_stage, pct:.progress_pct}'
```
### Lista pacchetti
```bash
curl -s "https://api.aeterna.idrainformatica.it/api/v1/packages?limit=10" \
-H "Authorization: Bearer $TOKEN" \
| jq '.items[] | {id:.id, title:.title, status:.status}'
```
### Report ingest
```bash
curl -s https://api.aeterna.idrainformatica.it/api/v1/ingest/$PACKAGE_ID/report \
-H "Authorization: Bearer $TOKEN" \
| jq .
```
### Richiesta DIP
```bash
curl -s -X POST \
"https://api.aeterna.idrainformatica.it/api/v1/packages/$PACKAGE_ID/disseminate" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"note":"Richiesto da PecHub"}' \
| jq .
```
### Download DIP
```bash
curl -L -o /tmp/dip.zip \
"https://api.aeterna.idrainformatica.it/api/v1/packages/$PACKAGE_ID/dip/download" \
-H "Authorization: Bearer $TOKEN"
```
---
## 11. Note operative
### Dimensione pacchetti
Non ci sono limiti documentati. I file EML di PEC sono tipicamente 10-500 KB.
Il timeout HTTP nel client e' impostato a 120 secondi per l'upload.
### Concorrenza
Ogni `AeternaConservatoreClient` gestisce il token JWT in memoria.
Se il worker usa piu' istanze concorrenti, ogni istanza effettua il suo login.
Il token dura 3600s, il rinnovo automatico avviene 60s prima della scadenza.
### Retry policy
In caso di fallimento dell'upload, il batch `ArchivalBatch` in PecHub
ha `max_attempts=3` e un `next_retry_at` con back-off esponenziale.
### Standard di riferimento
- **E-ARK CSIP v2.1.0** — Common Specification for Information Packages
- **E-ARK SIP/AIP/DIP** — Submission/Archival/Dissemination Information Package
- **BagIt RFC 8493** — formato file standard per trasferimento dati
- **PREMIS 3.0** — metadati di preservazione
- **METS 1.12** — Metadata Encoding and Transmission Standard
- **Dublin Core / EAD** — metadati descrittivi
### Codice sorgente rilevante
| File | Descrizione |
|------|-------------|
| `worker/app/archival/conservatore_client.py` | Client completo con AeternaConservatoreClient, BagIt builder, factory |
| `worker/scripts/test_aeterna_transmission.py` | Script di test standalone |
| `backend/app/api/v1/settings.py` | Endpoint test-conservatore |
| `backend/app/models/tenant_settings.py` | Modello DB con conservatore_tenant_slug |
| `backend/app/services/tenant_settings_service.py` | Servizio credenziali |
| `backend/alembic/versions/0020_add_conservatore_tenant_slug.py` | Migrazione DB |
| `frontend/src/pages/Settings/SettingsPage.tsx` | UI configurazione conservatore |
| `frontend/src/api/settings.api.ts` | Client API frontend |
### Esecuzione script di test
```bash
# Dal server, dentro il container worker
docker exec -it pechub-worker-1 \
python /app/scripts/test_aeterna_transmission.py
# I risultati vengono salvati in /tmp/aeterna_test_results.json
```
---
*Documento generato il 2026-06-18. Versione Aeterna: 0.1.0.*