Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ LOCALSTACK_AWS_SESSION_TOKEN=


# -----------------------------------------------------------------------------
# Frontend SPA statico locale (S3 LocalStack + CloudFront locale)
# Frontend SPA statico locale (S3 LocalStack + CDN/edge locale)
# -----------------------------------------------------------------------------
# Queste variabili riguardano SOLO il serving della SPA Angular.
# Non usarle per i documenti e non sostituirle con bucket S3 reali.
# I documenti restano governati da POC_DOCUMENT_DISK/AWS_REAL_* più sotto.
FRONTEND_STATIC_BUCKET=poc-frontend-static-local
FRONTEND_CLOUDFRONT_LOCAL_URL=https://localhost:8443
EDGE_CDN_LOCAL_URL=https://localhost:8443


# -----------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ jobs:
- name: Apply LocalStack infrastructure
run: docker compose --profile tools run --rm terraform init && docker compose --profile tools run --rm terraform apply -auto-approve

# Il percorso di serving default e' Traefik -> frontend-cloudfront -> S3
# Il percorso di serving default e' Traefik -> edge-cdn -> S3
# LocalStack: lo smoke e gli audit a11y devono colpire la SPA realmente
# caricata nel bucket, non un bucket vuoto. Si riusa la stessa logica di
# make setup (build + sync con cache-control differenziato).
Expand Down Expand Up @@ -310,7 +310,7 @@ jobs:
echo "$spa_headers" | grep -i '^x-content-type-options: nosniff'
echo "$spa_headers" | grep -i '^referrer-policy: strict-origin-when-cross-origin'

# La SPA Angular deve essere servita da S3 LocalStack via frontend-cloudfront,
# La SPA Angular deve essere servita da S3 LocalStack via edge-cdn,
# sia sulla root sia su un deep link (fallback SPA verso index.html)
curl --silent --fail --insecure https://localhost:8443/ | grep -q '<poc-root'
curl --silent --fail --insecure https://localhost:8443/copilot | grep -q '<poc-root'
Expand Down
20 changes: 10 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.PHONY: help test pint node-install frontend-build frontend-lint frontend-test frontend-typecheck frontend-audit frontend-a11y frontend-s3-local-provision frontend-s3-local-upload frontend-s3-local-deploy frontend-cloudfront-local-url frontend-serving-local-test openapi-generate openapi-validate observability-config observability-up local-tls trusted-local-tls fresh logs sh restart setup release infra-up infra-init infra-plan infra-apply infra-destroy refresh-runtime verify verify-fast verify-backend verify-frontend verify-infra verify-observability verify-ci-local aws-smoke reset-all workers backup-local restore-local
.PHONY: help test pint node-install frontend-build frontend-lint frontend-test frontend-typecheck frontend-audit frontend-a11y frontend-s3-local-provision frontend-s3-local-upload frontend-s3-local-deploy edge-cdn-local-url frontend-serving-local-test openapi-generate openapi-validate observability-config observability-up local-tls trusted-local-tls fresh logs sh restart setup release infra-up infra-init infra-plan infra-apply infra-destroy refresh-runtime verify verify-fast verify-backend verify-frontend verify-infra verify-observability verify-ci-local aws-smoke reset-all workers backup-local restore-local

