Fix Associazione ricevute

This commit is contained in:
2026-03-19 16:55:31 +01:00
parent 3bdb32e6ae
commit c34d6bb080
15 changed files with 59 additions and 44 deletions
+8 -8
View File
@@ -1,5 +1,5 @@
# ─────────────────────────────────────────────────────────────────────────────
# PecFlow Variabili d'ambiente
# PEChub Variabili d'ambiente
# Copia questo file in .env e personalizza i valori
# NON committare mai il file .env con valori reali
# ─────────────────────────────────────────────────────────────────────────────
@@ -25,12 +25,12 @@ ENCRYPTION_KEY=change-me-generate-a-random-64-char-hex-string-here-11111111111
# ── Database PostgreSQL ───────────────────────────────────────────────────────
POSTGRES_HOST=db
POSTGRES_PORT=5432
POSTGRES_DB=pecflow
POSTGRES_USER=pecflow
POSTGRES_PASSWORD=pecflow_dev_password
POSTGRES_DB=pechub
POSTGRES_USER=pechub
POSTGRES_PASSWORD=pechub_dev_password
DATABASE_URL=postgresql+asyncpg://pecflow:pecflow_dev_password@db:5432/pecflow
DATABASE_URL_SYNC=postgresql://pecflow:pecflow_dev_password@db:5432/pecflow
DATABASE_URL=postgresql+asyncpg://pechub:pechub_dev_password@db:5432/pechub
DATABASE_URL_SYNC=postgresql://pechub:pechub_dev_password@db:5432/pechub
# ── Redis ─────────────────────────────────────────────────────────────────────
REDIS_URL=redis://redis:6379/0
@@ -39,7 +39,7 @@ REDIS_URL=redis://redis:6379/0
MINIO_ENDPOINT=minio:9000
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_BUCKET=pecflow
MINIO_BUCKET=pechub
MINIO_USE_SSL=false
# ── CORS ──────────────────────────────────────────────────────────────────────
@@ -58,4 +58,4 @@ SYSTEM_SMTP_HOST=
SYSTEM_SMTP_PORT=587
SYSTEM_SMTP_USER=
SYSTEM_SMTP_PASSWORD=
SYSTEM_SMTP_FROM=noreply@pecflow.it
SYSTEM_SMTP_FROM=noreply@pechub.it
+12 -12
View File
@@ -1,4 +1,4 @@
# PecFlow Architettura di Sistema
# PEChub Architettura di Sistema
> **Documento redatto il:** 2026-03-18 | **Ultima revisione:** 2026-03-18
> **Versione:** 2.0
@@ -26,7 +26,7 @@ Monorepo con workspace separati. Il confine di responsabilità è netto: ogni
cartella di primo livello è un deployable (o una libreria condivisa) indipendente.
```
PecFlow/ ← root del monorepo
PEChub/ ← root del monorepo
├── .github/ ← CI/CD GitHub Actions
│ ├── workflows/
@@ -279,14 +279,14 @@ PecFlow/ ← root del monorepo
│ ├── nginx/
│ │ ├── nginx.conf # reverse proxy, rate limiting, TLS termination
│ │ └── conf.d/
│ │ └── pecflow.conf
│ │ └── pechub.conf
│ ├── redis/
│ │ └── redis.conf # maxmemory, eviction policy
│ ├── prometheus/
│ │ └── prometheus.yml
│ └── grafana/
│ └── dashboards/
│ └── pecflow.json
│ └── pechub.json
└── docs/ ═══════════════════════════════
├── api/ ← OpenAPI spec generata (non commitare auto-gen)
@@ -326,7 +326,7 @@ CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- gen_random_bytes per nonce AES
-- ============================================================
CREATE TABLE tenants (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
slug VARCHAR(63) NOT NULL UNIQUE, -- usato come subdomain: acme.pecflow.it
slug VARCHAR(63) NOT NULL UNIQUE, -- usato come subdomain: acme.pechub.it
name VARCHAR(255) NOT NULL,
plan VARCHAR(50) NOT NULL DEFAULT 'starter', -- starter|pro|enterprise
is_active BOOLEAN NOT NULL DEFAULT TRUE,
@@ -1000,7 +1000,7 @@ Il conservatore AgID può essere temporaneamente non disponibile (manutenzione,
**Strategia:**
1. **Backoff esponenziale con jitter:** tentativo 1 subito, poi 1h, 4h, 24h (max 3 retry oltre il primo)
2. **Dead letter queue:** dopo tutti i retry falliti, il batch va in stato `failed` e genera un alert email all'admin del tenant + al team ops PecFlow
2. **Dead letter queue:** dopo tutti i retry falliti, il batch va in stato `failed` e genera un alert email all'admin del tenant + al team ops PEChub
3. **Idempotenza:** prima di ogni retry, verificare se il conservatore ha già ricevuto il versamento (`GET /versamento/{id}`) per evitare duplicati
4. **Finestra di versamento:** i versamenti periodici (es. mensili) devono completarsi entro la scadenza normativa; se il sistema prevede che non ce la farà, genera alert anticipato a 72h dalla scadenza
5. **Circuit breaker:** se il conservatore fallisce 5 volte in 1 ora, si sospendono tutti i versamenti verso quel conservatore per 30 minuti (evita tempesta di retry)
@@ -1418,7 +1418,7 @@ alla Fase 1, le altre possono essere sviluppate in parallelo al frontend (Fase 5
- [ ] API `POST /notifications/rules` (crea regola evento → canale + filtri)
- [ ] `notification_service.py`: al salvataggio di ogni messaggio, valuta regole applicabili e accoda job
- [ ] `worker/notifications/dispatcher.py`: smista per tipo canale
- [ ] `webhook.py`: POST JSON con header `X-PecFlow-Signature: sha256=<HMAC>` per verifica autenticità
- [ ] `webhook.py`: POST JSON con header `X-PEChub-Signature: sha256=<HMAC>` per verifica autenticità
- [ ] `email_smtp.py`: template HTML notifica (oggetto, mittente, link messaggio)
- [ ] `telegram.py`: messaggio Telegram con MarkdownV2, link deep al messaggio
- [ ] `whatsapp.py`: Meta Cloud API `POST /messages` con template pre-approvato (o freeform in 24h window)
@@ -1668,10 +1668,10 @@ Evento PEC (trigger: salvataggio nuovo messaggio o cambio stato)
**Webhook**
```json
POST https://your-endpoint.com/pecflow-hook
POST https://your-endpoint.com/pechub-hook
Headers:
X-PecFlow-Event: message.received
X-PecFlow-Signature: sha256=<HMAC-SHA256(secret, body)>
X-PEChub-Event: message.received
X-PEChub-Signature: sha256=<HMAC-SHA256(secret, body)>
Content-Type: application/json
Body:
@@ -1685,7 +1685,7 @@ Body:
"mailbox": "info@azienda.it",
"received_at": "2026-03-18T14:00:00Z",
"state": "received",
"url": "https://app.pecflow.it/messages/..."
"url": "https://app.pechub.it/messages/..."
}
}
```
@@ -1702,7 +1702,7 @@ Notifica in HTML via template configurabile. Il mittente usa un relay SMTP dedic
📋 Oggetto: Convocazione riunione del 20/03/2026
🕐 Ricevuta: 18/03/2026 14:00
🔗 Visualizza: https://app.pecflow.it/messages/...
🔗 Visualizza: https://app.pechub.it/messages/...
```
**WhatsApp (Meta Cloud API)**
+3 -3
View File
@@ -26,7 +26,7 @@ Tutto il frontend deve essere in italiano
Credenziali admin
Ruolo Email Password
Super Admin superadmin@pecflow.it SuperAdmin@PecFlow2026!
Admin (tenant demo) admin@demo.pecflow.it Demo@PecFlow2026!
Operator (tenant demo) operator@demo.pecflow.it Oper@PecFlow2026!
Super Admin superadmin@pechub.it SuperAdmin@PEChub2026!
Admin (tenant demo) admin@demo.pechub.it Demo@PEChub2026!
Operator (tenant demo) operator@demo.pechub.it Oper@PEChub2026!
Per accedere all'applicazione usa le credenziali Admin del tenant demo.
+3 -3
View File
@@ -1,4 +1,4 @@
## PecFlow Developer Commands
## PEChub Developer Commands
.PHONY: dev down build test migrate seed lint format clean logs ps help
@@ -79,7 +79,7 @@ makemigration: ## Genera una nuova migrazione (usa: make makemigration MSG="des
$(BACKEND) alembic revision --autogenerate -m "$(MSG)"
seed: ## Esegui seed dati di sviluppo (tenant demo + admin)
$(COMPOSE) exec db psql -U pecflow -d pecflow -f /docker-entrypoint-initdb.d/seeds/dev_tenant.sql
$(COMPOSE) exec db psql -U pechub -d pechub -f /docker-entrypoint-initdb.d/seeds/dev_tenant.sql
@echo " ✅ Seed completato"
reset-db: ## Reset completo DB (down-v + dev + migrate + seed)
@@ -151,7 +151,7 @@ shell-backend: ## Shell nel container backend
$(BACKEND) bash
shell-db: ## psql nel container database
$(COMPOSE) exec db psql -U pecflow -d pecflow
$(COMPOSE) exec db psql -U pechub -d pechub
clean: ## Rimuovi file temporanei Python
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
+18 -3
View File
@@ -362,8 +362,16 @@ async def bulk_update_messages(
message.archived_at = None
await db.commit()
for message in messages:
await db.refresh(message)
# Ricarica i messaggi aggiornati con selectinload per evitare MissingGreenlet sui labels
if messages:
updated_ids = [m.id for m in messages]
refreshed_result = await db.execute(
select(Message)
.where(Message.id.in_(updated_ids))
.options(selectinload(Message.labels))
)
messages = list(refreshed_result.scalars().all())
return MessageBulkUpdateResponse(
updated=len(messages),
@@ -407,7 +415,13 @@ async def update_message(
message.archived_at = None
await db.commit()
await db.refresh(message)
# Re-query con selectinload per evitare MissingGreenlet sui labels
refreshed = await db.execute(
select(Message)
.where(Message.id == message_id)
.options(selectinload(Message.labels))
)
message = refreshed.scalar_one()
return MessageResponse.model_validate(message)
@@ -508,6 +522,7 @@ async def list_receipts(
result = await db.execute(
select(Message)
.where(Message.parent_message_id == message_id)
.options(selectinload(Message.labels))
.order_by(Message.received_at.asc().nullslast(), Message.created_at.asc())
)
receipts = list(result.scalars().all())
+2 -2
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PecFlow Gestore PEC</title>
<meta name="description" content="PecFlow - Piattaforma SaaS per la gestione della posta elettronica certificata" />
<title>PEChub Gestore PEC</title>
<meta name="description" content="PEChub - Piattaforma SaaS per la gestione della posta elettronica certificata" />
</head>
<body>
<div id="root"></div>
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "pecflow-frontend",
"name": "pechub-frontend",
"version": "1.0.0",
"private": true,
"type": "module",
+1 -1
View File
@@ -12,7 +12,7 @@ import { VirtualBoxesPage } from '@/pages/VirtualBoxes/VirtualBoxesPage'
import { NotificationsPage } from '@/pages/Notifications/NotificationsPage'
/**
* Routing principale dell'applicazione PecFlow.
* Routing principale dell'applicazione PEChub.
*
* Struttura:
* - /login LoginPage (pubblica)
+3 -3
View File
@@ -1,9 +1,9 @@
/**
* Sidebar navigazione principale di PecFlow.
* Sidebar navigazione principale di PEChub.
*
* Struttura visiva (sidebar espansa):
*
* [PF] PecFlow []
* [PF] PEChub []
*
* TUTTE LE CASELLE
* 📥 Posta in Arrivo [badge]
@@ -140,7 +140,7 @@ export function Sidebar() {
<div className="h-8 w-8 rounded-lg bg-blue-500 flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
PF
</div>
<span className="font-bold text-lg">PecFlow</span>
<span className="font-bold text-lg">PEChub</span>
</div>
)}
{collapsed && (
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* Hook useWebSocket connessione WebSocket al backend PecFlow.
* Hook useWebSocket connessione WebSocket al backend PEChub.
*
* Il backend usa FastAPI WebSocket nativo (non Socket.io).
* Endpoint: /api/v1/ws/{tenant_id}?token=<access_token>
+2 -2
View File
@@ -66,7 +66,7 @@ export function LoginPage() {
<div className="mx-auto h-16 w-16 rounded-2xl bg-blue-600 flex items-center justify-center shadow-lg">
<span className="text-white font-bold text-2xl">PF</span>
</div>
<h1 className="mt-4 text-3xl font-bold text-gray-900">PecFlow</h1>
<h1 className="mt-4 text-3xl font-bold text-gray-900">PEChub</h1>
<p className="mt-1 text-sm text-gray-500">Gestore PEC SaaS</p>
</div>
@@ -188,7 +188,7 @@ export function LoginPage() {
</Card>
<p className="text-center text-xs text-gray-400">
PecFlow v1.0 Piattaforma gestione PEC certificata
PEChub v1.0 Piattaforma gestione PEC certificata
</p>
</div>
</div>
@@ -447,7 +447,7 @@ function RulesTab() {
<Zap className="h-12 w-12 text-muted-foreground mb-4 opacity-40" />
<p className="text-lg font-medium text-muted-foreground">Nessuna regola</p>
<p className="text-sm text-muted-foreground mt-1">
Le regole collegano gli eventi PecFlow ai canali di notifica.
Le regole collegano gli eventi PEChub ai canali di notifica.
</p>
</div>
)
+1 -1
View File
@@ -99,7 +99,7 @@ export const useAuthStore = create<AuthState>()(
})
},
}),
{ name: 'PecFlow/Auth' },
{ name: 'PEChub/Auth' },
),
)
+1 -1
View File
@@ -1,4 +1,4 @@
# Redis configuration per PecFlow
# Redis configuration per PEChub
# ── Bind e rete ───────────────────────────────────────────────────────────────
bind 0.0.0.0
+2 -2
View File
@@ -3,9 +3,9 @@ requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "pecflow-worker"
name = "pechub-worker"
version = "1.0.0"
description = "PecFlow Worker IMAP sync + background jobs"
description = "PEChub Worker IMAP sync + background jobs"
requires-python = ">=3.12"
dependencies = [