mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 12:45:42 +02:00
Cambio nome
This commit is contained in:
@@ -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 @@
|
|||||||
# PecFlow Backend
|
# PEChub Backend
|
||||||
|
|||||||
@@ -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,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Eccezioni applicative custom per PecFlow.
|
Eccezioni applicative custom per PEChub.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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.)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 '';
|
||||||
|
|||||||
@@ -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
@@ -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> = {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -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>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -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 @@
|
|||||||
# Worker PecFlow
|
# Worker PEChub
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
Reference in New Issue
Block a user