# Colori per l'output
BLUE := \033[34m
RESET := \033[0m
LOCALSTACK_ENDPOINT_INTERNAL ?= http://localstack:4566
FRONTEND_DIST ?= apps/frontend/dist
FRONTEND_STATIC_BUCKET ?= poc-frontend-static-local
FRONTEND_CLOUDFRONT_LOCAL_URL ?= https://localhost:8443
EDGE_CDN_LOCAL_URL ?= https://localhost:8443
# -T: niente pseudo-TTY per i tool non interattivi. Senza, "docker compose run"
# mette il terminale in raw mode e, se il run si blocca (es. glitch del daemon),
# Ctrl+C viene inghiottito e l'unico modo è chiudere il terminale — che però NON
Expand Down Expand Up @@ -41,8 +41,8 @@ help:
@echo " $(BLUE)make frontend-a11y$(RESET) Esegue axe e Pa11y sullo stack HTTPS locale"
@echo " $(BLUE)make frontend-s3-local-provision$(RESET) Provisiona il bucket S3 locale della SPA"
@echo " $(BLUE)make frontend-s3-local-deploy$(RESET) Builda e carica la SPA Angular su S3 LocalStack"
@echo " $(BLUE)make frontend-cloudfront-local-url$(RESET) Stampa URL del CloudFront locale"
@echo " $(BLUE)make frontend-serving-local-test$(RESET) Smoke test del serving S3 locale + CloudFront locale"
@echo " $(BLUE)make edge-cdn-local-url$(RESET) Stampa URL della CDN/edge locale"
@echo " $(BLUE)make frontend-serving-local-test$(RESET) Smoke test del serving S3 locale + CDN/edge locale"
@echo " $(BLUE)make openapi-generate$(RESET) Rigenera il client TypeScript"
@echo " $(BLUE)make openapi-validate$(RESET) Valida il contratto OpenAPI"
@echo " $(BLUE)make observability-config$(RESET) Valida la configurazione OTel Collector"
Expand Down Expand Up @@ -132,14 +132,14 @@ frontend-s3-local-upload:
frontend-s3-local-deploy: frontend-build frontend-s3-local-provision
$(MAKE) frontend-s3-local-upload

frontend-cloudfront-local-url:
@printf "%s\n" "$(FRONTEND_CLOUDFRONT_LOCAL_URL)"
edge-cdn-local-url:
@printf "%s\n" "$(EDGE_CDN_LOCAL_URL)"
@printf "\n"

frontend-serving-local-test: frontend-s3-local-deploy
@if [ ! -f docker/traefik/certs/poc-local.test.crt ] || [ ! -f docker/traefik/certs/poc-local.test.key ]; then $(MAKE) local-tls; fi
docker compose up -d --wait --force-recreate frontend-cloudfront traefik
@url="$(FRONTEND_CLOUDFRONT_LOCAL_URL)"; \
docker compose up -d --wait --force-recreate edge-cdn traefik
@url="$(EDGE_CDN_LOCAL_URL)"; \
echo "$(BLUE)Testing $$url$(RESET)"; \
curl -kfsS "$$url" | grep -q "<poc-root"

Expand All @@ -148,7 +148,7 @@ frontend-audit: node-install

frontend-a11y: frontend-s3-local-deploy
@if [ ! -f docker/traefik/certs/poc-local.test.crt ] || [ ! -f docker/traefik/certs/poc-local.test.key ]; then $(MAKE) local-tls; fi
docker compose up -d --wait --force-recreate app nginx frontend-cloudfront traefik
docker compose up -d --wait --force-recreate app nginx edge-cdn traefik
$(FRONTEND_AUDIT) node scripts/a11y/csp-smoke.mjs https://traefik:8443
$(FRONTEND_AUDIT) node scripts/a11y/axe-playwright.mjs https://traefik:8443
$(FRONTEND_AUDIT) node scripts/a11y/pa11y-runner.mjs https://traefik:8443
Expand Down Expand Up @@ -183,7 +183,7 @@ fresh:
docker compose exec -T redis redis-cli FLUSHALL

logs:
docker compose logs -f app queue nginx frontend-cloudfront localstack
docker compose logs -f app queue nginx edge-cdn localstack

sh:
docker compose run --rm app sh
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ La PoC dimostra un modello applicativo composto da più livelli cooperanti:
* **Redis** per cache, sessioni e rate limiting;
* storage documentale **S3-compatible** per PDF originali e sotto-documenti generati;
* **LocalStack** per emulare localmente servizi AWS come SQS, Step Functions, SSM, Secrets Manager e S3;
* **CloudFront locale** davanti al bucket S3 LocalStack per il serving della SPA Angular;
* **emulatore CDN locale** (Nginx) davanti al bucket S3 LocalStack per il serving della SPA Angular (in produzione: AWS CloudFront);
* integrazione AI tramite astrazione verso **Bedrock** e integrazione OCR tramite **Textract** (attivabile, disabilitata di default);
* stack di osservabilità con **OpenTelemetry, Prometheus, Grafana, Tempo, Loki, Alloy e Alertmanager**;
* CI con test backend/frontend, scansione immagini, validazione infrastrutturale e audit accessibilità.
Expand All @@ -45,7 +45,7 @@ La separazione tra richiesta HTTP e workflow asincrono è uno dei punti centrali

L’architettura locale è organizzata intorno a un entrypoint edge, un layer applicativo, servizi dati, workflow asincroni e osservabilità.

Traefik gestisce l’ingresso verso i servizi esposti e instrada il traffico applicativo verso il CloudFront locale. Il CloudFront locale serve la SPA Angular dagli oggetti caricati nel bucket S3 LocalStack e inoltra `/api/`, `/health` e `/ready` a Nginx/Laravel. Nginx resta il proxy applicativo verso PHP-FPM e il percorso interno di compatibilità. PostgreSQL conserva lo stato persistente, Redis supporta componenti runtime a bassa latenza, mentre LocalStack fornisce servizi AWS-like in ambiente locale.
Traefik gestisce l’ingresso verso i servizi esposti e instrada il traffico applicativo verso l’emulatore CDN locale. Quest’ultimo (`edge-cdn`) è un secondo Nginx che emula il ruolo di una CDN/edge — non Amazon CloudFront — servendo la SPA Angular dagli oggetti caricati nel bucket S3 LocalStack e inoltrando `/api/`, `/health` e `/ready` all’Nginx applicativo/Laravel. È un container separato dall’Nginx applicativo, che è un’immagine di produzione e non deve conoscere LocalStack; quest’ultimo resta il proxy verso PHP-FPM e il percorso interno di compatibilità. PostgreSQL conserva lo stato persistente, Redis supporta componenti runtime a bassa latenza, mentre LocalStack fornisce servizi AWS-like in ambiente locale.

I worker Laravel consumano task asincroni da SQS e comunicano con Step Functions tramite callback task token. Questo permette di rappresentare una pipeline documentale composta da stati espliciti, retry, gestione errori, idempotenza e aggiornamento progressivo dello stato.

Expand Down Expand Up @@ -114,11 +114,11 @@ make logs

```bash
make frontend-s3-local-deploy
make frontend-cloudfront-local-url
make edge-cdn-local-url
make frontend-serving-local-test
```

Il flusso builda Angular, provisiona il bucket S3 LocalStack via Terraform, carica `apps/frontend/dist` con cache-control differenziato (`index.html` no-cache, bundle hashati immutable) e verifica il serving attraverso il CloudFront locale su `https://localhost:8443`. La simulazione valida il pattern build → bucket → distribuzione CDN-like locale, ma non sostituisce una validazione CloudFront reale con TLS/OAC/edge propagation/invalidation AWS.
Il flusso builda Angular, provisiona il bucket S3 LocalStack via Terraform, carica `apps/frontend/dist` con cache-control differenziato (`index.html` no-cache, bundle hashati immutable) e verifica il serving attraverso l’emulatore CDN locale su `https://localhost:8443`. L’emulazione valida il pattern build → bucket → distribuzione edge in locale, ma non sostituisce una CDN reale (in produzione AWS CloudFront, con TLS/OAC/edge propagation/invalidation).

Il bucket `FRONTEND_STATIC_BUCKET` è dedicato solo alla SPA. I documenti continuano a usare `POC_DOCUMENT_DISK=s3` per S3 LocalStack o `POC_DOCUMENT_DISK=real_s3` con `AWS_REAL_*` per S3/Textract reali.

Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ make frontend-s3-local-deploy

The root package uses npm workspaces. Frontend commands run through the Docker Compose `node` tool container (`node:22-bookworm-slim`), not through the host Node runtime.

The app calls Laravel with relative `/api/v1` URLs by default. `proxy.conf.json` keeps `ng serve` aligned with the local Traefik/Nginx entrypoint, while production builds are static and can be served by Nginx or by the LocalStack S3/CloudFront simulation.
The app calls Laravel with relative `/api/v1` URLs by default. `proxy.conf.json` keeps `ng serve` aligned with the local Traefik/Nginx entrypoint, while production builds are static and can be served by Nginx or, in the default local flow, by the local CDN emulator (a separate Nginx) in front of the LocalStack S3 bucket — the role a real CDN such as AWS CloudFront would play in production.
8 changes: 4 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ services:
timeout: 5s
retries: 3
depends_on:
frontend-cloudfront:
edge-cdn:
condition: service_healthy
networks:
- edge
Expand Down Expand Up @@ -169,12 +169,12 @@ services:
- edge
- backend

frontend-cloudfront:
edge-cdn:
image: nginx:1.29-alpine
environment:
FRONTEND_STATIC_BUCKET: ${FRONTEND_STATIC_BUCKET:-poc-frontend-static-local}
volumes:
- ./docker/cloudfront/default.conf.template:/etc/nginx/templates/default.conf.template:ro
- ./docker/edge-cdn/default.conf.template:/etc/nginx/templates/default.conf.template:ro
depends_on:
localstack:
condition: service_healthy
Expand Down Expand Up @@ -277,7 +277,7 @@ services:
TF_VAR_runtime_secret_name: ${CONFIG_SECRET_IDS:-/poc/app/runtime}
TF_VAR_app_url: ${POC_APP_URL:-https://localhost:8443}
TF_VAR_frontend_static_bucket: ${FRONTEND_STATIC_BUCKET:-poc-frontend-static-local}
TF_VAR_frontend_cloudfront_local_url: ${FRONTEND_CLOUDFRONT_LOCAL_URL:-https://localhost:8443}
TF_VAR_edge_cdn_local_url: ${EDGE_CDN_LOCAL_URL:-https://localhost:8443}
TF_VAR_db_database: ${POC_DB_DATABASE:-poc}
TF_VAR_db_username: ${POC_DB_USERNAME:-poc}
TF_VAR_db_password: ${POC_DB_PASSWORD:-poc-local-password}
Expand Down
6 changes: 3 additions & 3 deletions docker/traefik/dynamic/http.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ http:
rule: Host(`localhost`) || Host(`poc.localhost`) || Host(`traefik`)
entryPoints:
- websecure
service: poc-frontend-cloudfront
service: poc-edge-cdn
tls: {}

# Dashboard di osservabilità: nessuna porta host, si entra solo da qui.
Expand Down Expand Up @@ -49,11 +49,11 @@ http:
usersFile: /etc/traefik/usersfile

services:
poc-frontend-cloudfront:
poc-edge-cdn:
loadBalancer:
passHostHeader: true
servers:
- url: http://frontend-cloudfront:8080
- url: http://edge-cdn:8080
grafana:
loadBalancer:
servers:
Expand Down
Loading
Loading