Cambio nome

This commit is contained in:
2026-03-19 16:58:23 +01:00
parent c34d6bb080
commit 83e494e171
29 changed files with 105 additions and 105 deletions
+6 -6
View File
@@ -10,8 +10,8 @@ env:
PYTHON_VERSION: "3.12" PYTHON_VERSION: "3.12"
ENCRYPTION_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ENCRYPTION_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
SECRET_KEY: "ci-test-secret-key-for-github-actions-only-not-for-production" SECRET_KEY: "ci-test-secret-key-for-github-actions-only-not-for-production"
DATABASE_URL: "postgresql+asyncpg://pecflow:pecflow_ci@localhost:5432/pecflow_test" DATABASE_URL: "postgresql+asyncpg://pechub:pechub_ci@localhost:5432/pechub_test"
DATABASE_URL_SYNC: "postgresql://pecflow:pecflow_ci@localhost:5432/pecflow_test" DATABASE_URL_SYNC: "postgresql://pechub:pechub_ci@localhost:5432/pechub_test"
REDIS_URL: "redis://localhost:6379/0" REDIS_URL: "redis://localhost:6379/0"
jobs: jobs:
@@ -61,9 +61,9 @@ jobs:
postgres: postgres:
image: postgres:16-alpine image: postgres:16-alpine
env: env:
POSTGRES_DB: pecflow_test POSTGRES_DB: pechub_test
POSTGRES_USER: pecflow POSTGRES_USER: pechub
POSTGRES_PASSWORD: pecflow_ci POSTGRES_PASSWORD: pechub_ci
ports: ports:
- 5432:5432 - 5432:5432
options: >- options: >-
@@ -156,7 +156,7 @@ jobs:
context: ./backend context: ./backend
file: ./backend/Dockerfile file: ./backend/Dockerfile
push: false push: false
tags: pecflow-backend:${{ github.sha }} tags: pechub-backend:${{ github.sha }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
@@ -1,4 +1,4 @@
"""Initial schema tutte le tabelle PecFlow Fase 1 """Initial schema tutte le tabelle PEChub Fase 1
Revision ID: 0001 Revision ID: 0001
Revises: Revises:
+1 -1
View File
@@ -1 +1 @@
# PecFlow Backend # PEChub Backend
+3 -3
View File
@@ -34,8 +34,8 @@ class Settings(BaseSettings):
encryption_key: str = "0" * 64 encryption_key: str = "0" * 64
# ── Database ────────────────────────────────────────────────────────────── # ── Database ──────────────────────────────────────────────────────────────
database_url: str = "postgresql+asyncpg://pecflow:pecflow_dev_password@db:5432/pecflow" database_url: str = "postgresql+asyncpg://pechub:pechub_dev_password@db:5432/pechub"
database_url_sync: str = "postgresql://pecflow:pecflow_dev_password@db:5432/pecflow" database_url_sync: str = "postgresql://pechub:pechub_dev_password@db:5432/pechub"
# ── Redis ───────────────────────────────────────────────────────────────── # ── Redis ─────────────────────────────────────────────────────────────────
redis_url: str = "redis://redis:6379/0" redis_url: str = "redis://redis:6379/0"
@@ -44,7 +44,7 @@ class Settings(BaseSettings):
minio_endpoint: str = "minio:9000" minio_endpoint: str = "minio:9000"
minio_access_key: str = "minioadmin" minio_access_key: str = "minioadmin"
minio_secret_key: str = "minioadmin" minio_secret_key: str = "minioadmin"
minio_bucket: str = "pecflow" minio_bucket: str = "pechub"
minio_use_ssl: bool = False minio_use_ssl: bool = False
# ── CORS ────────────────────────────────────────────────────────────────── # ── CORS ──────────────────────────────────────────────────────────────────
+1 -1
View File
@@ -1,5 +1,5 @@
""" """
Eccezioni applicative custom per PecFlow. Eccezioni applicative custom per PEChub.
""" """
from fastapi import HTTPException, status from fastapi import HTTPException, status
+1 -1
View File
@@ -1,5 +1,5 @@
""" """
Structured logging per PecFlow. Structured logging per PEChub.
In produzione (LOG_JSON=true) emette log JSON per aggregatori (Loki, ELK). In produzione (LOG_JSON=true) emette log JSON per aggregatori (Loki, ELK).
In sviluppo emette log leggibili colorati. In sviluppo emette log leggibili colorati.
""" """
+3 -3
View File
@@ -31,7 +31,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
setup_logging() setup_logging()
logger.info( logger.info(
"🚀 PecFlow Backend avviato", "🚀 PEChub Backend avviato",
extra={"env": settings.app_env, "debug": settings.app_debug}, extra={"env": settings.app_env, "debug": settings.app_debug},
) )
@@ -53,14 +53,14 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
from app.services.send_service import close_arq_pool from app.services.send_service import close_arq_pool
await close_arq_pool() await close_arq_pool()
await engine.dispose() await engine.dispose()
logger.info("🛑 PecFlow Backend fermato") logger.info("🛑 PEChub Backend fermato")
# ─── Applicazione FastAPI ───────────────────────────────────────────────────── # ─── Applicazione FastAPI ─────────────────────────────────────────────────────
limiter = Limiter(key_func=get_remote_address, default_limits=["200/minute"]) limiter = Limiter(key_func=get_remote_address, default_limits=["200/minute"])
app = FastAPI( app = FastAPI(
title="PecFlow API", title="PEChub API",
description="API per la gestione PEC SaaS multi-tenant", description="API per la gestione PEC SaaS multi-tenant",
version="1.0.0", version="1.0.0",
docs_url="/docs" if not settings.is_production else None, docs_url="/docs" if not settings.is_production else None,
+1 -1
View File
@@ -109,7 +109,7 @@ class NotificationChannel(Base):
class NotificationRule(Base): class NotificationRule(Base):
""" """
Regola: evento PecFlow → canale di notifica. Regola: evento PEChub → canale di notifica.
event_type: new_message | state_changed | anomaly | send_failed | ... event_type: new_message | state_changed | anomaly | send_failed | ...
filter: JSONB con condizioni opzionali (mailbox_id, state, ecc.) filter: JSONB con condizioni opzionali (mailbox_id, state, ecc.)
+1 -1
View File
@@ -26,7 +26,7 @@ class TenantCreateRequest(BaseModel):
@field_validator("slug") @field_validator("slug")
@classmethod @classmethod
def validate_slug(cls, v: str) -> str: def validate_slug(cls, v: str) -> str:
reserved = {"api", "admin", "www", "mail", "smtp", "imap", "pecflow", "app"} reserved = {"api", "admin", "www", "mail", "smtp", "imap", "pechub", "app"}
if v in reserved: if v in reserved:
raise ValueError(f"Slug '{v}' riservato") raise ValueError(f"Slug '{v}' riservato")
return v.lower() return v.lower()
+1 -1
View File
@@ -209,7 +209,7 @@ class AuthService:
# Genera URI otpauth:// # Genera URI otpauth://
totp = pyotp.TOTP(secret) totp = pyotp.TOTP(secret)
uri = totp.provisioning_uri(name=user.email, issuer_name="PecFlow") uri = totp.provisioning_uri(name=user.email, issuer_name="PEChub")
# Genera QR code # Genera QR code
qr = qrcode.QRCode(version=1, box_size=6, border=4) qr = qrcode.QRCode(version=1, box_size=6, border=4)
+2 -2
View File
@@ -3,9 +3,9 @@ requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "pecflow-backend" name = "pechub-backend"
version = "1.0.0" version = "1.0.0"
description = "PecFlow Backend API per gestione PEC SaaS" description = "PEChub Backend API per gestione PEC SaaS"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
+2 -2
View File
@@ -143,8 +143,8 @@ class TestCreateSendJob:
json={ json={
"mailbox_id": str(active_mailbox.id), "mailbox_id": str(active_mailbox.id),
"to_addresses": ["matteo1801@spidmail.it"], "to_addresses": ["matteo1801@spidmail.it"],
"subject": "Test PecFlow Fase 4", "subject": "Test PEChub Fase 4",
"body_text": "Messaggio di test inviato da PecFlow.", "body_text": "Messaggio di test inviato da PEChub.",
}, },
headers=auth_headers, headers=auth_headers,
) )
+2 -2
View File
@@ -1,8 +1,8 @@
-- Estensioni PostgreSQL richieste da PecFlow -- Estensioni PostgreSQL richieste da PEChub
-- Questo script viene eseguito automaticamente da Docker al primo avvio -- Questo script viene eseguito automaticamente da Docker al primo avvio
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- Permette SET LOCAL per RLS (app.current_tenant_id) -- Permette SET LOCAL per RLS (app.current_tenant_id)
ALTER DATABASE pecflow SET "app.current_tenant_id" TO ''; ALTER DATABASE pechub SET "app.current_tenant_id" TO '';
+14 -14
View File
@@ -2,8 +2,8 @@
-- SEED: Tenant demo + utenti per sviluppo locale -- SEED: Tenant demo + utenti per sviluppo locale
-- --
-- Credenziali: -- Credenziali:
-- Admin: admin@demo.pecflow.it / Demo@PecFlow2026! -- Admin: admin@demo.pechub.it / Demo@PEChub2026!
-- Operator: operator@demo.pecflow.it / Oper@PecFlow2026! -- Operator: operator@demo.pechub.it / Oper@PEChub2026!
-- --
-- Esegui con: make seed -- Esegui con: make seed
-- ============================================================ -- ============================================================
@@ -25,27 +25,27 @@ VALUES (
ON CONFLICT (slug) DO NOTHING; ON CONFLICT (slug) DO NOTHING;
-- Utente super_admin (global, senza tenant specifico usa il tenant demo) -- Utente super_admin (global, senza tenant specifico usa il tenant demo)
-- Password: SuperAdmin@PecFlow2026! (bcrypt hash) -- Password: SuperAdmin@PEChub2026! (bcrypt hash)
INSERT INTO users (id, tenant_id, email, password_hash, full_name, role, is_active) INSERT INTO users (id, tenant_id, email, password_hash, full_name, role, is_active)
VALUES ( VALUES (
'00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000001',
'11111111-1111-1111-1111-111111111111', '11111111-1111-1111-1111-111111111111',
'superadmin@pecflow.it', 'superadmin@pechub.it',
'$2b$12$y2yq6X2f3dZi22wqWZd1aumP03IU6OWrrevRMFj9054aGnUms116W', -- SuperAdmin@PecFlow2026! '$2b$12$y2yq6X2f3dZi22wqWZd1aumP03IU6OWrrevRMFj9054aGnUms116W', -- SuperAdmin@PEChub2026!
'Super Admin PecFlow', 'Super Admin PEChub',
'super_admin', 'super_admin',
TRUE TRUE
) )
ON CONFLICT (tenant_id, email) DO NOTHING; ON CONFLICT (tenant_id, email) DO NOTHING;
-- Utente admin del tenant demo -- Utente admin del tenant demo
-- Password: Demo@PecFlow2026! (bcrypt hash) -- Password: Demo@PEChub2026! (bcrypt hash)
INSERT INTO users (id, tenant_id, email, password_hash, full_name, role, is_active) INSERT INTO users (id, tenant_id, email, password_hash, full_name, role, is_active)
VALUES ( VALUES (
'11111111-0000-0000-0000-000000000001', '11111111-0000-0000-0000-000000000001',
'11111111-1111-1111-1111-111111111111', '11111111-1111-1111-1111-111111111111',
'admin@demo.pecflow.it', 'admin@demo.pechub.it',
'$2b$12$PmyaJvF0i7ACFR39k6hfMO2.6U.FVPYma.7OyXyrGuGuokiJOfX8y', -- Demo@PecFlow2026! '$2b$12$PmyaJvF0i7ACFR39k6hfMO2.6U.FVPYma.7OyXyrGuGuokiJOfX8y', -- Demo@PEChub2026!
'Admin Demo', 'Admin Demo',
'admin', 'admin',
TRUE TRUE
@@ -53,13 +53,13 @@ VALUES (
ON CONFLICT (tenant_id, email) DO NOTHING; ON CONFLICT (tenant_id, email) DO NOTHING;
-- Utente operator del tenant demo -- Utente operator del tenant demo
-- Password: Oper@PecFlow2026! (bcrypt hash) -- Password: Oper@PEChub2026! (bcrypt hash)
INSERT INTO users (id, tenant_id, email, password_hash, full_name, role, is_active) INSERT INTO users (id, tenant_id, email, password_hash, full_name, role, is_active)
VALUES ( VALUES (
'11111111-0000-0000-0000-000000000002', '11111111-0000-0000-0000-000000000002',
'11111111-1111-1111-1111-111111111111', '11111111-1111-1111-1111-111111111111',
'operator@demo.pecflow.it', 'operator@demo.pechub.it',
'$2b$12$Z0REc7flPCD3Sb8fZHsuW.Uk2X4JiJO7HhTajNSuPiQgzppkCDmLu', -- Oper@PecFlow2026! '$2b$12$Z0REc7flPCD3Sb8fZHsuW.Uk2X4JiJO7HhTajNSuPiQgzppkCDmLu', -- Oper@PEChub2026!
'Operatore Demo', 'Operatore Demo',
'operator', 'operator',
TRUE TRUE
@@ -74,7 +74,7 @@ DO $$
BEGIN BEGIN
RAISE NOTICE '✅ Seed completato!'; RAISE NOTICE '✅ Seed completato!';
RAISE NOTICE ' Tenant demo: 11111111-1111-1111-1111-111111111111'; RAISE NOTICE ' Tenant demo: 11111111-1111-1111-1111-111111111111';
RAISE NOTICE ' Admin: admin@demo.pecflow.it / Demo@PecFlow2026!'; RAISE NOTICE ' Admin: admin@demo.pechub.it / Demo@PEChub2026!';
RAISE NOTICE ' Operator: operator@demo.pecflow.it / Oper@PecFlow2026!'; RAISE NOTICE ' Operator: operator@demo.pechub.it / Oper@PEChub2026!';
END END
$$; $$;
+23 -23
View File
@@ -1,4 +1,4 @@
name: pecflow name: pechub
services: services:
@@ -7,9 +7,9 @@ services:
image: postgres:16-alpine image: postgres:16-alpine
restart: unless-stopped restart: unless-stopped
environment: environment:
POSTGRES_DB: pecflow POSTGRES_DB: pechub
POSTGRES_USER: pecflow POSTGRES_USER: pechub
POSTGRES_PASSWORD: pecflow_dev_password POSTGRES_PASSWORD: pechub_dev_password
ports: ports:
- "5432:5432" - "5432:5432"
volumes: volumes:
@@ -17,12 +17,12 @@ services:
- ./database/init:/docker-entrypoint-initdb.d/init:ro - ./database/init:/docker-entrypoint-initdb.d/init:ro
- ./database/seeds:/docker-entrypoint-initdb.d/seeds:ro - ./database/seeds:/docker-entrypoint-initdb.d/seeds:ro
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U pecflow -d pecflow"] test: ["CMD-SHELL", "pg_isready -U pechub -d pechub"]
interval: 5s interval: 5s
timeout: 5s timeout: 5s
retries: 10 retries: 10
networks: networks:
- pecflow_net - pechub_net
# ─── Redis 7 ──────────────────────────────────────────────────────────────── # ─── Redis 7 ────────────────────────────────────────────────────────────────
redis: redis:
@@ -40,7 +40,7 @@ services:
timeout: 3s timeout: 3s
retries: 10 retries: 10
networks: networks:
- pecflow_net - pechub_net
# ─── MinIO (Object Storage S3-compatible) ─────────────────────────────────── # ─── MinIO (Object Storage S3-compatible) ───────────────────────────────────
minio: minio:
@@ -61,7 +61,7 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
networks: networks:
- pecflow_net - pechub_net
# ─── MinIO bucket initializer ─────────────────────────────────────────────── # ─── MinIO bucket initializer ───────────────────────────────────────────────
minio-init: minio-init:
@@ -72,12 +72,12 @@ services:
entrypoint: > entrypoint: >
/bin/sh -c " /bin/sh -c "
mc alias set local http://minio:9000 minioadmin minioadmin && mc alias set local http://minio:9000 minioadmin minioadmin &&
mc mb --ignore-existing local/pecflow && mc mb --ignore-existing local/pechub &&
mc anonymous set none local/pecflow && mc anonymous set none local/pechub &&
echo 'MinIO bucket pecflow creato' echo 'MinIO bucket pechub creato'
" "
networks: networks:
- pecflow_net - pechub_net
# ─── Backend FastAPI ───────────────────────────────────────────────────────── # ─── Backend FastAPI ─────────────────────────────────────────────────────────
backend: backend:
@@ -87,8 +87,8 @@ services:
restart: unless-stopped restart: unless-stopped
env_file: .env env_file: .env
environment: environment:
DATABASE_URL: postgresql+asyncpg://pecflow:pecflow_dev_password@db:5432/pecflow DATABASE_URL: postgresql+asyncpg://pechub:pechub_dev_password@db:5432/pechub
DATABASE_URL_SYNC: postgresql://pecflow:pecflow_dev_password@db:5432/pecflow DATABASE_URL_SYNC: postgresql://pechub:pechub_dev_password@db:5432/pechub
REDIS_URL: redis://redis:6379/0 REDIS_URL: redis://redis:6379/0
MINIO_ENDPOINT: minio:9000 MINIO_ENDPOINT: minio:9000
ports: ports:
@@ -107,7 +107,7 @@ services:
timeout: 5s timeout: 5s
retries: 5 retries: 5
networks: networks:
- pecflow_net - pechub_net
# ─── Frontend React (Vite dev server) ────────────────────────────────────── # ─── Frontend React (Vite dev server) ──────────────────────────────────────
frontend: frontend:
@@ -128,7 +128,7 @@ services:
depends_on: depends_on:
- backend - backend
networks: networks:
- pecflow_net - pechub_net
# ─── Nginx reverse proxy ───────────────────────────────────────────────────── # ─── Nginx reverse proxy ─────────────────────────────────────────────────────
nginx: nginx:
@@ -143,7 +143,7 @@ services:
- backend - backend
- frontend - frontend
networks: networks:
- pecflow_net - pechub_net
# ─── Worker IMAP Sync (arq) ────────────────────────────────────────────────── # ─── Worker IMAP Sync (arq) ──────────────────────────────────────────────────
worker: worker:
@@ -153,7 +153,7 @@ services:
restart: unless-stopped restart: unless-stopped
env_file: .env env_file: .env
environment: environment:
DATABASE_URL: postgresql+asyncpg://pecflow:pecflow_dev_password@db:5432/pecflow DATABASE_URL: postgresql+asyncpg://pechub:pechub_dev_password@db:5432/pechub
REDIS_URL: redis://redis:6379/0 REDIS_URL: redis://redis:6379/0
MINIO_ENDPOINT: minio:9000 MINIO_ENDPOINT: minio:9000
volumes: volumes:
@@ -166,7 +166,7 @@ services:
minio: minio:
condition: service_healthy condition: service_healthy
networks: networks:
- pecflow_net - pechub_net
# ─── GreenMail (server IMAP/SMTP mock per test) ─────────────────────────────── # ─── GreenMail (server IMAP/SMTP mock per test) ───────────────────────────────
greenmail: greenmail:
@@ -195,14 +195,14 @@ services:
profiles: profiles:
- greenmail # avviato solo con: docker compose --profile greenmail up - greenmail # avviato solo con: docker compose --profile greenmail up
networks: networks:
- pecflow_net - pechub_net
# ─── PgAdmin (solo dev) ────────────────────────────────────────────────────── # ─── PgAdmin (solo dev) ──────────────────────────────────────────────────────
pgadmin: pgadmin:
image: dpage/pgadmin4:latest image: dpage/pgadmin4:latest
restart: unless-stopped restart: unless-stopped
environment: environment:
PGADMIN_DEFAULT_EMAIL: admin@pecflow.it PGADMIN_DEFAULT_EMAIL: admin@pechub.it
PGADMIN_DEFAULT_PASSWORD: admin PGADMIN_DEFAULT_PASSWORD: admin
PGADMIN_CONFIG_SERVER_MODE: "False" PGADMIN_CONFIG_SERVER_MODE: "False"
ports: ports:
@@ -214,7 +214,7 @@ services:
profiles: profiles:
- tools - tools
networks: networks:
- pecflow_net - pechub_net
volumes: volumes:
postgres_data: postgres_data:
@@ -223,5 +223,5 @@ volumes:
pgadmin_data: pgadmin_data:
networks: networks:
pecflow_net: pechub_net:
driver: bridge driver: bridge
@@ -19,14 +19,14 @@ const STATE_COLORS: Record<PecState, string> = {
} }
const STATE_ICONS: Record<PecState, string> = { const STATE_ICONS: Record<PecState, string> = {
draft: '📝', draft: '',
queued: '', queued: '',
sent: '📤', sent: '',
accepted: '', accepted: '',
delivered: '📬', delivered: '',
received: '📥', received: '',
anomaly: '⚠️', anomaly: '',
failed: '', failed: '',
} }
const TYPE_COLORS: Record<string, string> = { const TYPE_COLORS: Record<string, string> = {
+5 -5
View File
@@ -39,7 +39,7 @@ export function useWebSocket() {
if (!message.is_read) { if (!message.is_read) {
const from = message.from_address || 'Mittente sconosciuto' const from = message.from_address || 'Mittente sconosciuto'
const subject = message.subject || '(nessun oggetto)' const subject = message.subject || '(nessun oggetto)'
toast.success(`📨 Nuova PEC da ${from}: ${subject}`, { toast.success(` Nuova PEC da ${from}: ${subject}`, {
duration: 5000, duration: 5000,
id: `new-msg-${message.id}`, id: `new-msg-${message.id}`,
}) })
@@ -55,7 +55,7 @@ export function useWebSocket() {
} }
updateMailboxStatus(mailbox_id, status, error_msg) updateMailboxStatus(mailbox_id, status, error_msg)
if (status === 'error') { if (status === 'error') {
toast.error(`⚠️ Errore sincronizzazione casella`, { duration: 8000 }) toast.error(` Errore sincronizzazione casella`, { duration: 8000 })
} }
break break
} }
@@ -67,16 +67,16 @@ export function useWebSocket() {
mailbox_id: string mailbox_id: string
} }
if (status === 'sent') { if (status === 'sent') {
toast.success(' PEC inviata con successo', { duration: 4000 }) toast.success(' PEC inviata con successo', { duration: 4000 })
} else if (status === 'failed') { } else if (status === 'failed') {
toast.error(' Invio PEC fallito definitivamente', { duration: 8000 }) toast.error(' Invio PEC fallito definitivamente', { duration: 8000 })
} }
break break
} }
case 'send_job:anomaly': { case 'send_job:anomaly': {
toast.error( toast.error(
'⚠️ Anomalia invio PEC: nessuna ricevuta di accettazione entro 24h', ' Anomalia invio PEC: nessuna ricevuta di accettazione entro 24h',
{ duration: 10000 }, { duration: 10000 },
) )
break break
+2 -2
View File
@@ -260,14 +260,14 @@ export function ComposePage() {
<option value="">Seleziona casella...</option> <option value="">Seleziona casella...</option>
{activeCaselle.map((mb) => ( {activeCaselle.map((mb) => (
<option key={mb.id} value={mb.id}> <option key={mb.id} value={mb.id}>
{mb.fromVbox ? '📥 ' : ''}{mb.display_name || mb.email_address} ({mb.email_address}) {mb.fromVbox ? ' ' : ''}{mb.display_name || mb.email_address} ({mb.email_address})
</option> </option>
))} ))}
</select> </select>
{activeCaselle.some((m) => m.fromVbox) && ( {activeCaselle.some((m) => m.fromVbox) && (
<p className="text-xs text-purple-600 flex items-center gap-1"> <p className="text-xs text-purple-600 flex items-center gap-1">
<Filter className="h-3 w-3" /> <Filter className="h-3 w-3" />
Le caselle con 📥 sono accessibili tramite Virtual Box Le caselle con sono accessibili tramite Virtual Box
</p> </p>
)} )}
</> </>
+1 -1
View File
@@ -825,7 +825,7 @@ function MessageRow({
{/* Indicatore allegati */} {/* Indicatore allegati */}
{message.has_attachments && ( {message.has_attachments && (
<span className="text-xs text-muted-foreground">📎</span> <span className="text-xs text-muted-foreground"></span>
)} )}
</div> </div>
</div> </div>
@@ -229,9 +229,9 @@ function ChannelsTab({ onEdit }: ChannelsTabProps) {
mutationFn: (id: string) => notificationsApi.testChannel(id), mutationFn: (id: string) => notificationsApi.testChannel(id),
onSuccess: (result) => { onSuccess: (result) => {
if (result.success) { if (result.success) {
toast.success(` ${result.message}`) toast.success(` ${result.message}`)
} else { } else {
toast.error(` ${result.message}`) toast.error(` ${result.message}`)
} }
}, },
onError: (e) => toast.error(getErrorMessage(e)), onError: (e) => toast.error(getErrorMessage(e)),
@@ -234,7 +234,7 @@ function PermissionRow({ perm, mailboxId, onRevoke }: PermissionRowProps) {
: 'border-muted-foreground/40 bg-background hover:border-primary', : 'border-muted-foreground/40 bg-background hover:border-primary',
)} )}
> >
{perm[field] && <span className="text-xs"></span>} {perm[field] && <span className="text-xs"></span>}
</button> </button>
</td> </td>
))} ))}
+5 -5
View File
@@ -219,8 +219,8 @@ export function SettingsPage() {
toast.success( toast.success(
updated.archival_mode === 'production' updated.archival_mode === 'production'
? ' Archiviazione attivata in modalità PRODUZIONE' ? ' Archiviazione attivata in modalità PRODUZIONE'
: '🧪 Archiviazione impostata in modalità mock' : ' Archiviazione impostata in modalità mock'
) )
} catch (err: unknown) { } catch (err: unknown) {
const msg = (err as { response?: { data?: { detail?: string } } }) const msg = (err as { response?: { data?: { detail?: string } } })
@@ -510,12 +510,12 @@ export function SettingsPage() {
Vengono salvate cifrate (AES-256-GCM). Vengono salvate cifrate (AES-256-GCM).
{archivalSettings?.conservatore_username_configured && ( {archivalSettings?.conservatore_username_configured && (
<span className="ml-1 text-green-600 font-medium"> <span className="ml-1 text-green-600 font-medium">
Username configurata Username configurata
</span> </span>
)} )}
{archivalSettings?.conservatore_password_configured && ( {archivalSettings?.conservatore_password_configured && (
<span className="ml-2 text-green-600 font-medium"> <span className="ml-2 text-green-600 font-medium">
Password configurata Password configurata
</span> </span>
)} )}
</p> </p>
@@ -613,7 +613,7 @@ export function SettingsPage() {
Credenziali:{' '} Credenziali:{' '}
{archivalSettings.conservatore_username_configured && {archivalSettings.conservatore_username_configured &&
archivalSettings.conservatore_password_configured archivalSettings.conservatore_password_configured
? <strong className="text-green-700">Configurate </strong> ? <strong className="text-green-700">Configurate </strong>
: <span className="text-gray-500">Non configurate</span> : <span className="text-gray-500">Non configurate</span>
} }
</li> </li>
@@ -276,7 +276,7 @@ function VBoxCard({
</div> </div>
) : ( ) : (
<p className="text-xs text-amber-600 mt-1"> <p className="text-xs text-amber-600 mt-1">
Nessuna casella PEC associata Nessuna casella PEC associata
</p> </p>
)} )}
</div> </div>
@@ -581,7 +581,7 @@ function VBoxFormDialog({ open, editingVbox, onClose, onSaved }: VBoxFormDialogP
)} )}
{(selectedMailboxIds ?? []).length === 0 && availableMailboxes.length > 0 && ( {(selectedMailboxIds ?? []).length === 0 && availableMailboxes.length > 0 && (
<p className="text-xs text-amber-600"> <p className="text-xs text-amber-600">
Seleziona almeno una casella PEC reale da associare. Seleziona almeno una casella PEC reale da associare.
</p> </p>
)} )}
</div> </div>
+1 -1
View File
@@ -1 +1 @@
# Worker PecFlow # Worker PEChub
+2 -2
View File
@@ -21,7 +21,7 @@ class WorkerSettings(BaseSettings):
log_level: str = "INFO" log_level: str = "INFO"
# ── Database ────────────────────────────────────────────────────────────── # ── Database ──────────────────────────────────────────────────────────────
database_url: str = "postgresql+asyncpg://pecflow:pecflow_dev_password@db:5432/pecflow" database_url: str = "postgresql+asyncpg://pechub:pechub_dev_password@db:5432/pechub"
# ── Redis ───────────────────────────────────────────────────────────────── # ── Redis ─────────────────────────────────────────────────────────────────
redis_url: str = "redis://redis:6379/0" redis_url: str = "redis://redis:6379/0"
@@ -30,7 +30,7 @@ class WorkerSettings(BaseSettings):
minio_endpoint: str = "minio:9000" minio_endpoint: str = "minio:9000"
minio_access_key: str = "minioadmin" minio_access_key: str = "minioadmin"
minio_secret_key: str = "minioadmin" minio_secret_key: str = "minioadmin"
minio_bucket: str = "pecflow" minio_bucket: str = "pechub"
minio_use_ssl: bool = False minio_use_ssl: bool = False
# ── Cifratura credenziali (ADR-002) ─────────────────────────────────────── # ── Cifratura credenziali (ADR-002) ───────────────────────────────────────
+4 -4
View File
@@ -1,5 +1,5 @@
""" """
Entrypoint worker arq PecFlow IMAP Sync Engine. Entrypoint worker arq PEChub IMAP Sync Engine.
Avvio: python -m app.main Avvio: python -m app.main
@@ -55,7 +55,7 @@ async def on_startup(ctx: dict[str, Any]) -> None:
""" """
global _mailbox_pool global _mailbox_pool
logger.info("🚀 PecFlow Worker avviato") logger.info("🚀 PEChub Worker avviato")
logger.info(f" DB: {settings.database_url.split('@')[-1]}") logger.info(f" DB: {settings.database_url.split('@')[-1]}")
logger.info(f" Redis: {settings.redis_url}") logger.info(f" Redis: {settings.redis_url}")
logger.info(f" MinIO: {settings.minio_endpoint}") logger.info(f" MinIO: {settings.minio_endpoint}")
@@ -85,7 +85,7 @@ async def on_shutdown(ctx: dict[str, Any]) -> None:
"""Cleanup all'arresto del worker.""" """Cleanup all'arresto del worker."""
global _mailbox_pool global _mailbox_pool
logger.info("🛑 PecFlow Worker in arresto...") logger.info("🛑 PEChub Worker in arresto...")
pool = ctx.get("mailbox_pool") or _mailbox_pool pool = ctx.get("mailbox_pool") or _mailbox_pool
if pool: if pool:
@@ -161,5 +161,5 @@ class WorkerSettings:
# ─── Entrypoint ─────────────────────────────────────────────────────────────── # ─── Entrypoint ───────────────────────────────────────────────────────────────
if __name__ == "__main__": if __name__ == "__main__":
logger.info("Avvio PecFlow Worker (arq)...") logger.info("Avvio PEChub Worker (arq)...")
run_worker(WorkerSettings) run_worker(WorkerSettings)
+1 -1
View File
@@ -115,7 +115,7 @@ class SmtpSender:
body_container = msg body_container = msg
# Headers obbligatori # Headers obbligatori
message_id = make_msgid(domain="pecflow.local") message_id = make_msgid(domain="pechub.local")
msg["From"] = self.mailbox.email_address msg["From"] = self.mailbox.email_address
msg["To"] = ", ".join(to_addresses) msg["To"] = ", ".join(to_addresses)
if cc_addresses: if cc_addresses:
+2 -2
View File
@@ -1,8 +1,8 @@
""" """
Client MinIO/S3 asincrono per il worker. Client MinIO/S3 asincrono per il worker.
Percorso EML raw: pecflow/tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml Percorso EML raw: pechub/tenants/{tenant_id}/mailboxes/{mailbox_id}/raw/{uid}.eml
Percorso allegati: pecflow/tenants/{tenant_id}/mailboxes/{mailbox_id}/attachments/{msg_id}/{filename} Percorso allegati: pechub/tenants/{tenant_id}/mailboxes/{mailbox_id}/attachments/{msg_id}/{filename}
""" """
import io import io
@@ -11,7 +11,7 @@ Testa:
5. Rilevazione ricevute accettazione/consegna 5. Rilevazione ricevute accettazione/consegna
Eseguire DENTRO il container worker: Eseguire DENTRO il container worker:
docker exec -e PYTHONPATH=/worker pecflow-worker-1 python \ docker exec -e PYTHONPATH=/worker pechub-worker-1 python \
/worker/tests/integration/test_smtp_real_aruba.py /worker/tests/integration/test_smtp_real_aruba.py
""" """
@@ -28,7 +28,7 @@ from unittest.mock import MagicMock
if "ENCRYPTION_KEY" not in os.environ: if "ENCRYPTION_KEY" not in os.environ:
os.environ["ENCRYPTION_KEY"] = "6465762d656e6372797074696f6e2d6b65792d6e6f742d666f722d70726f6400" os.environ["ENCRYPTION_KEY"] = "6465762d656e6372797074696f6e2d6b65792d6e6f742d666f722d70726f6400"
os.environ.setdefault("SECRET_KEY", "dev-secret-key-not-for-production-use-only-for-local-0000000000000") os.environ.setdefault("SECRET_KEY", "dev-secret-key-not-for-production-use-only-for-local-0000000000000")
os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://pecflow:pecflow_dev_password@db:5432/pecflow") os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://pechub:pechub_dev_password@db:5432/pechub")
os.environ.setdefault("REDIS_URL", "redis://redis:6379/0") os.environ.setdefault("REDIS_URL", "redis://redis:6379/0")
os.environ.setdefault("MINIO_ENDPOINT", "minio:9000") os.environ.setdefault("MINIO_ENDPOINT", "minio:9000")
@@ -157,9 +157,9 @@ async def test_smtp_send() -> tuple[str | None, bool]:
sender = SmtpSender(mailbox) sender = SmtpSender(mailbox)
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
subject = f"[PecFlow TEST] Verifica SMTP+Ricevute {ts}" subject = f"[PEChub TEST] Verifica SMTP+Ricevute {ts}"
body = ( body = (
f"Messaggio di test automatico PecFlow {ts}\n\n" f"Messaggio di test automatico PEChub {ts}\n\n"
"Verifica:\n" "Verifica:\n"
" 1. Connessione SMTP SSL porta 465 → smtps.pec.aruba.it\n" " 1. Connessione SMTP SSL porta 465 → smtps.pec.aruba.it\n"
" 2. Autenticazione e invio PEC\n" " 2. Autenticazione e invio PEC\n"
@@ -167,7 +167,7 @@ async def test_smtp_send() -> tuple[str | None, bool]:
" 4. Ricezione ricevuta di avvenuta consegna\n\n" " 4. Ricezione ricevuta di avvenuta consegna\n\n"
f"Mittente : {PEC_EMAIL}\n" f"Mittente : {PEC_EMAIL}\n"
f"Destinato: {TO_ADDRESS}\n\n" f"Destinato: {TO_ADDRESS}\n\n"
"Non rispondere. Generato da PecFlow SaaS.\n" "Non rispondere. Generato da PEChub SaaS.\n"
) )
_sep() _sep()
@@ -458,7 +458,7 @@ async def test_imap_full_inspection() -> None:
print(" • I messaggi in INBOX non sono ancora stati aggiornati") print(" • I messaggi in INBOX non sono ancora stati aggiornati")
print() print()
print(" 💡 Ri-esegui lo STEP 2 manualmente tra qualche secondo con:") print(" 💡 Ri-esegui lo STEP 2 manualmente tra qualche secondo con:")
print(" docker exec -e PYTHONPATH=/worker pecflow-worker-1 python -c \"") print(" docker exec -e PYTHONPATH=/worker pechub-worker-1 python -c \"")
print(" import asyncio, sys; sys.path.insert(0,'/worker')") print(" import asyncio, sys; sys.path.insert(0,'/worker')")
print(" from tests.integration.test_smtp_real_aruba import test_imap_full_inspection") print(" from tests.integration.test_smtp_real_aruba import test_imap_full_inspection")
print(" asyncio.run(test_imap_full_inspection())\"") print(" asyncio.run(test_imap_full_inspection())\"")
@@ -481,7 +481,7 @@ def _sep(char: str = "─", width: int = 60) -> None:
def _banner() -> None: def _banner() -> None:
_sep("") _sep("")
print(" PecFlow Test SMTP/IMAP Reale (Aruba PEC)") print(" PEChub Test SMTP/IMAP Reale (Aruba PEC)")
_sep("") _sep("")
print(f" Timestamp : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print(f" Timestamp : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Casella PEC : {PEC_EMAIL}") print(f" Casella PEC : {PEC_EMAIL}")