Installazione

La distribuzione dei servizi della piattaforma avviene mediante immagini docker, non ci sono requisiti particolari in merito all'orchestratore utilizzato (docker swarm, kubernetes, nomad, mesos).

Architettura generale

Installazione ambiente di Test / Sviluppo

Per una installazione di test su singolo nodo si consiglia di usare il docker-compose.yml presente nel repository principale.

Installazione ambiente di Produzione

Per un ambiente di produzione è indispensabile predisporre un ambiente con adeguati livelli di ridondanza e dimensionamento dei singoli servizi in base alla propria infrastruttura e ai propri requisiti in termini di affidabilità, livelli di servizio.

Elenco dei servizi necessari

Ogni servizio si basa su una immagine docker specifica e necessita di adeguate configurazioni. Le configurazioni e i valori di default delle stesse sono documentate nei singoli repository dei microservizi.

Al fine di dare una corretta configurazione dei vari servizi vengono indicate inoltre:

  1. la dipendenza dalle persistenze usate dalla piattaforma

  2. la necessità di pubblicare un endpoint per quel servizio pubblicamente raggiungibile

ServizioDescrizioneDocker ImagePubblicoPersistenza

app-web

Servizio principale della piattaforma, gestisce pratiche, appuntamenti, offre interfacce di configurazione per gli amministratori e di gestione operativa per i funzionari degli enti. E' inoltre l'interfaccia principale anche per l'esperienza utente.

PostgreSQLFile StorageRedis

app-worker

Esecuzione delle operazioni asincrone del Core (creazione PDF, invio di messaggi, webhooks, ...)

PostgreSQLFile Storage

app-manager

Esecuzione delle migrazioni database

PostgreSQL

app-varnish

Http cache per scaricare l'app-web

form-server

Server delle form (Form.IO)

MongoDB

form-varnish

Http cache per scaricare il form-server

form-builder

Applicativo per la gestione dei subform condivisi da tutti i moduli form.io

payment- dispatcher-v1

Genera, se necessario, un evento Versione 1 di pagamento da un evento di una pratica

Kafka

payment- dispatcher-v2

Genera, se necessario, un evento Versione 2 di pagamento da un evento di una pratica

Kafka

payment-updater

Ascolta sul topic di kafka dei pagamenti e aggiorna lo stato delle pratiche di conseguenza

Kafka

gotenberg

API interna per la creazione di file PDF

ksqldb

Ascolta su tutti i topic di kafka ed espone via API dati aggregati sugli eventi della piattaforma

Kafka

kafka-core-api

Espone un endpoint http che il core usa per inviare eventi sui topic di kafka

KafkaFile Storage

payments poller

Legge da Ksql i pagamenti pendenti e per ognuno di essi chiama il proxy che lo gestisce per aggiornare lo stato del pagamento

XXX-payment proxy

Servizio per l'integrazione con l'intermediario di pagamento XXX (vedi Integrazioni per la lista completa)

KafkaFile Storage

YYY-protocol- proxy

Servizio per l'integrazione con il sistema di protocollo YYY (vedi Integrazioni per la lista completa)

File StorageKafka

registry-api

API per l'app Django con cui abbiamo integrato i protocolli AAA BBB CCC

PostgreSQL

registry-scheduler

Scheduler per gli errori registrati durante un tentativo di protocollazione

PostgreSQLKafka

analytics-services-aggregator

Servizio per il calcolo dei KPI sui Servizi

KafkaClickHouse

analytics-first-availability-aggregator

Servizio per il calcolo dei KPI sui Calendari

KafkaClickHouse

analytics-bookings-aggregator

Servizio per il calcolo dei KPI sui Calendari

KafkaClickHouse

analytics-charts-exporter

API per la generazione dei grafici degli Analytics

ClickHouse

satisfy-hasura

API per il servizio di Customer Satisfaction

PostgreSQL

satisfy-api

API per la raccolta di ratings e questionari di Customer Satisfaction

Kafka

satisfy-ratings-aggregator

Servizio per il calcolo dei KPI della Customer Satisfaction

Kafka

Pdnd-connector

Servizio che gestisce l'interazione con la pdnd e gli enti erogatori degli e-service utilizzati dalla piattaforma

File Storage

Esempio di un file di deployment per orchestratore Docker Swarm:

File di esempio
version: "3.8"
# vim: autoindent tabstop=2 shiftwidth=2 expandtab softtabstop=2 filetype=yaml fileencoding=utf-8

networks:
  oc-prod-public:             # usata per pubblicare servizi
    external: true
  oc-prod-internal:           # usata per la comunicazione tra i componenti dello stack
    external: true

x-deploy: &deploy-snippet
      endpoint_mode: dnsrr
      placement:
        max_replicas_per_node: 2
        constraints: [ node.labels.apps == true ]
      replicas: 1
      update_config:
        parallelism: 1
        order: start-first
        delay: 30s
        failure_action: rollback
        monitor: 10s
      rollback_config:
        parallelism: 1
        delay: 10s
        failure_action: pause         # default: pause or continue
        monitor: 10s                  # default: 5s
        order: start-first
      restart_policy:
        condition: any
        delay: 15s                    # default: 5s, between attempts
        max_attempts: 50
        window: 20s                   # time to decide if the restart has been sucessfull
      resources:
        limits:
          cpus: '0.2'
          memory: 512M
        reservations:
          memory: 32M


x-sdc-env: &sdc-env
    ENV: prod
    APP_ENV: prod
    DB_DRIVER: pdo_pgsql
    DB_HOST: 'postgres'
    DB_PORT: 5432
    DB_NAME: oc
    DB_USER: oc
    DB_VERSION: 14
    DB_PASSWORD: 'oc'
    MAILER_URL: 'smtp://mailserver:1025'
    SECRET: change_me
    DEFAULT_FROM_EMAIL_ADDRESS: no-reply@opencity.it
    EZ_PASSWORD: change_me
    OCSDC_SCHEME: https
    OCSDC_HOST: app.localtest.me
    LOGS_PATH: php://stderr
    ENABLE_MIGRATIONS: 'false'
    ENABLE_INSTANCE_CONFIG: 'false'
    # accesso in scrittura da PHP
    FORMSERVER_PRIVATE_URL: http://form-server:8000
    # accesso con cache varnish
    FORMSERVER_PUBLIC_URL: http://form.localtest.me
    # accesso readonly per admin
    FORMSERVER_ADMIN_URL: http://forms-readonly.localtest.me
    WKHTMLTOPDF_SERVICE: http://gotenberg:3000
    PHP_FPM_USER: wodby
    PHP_FPM_GROUP: wodby
    PHP_APCU_ENABLED: 1
    PHP_OPCACHE_ENABLE: 1
    PHP_FPM_CLEAR_ENV: 'no'
    PHP_FPM_PM_MAX_CHILDREN: 50
    PHP_FPM_PM_MAX_REQUESTS: 2000
    PHP_FPM_PM_MAX_SPARE_SERVERS: 8
    PHP_FPM_PM_MIN_SPARE_SERVERS: 2
    PHP_FPM_REQUEST_SLOWLOG_TIMEOUT: 4000
    PHP_FPM_PM_START_SERVERS: 5
    PHP_DISPLAY_ERRORS: 'off'
    PHP_DISPLAY_STARTUP_ERRORS: 'off'
    PHP_ERROR_REPORTING: 'E_ERROR'
    PHP_DATE_TIMEZONE: 'Europe/Rome'
    PHP_SESSION_SAVE_HANDLER: redis
    PHP_SESSION_SAVE_PATH: 'tcp://redis:6379'
    PHP_SESSION_GC_MAXLIFETIME: 2880
    RECAPTCHA_KEY: recaptcha_key
    RECAPTCHA_SECRET: recaptcha_secret
    EWZ_RECAPTCHA_SITE_KEY: recaptcha_key
    EWZ_RECAPTCHA_SECRET: recaptcha_secret
    FEATURE_NEW_OUTDATED_BROWSER: 'true'
    FEATURE_APPLICATION_DETAIL: 'true'
    FEATURE_CALENDAR_TYPE: 'true'
    #FEATURE_DUE_AMOUNT: 'true'
    #FEATURE_ANALYTICS: 'true'
    TOKEN_TTL: 864000 # 10gg
    CACHE_MAX_AGE: 3600
    KAFKA_URL: vector
    KSQLDB_URL: ksqldb-server:8088
    SKIP_CACHE_WARMUP: 'false'
    TRUSTED_PROXIES: '10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
    DEFAULT_CACHE_REDIS_PROVIDER: 'redis://redis:6379'
    DEFAULT_CACHE_LIFETIME: 3600
    DEFAULT_CACHE_PREFIX_SEED: v-2.27.0
    REGISTRY_API_URL: https://registry.qa.stanzadelcittadino.it
    REGISTRY_API_KEY: change_me
    API_USER_PASSWORD: change_me
    # In assenza di una vera autenticazione basata su SPID si può simulare
    # un login di un utente con le seguenti variabili d'ambiente:
    shibb_pat_attribute_codicefiscale: CLNVTR76P01G822Q
    shibb_pat_attribute_cognome: Coliandro
    shibb_pat_attribute_nome: Vittorino
    shibb_pat_attribute_sesso: M
    shibb_pat_attribute_emailaddress: info@comune.bugliano.pi.it
    shibb_pat_attribute_datanascita: 1/9/1976
    shibb_pat_attribute_luogonascita: Ponsacco
    shibb_pat_attribute_provincianascita: PI
    shibb_pat_attribute_telefono: 003912378945
    shibb_pat_attribute_cellulare: 333 444 666 99
    shibb_pat_attribute_indirizzoresidenza: 'Via Gramsci, 1'
    shibb_pat_attribute_capresidenza: 56056
    shibb_pat_attribute_cittaresidenza: Bugliano
    shibb_pat_attribute_provinciaresidenza: PI
    shibb_pat_attribute_statoresidenza: Italia
    shibb_pat_attribute_spidcode: 123456789
    shibb_pat_attribute_x509certificate_issuerdn: FAKE_issuerdn
    shibb_pat_attribute_x509certificate_subjectdn: FAKE_subjectdn
    shibb_pat_attribute_x509certificate_base64: "DQpSZXN1bHQgZ29lcyBoZXJlLi4uDQpCYXNlNjQNCg0KQmFzZTY0IGlzIGEgZ2VuZXJpYyB0ZXJtIGZvciBhIG51bWJlciBvZiBzaW1pbGFyIGVuY29kaW5nIHNjaGVtZXMgdGhhdCBlbmNvZGUgYmluYXJ5IGRhdGEgYnkgdHJlYXRpbmcgaXQgbnVtZXJpY2FsbHkgYW5kIHRyYW5zbGF0aW5nIGl0IGludG8gYSBiYXNlIDY0IHJlcHJlc2VudGF0aW9uLiBUaGUgQmFzZTY0IHRlcm0gb3JpZ2luYXRlcyBmcm9tIGEgc3BlY2lmaWMgTUlNRSBjb250ZW50IHRyYW5zZmVyIGVuY29kaW5nLg=="
    shibb_Shib-Session-ID: abc123abc123abc123abc123abc123abc123abc123
    shibb_Shib-Session-Index: abc123abc123abc123abc123abc123abc123abc123
    shibb_Shib-Authentication-Instant: 2000-01-01T00-00Z

x-registry-env: &registry-env
  DJANGO_DATABASE: postgres:5432:oc_registry:oc:oc
  DJANGO_SECRET_KEY: change_me
  DJANGO_SETTINGS_MODULE: application_registry.settings_production
  KAFKA_BOOTSTRAP_SERVERS: kafka:9092
  SENTRY_DSN:

configs:
  sf-config-instances:
    name: oc-instances-v1
    file: ./config/app/instances.yml
  vector-api:
    name: occ-kafka-http-v1
    file: ./config/kafka/vector.toml

services:

  # Symfony-core, esposto solo su rete interna
  app:
    image: registry.gitlab.com/opencity-labs/area-personale/core/app:2.27.0
    stop_grace_period: 45s
    configs:
      - source: sf-config-instances
        target: /var/www/html/config/instances_prod.yml
    networks:
      - oc-prod-internal
    environment:
      <<: *sdc-env
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/health-check"]
      interval: 1m30s
      timeout: 10s
      retries: 6
      start_period: 5s
    deploy:
      <<: *deploy-snippet
      endpoint_mode: vip
      replicas: 1
      resources:
        limits:
          cpus: '1'
          memory: 2048M
        reservations:
          memory: 1024M
      labels:
        traefik.enable: 'false'

  # Frontend del symfony-core
  varnish:
    image: registry.gitlab.com/opencontent/varnish:1.2
    stop_grace_period: 45s
    networks:
      - oc-prod-public
      - oc-prod-internal
    healthcheck:
      disable: true
    deploy:
      <<: *deploy-snippet
      endpoint_mode: dnsrr
      replicas: 1
      resources:
        limits:
          cpus: '1'
          memory: 2048M
        reservations:
          memory: 1024M
      labels:
        traefik.enable: 'true'
        traefik.http.services.varnish.loadbalancer.server.port: 6081
        # disabilitato altrimenti quando fallisce backend non sfrutto grace
        #traefik.http.services.varnish.loadbalancer.healthcheck.path: /health-check

        # Security headers
        traefik.http.middlewares.security.headers.frameDeny: 'true'
        traefik.http.middlewares.security.headers.customFrameOptionsValue: 'SAMEORIGIN'
        traefik.http.middlewares.security.headers.stsSeconds: 31536000
        traefik.http.middlewares.security.headers.stsIncludeSubdomains: 'true'
        traefik.http.middlewares.security.headers.forceStsHeader: 'true'
        #traefik.http.middlewares.security.headers.contentSecurityPolicy: "default-src 'self' data:; img-src 'self' data: blob: c.bing.com www.googletagmanager.com *.openstreetmap.org c.clarity.ms cartodb-basemaps-b.global.ssl.fastly.net cartodb-basemaps-c.global.ssl.fastly.net cartodb-basemaps-a.global.ssl.fastly.nett; style-src 'self' 'unsafe-inline' static.opencityitalia.it satisfy.opencontent.it widget.freshworks.com stackpath.bootstrapcdn.com unpkg.com cdn.datatables.net printjs-4de6.kxcdn.com cdnjs.cloudflare.com fonts.googleapis.com; connect-src 'self' s3.eu-west-1.amazonaws.com api.opencityitalia.it qa-genova-id.boat.opencontent.io dev-genova-id.boat.opencontent.io mappe.genova.opencityitalia.it api.opencontent.it api.qa.stanzadelcittadino.it registry.qa.stanzadelcittadino.it satisfy.boat.opencontent.io static.opencityitalia.it www.giscom.cloud nominatim.openstreetmap.org a.clarity.ms d.clarity.ms k.clarity.ms i.clarity.ms h.clarity.ms widget.freshworks.com fiscalcode.opencontent.it form-qa.stanzadelcittadino.it form-readonly-qa.stanzadelcittadino.it cdn.datatables.net satisfy.opencityitalia.it satisfy.opencontent.it satisfy.hasura.app sdc-analytics-dev-charts-exporter.boat.opencontent.io efil-proxy-qa.boat.opencontent.io pmpay-proxy-qa.boat.opencontent.io iris-proxy-qa.boat.opencontent.io mypay-proxy-qa.boat.opencontent.io www.openstreetmap.org flyimg.opencontent.it; script-src 'self' 'unsafe-inline' 'unsafe-eval' static.opencityitalia.it cdn.announcekit.app widget.freshworks.com www.clarity.ms form-qa.stanzadelcittadino.it cdnjs.cloudflare.com cdn.form.io unpkg.com code.jquery.com www.googletagmanager.com cdn.datatables.net printjs-4de6.kxcdn.com satisfy.opencityitalia.it satisfy.opencontent.it www.recaptcha.net www.gstatic.com; object-src 'none'; font-src 'self' data: satisfy.opencontent.it stackpath.bootstrapcdn.com cdnjs.cloudflare.com unpkg.com fonts.gstatic.com; worker-src 'self'; report-uri https://csp-collector.opencontent.it/csp?env=production&mode=enforce&app=sdc-qa; child-src 'self' www.youtube-nocookie.com announcekit.app"
        traefik.http.middlewares.security.headers.contentTypeNosniff: 'true'
        traefik.http.middlewares.security.headers.permissionsPolicy: "geolocation=(self), camera=(self), microphone=(self)"
        traefik.http.middlewares.security.headers.referrerPolicy: 'strict-origin-when-cross-origin'
        traefik.http.middlewares.security.headers.browserXssFilter: 'true'

        # Cors generali
        traefik.http.routers.https.rule: 'Method(`GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`, `PURGE`) && Host(`app.localtest.me`)'
        traefik.http.routers.https.tls: 'true'
        traefik.http.routers.https.tls.certresolver: 'myhttpchallenge'
        traefik.http.routers.https.middlewares: 'cors, general-rl, security'

        # Cors per condivisione autenticazione
        traefik.http.routers.origin-https.rule: 'Method(`GET`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `PURGE`) && Host(`app.localtest.me`)  && PathPrefix(`/lang/api/session-auth`)'
        traefik.http.routers.origin-https.tls: 'true'
        traefik.http.routers.origin-https.priority: 10001
        traefik.http.routers.origin-https.tls.certresolver: 'myhttpchallenge'
        traefik.http.routers.origin-https.middlewares: 'cors-origin, general-rl, security'

        traefik.http.middlewares.cors.headers.accesscontrolallowcredentials: 'true'
        traefik.http.middlewares.cors.headers.accesscontrolallowheaders: 'Authorization,Origin,Content-Type,Accept,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,access-control-allow-credentials,Referer,X-Hasura-Role,X-Hasura-Admin-Secret,X-Hasura-Allowed-Roles,X-Hasura-Default-Role,X-Hasura-User-Id,X-Hasura-Org-Id,X-Locale'
        traefik.http.middlewares.cors.headers.accesscontrolallowmethods: 'GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS'
        traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist: '*'
        traefik.http.middlewares.cors.headers.accesscontrolmaxage: 100
        traefik.http.middlewares.cors.headers.addvaryheader: 'true'
        
        traefik.http.middlewares.cors-origin.headers.accesscontrolallowcredentials: 'true'
        traefik.http.middlewares.cors-origin.headers.accesscontrolallowheaders: 'Authorization,Origin,Content-Type,Accept,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,access-control-allow-credentials,Referer,X-Hasura-Role,X-Hasura-Admin-Secret,X-Hasura-Allowed-Roles,X-Hasura-Default-Role,X-Hasura-User-Id,X-Hasura-Org-Id,X-Locale'
        traefik.http.middlewares.cors-origin.headers.accesscontrolallowmethods: 'GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS'
        traefik.http.middlewares.cors-origin.headers.accesscontrolmaxage: 100
        traefik.http.middlewares.cors-origin.headers.addvaryheader: 'true'

    environment:
      # https://github.com/wodby/varnish#environment-variables
      VARNISH_BACKEND_GRACE  : '2880m'
      VARNISH_BACKEND_HOST: 'app'
      VARNISH_BACKEND_PORT: '80'
      VARNISH_BACKEND_PROBE: '/health-check'
      VARNISH_KEEP_ALL_PARAMS: 1
      VARNISH_CACHE_STATIC_FILES: 1
      ##VARNISH_MOBILE_SEPARATE_CASH: 1
      #VARNISH_BIG_FILES_SIZE: 10485760
      VARNISHD_MEMORY_SIZE: '1024m'
      VARNISH_BACKEND_BETWEEN_BYTES_TIMEOUT: 30s
      VARNISH_BACKEND_FIRST_BYTE_TIMEOUT: 30s
      VARNISH_STRIP_COOKIES: 'cookies_consent|_[_a-z]+|wooTracker|VCKEY-[a-zA-Z0-9-_]+'
      VARNISHD_PARAM_WORKSPACE_BACKEND: '256k'
      VARNISHD_PARAM_WORKSPACE_CLIENT: '256k'
      VARNISH_SECRET: 'change_me'

  # Manager symfony, non è esposto su rete pubblica esegue migrazioni e script all'avvio
  manager:
    image: registry.gitlab.com/opencity-labs/area-personale/core/app:2.27.0
    stop_grace_period: 45s
    configs:
      - source: sf-config-instances
        target: /var/www/html/config/instances_prod.yml
    networks:
      - oc-prod-internal
    environment:
      <<: *sdc-env
      DB_USER: oc   # Inserire username utente manager se configurato
      DB_PASSWORD: 'oc' # Inserire password utente manager se configurato
      ENABLE_MIGRATIONS: 'true'
    deploy:
      <<: *deploy-snippet
      endpoint_mode: vip
      replicas: 1
      resources:
        limits:
          cpus: '1'
          memory: 1024M
        reservations:
          memory: 512M

  # Ci sono 3 valori da tenere sempre uguali: WORKER_NUM, replicas e swam_cronjob_replicas
  worker:
    image: registry.gitlab.com/opencity-labs/area-personale/core/app:2.27.0
    networks:
      - oc-prod-internal
    stop_signal: SIGQUIT
    stop_grace_period: 20m
    command: [ "./bin/worker-daemon.sh" ]
    configs:
      - source: sf-config-instances
        target: /var/www/html/config/instances_prod.yml
    volumes:
      - ./config/worker/worker-daemon.sh:/var/www/html/bin/worker-daemon.sh:ro
    healthcheck:
      disable: true
    environment:
      <<: *sdc-env
      SWARM_SERVICE: "worker"
      SWARM_STACK: "oc-prod"
      #DEBUG: 1
      WORKER_ID: 1
      WORKER_NUM: 1
      MAX_EXECUTIONS: 100
      MAX_ACTIONS: 50
    deploy:
      <<: *deploy-snippet
      endpoint_mode: dnsrr
      replicas: 1
      update_config:
        order: stop-first
      restart_policy:
        condition: none
      resources:
        limits:
          cpus: '0.5'
          memory: 2048M
        reservations:
          memory: 512M
      labels:
        swarm.cronjob.enable: "true"
        swarm.cronjob.schedule: "*/3 * * * *"
        swarm.cronjob.skip-running: "true"
        swarm.cronjob.replicas: 1

  # Espone delle api rest per la creazione di eventi su kafka
  vector:
    image: timberio/vector:0.18.1-debian
    configs:
      - source: vector-api
        target: /etc/vector/vector.toml
    healthcheck:
      test: [ "CMD", "curl", "-f", "http://localhost:80/health" ]
      interval: 1m30s
      timeout: 10s
      retries: 6
      start_period: 5s
    environment:
      VECTOR_LOG: debug
    networks:
      - oc-prod-internal
    deploy:
      <<: *deploy-snippet
      labels:
        traefik.enable: 'false'

  # Microservizio per la creazione di pdf
  gotenberg:
    image: gotenberg/gotenberg:7.9.2
    networks:
      - oc-prod-internal
      - oc-prod-public
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 1m30s
      timeout: 10s
      retries: 6
      start_period: 5s
    deploy:
      <<: *deploy-snippet
      labels:
        prometheus.disable: 'true'
        traefik.enable: 'true'
        traefik.http.services.gotenberg.loadbalancer.server.port: 3000
        traefik.http.routers.gotenberg.rule: 'Host(`gotenberg.localtest.me`)'
        traefik.http.routers.gotenberg.tls: 'true'
        traefik.http.routers.gotenberg.tls.certresolver: 'myhttpchallenge'
        traefik.http.routers.gotenberg.middlewares: 'goodheaders'

  # Strumento per testare la posta elettronica
  mailhog:
    image: mailhog/mailhog
    networks:
      - oc-prod-internal
    deploy:
      <<: *deploy-snippet
      labels:
        traefik.enable: 'true'
        traefik.http.services.mailhog.loadbalancer.server.port: 8025
        traefik.http.routers.mailhog.rule: Host(`mail.localtest.me`)
        traefik.http.routers.mailhog.tls: 'true'
        traefik.http.routers.mailhog.middlewares: goodheaders
        traefik.http.routers.mailhog.tls.certresolver: 'myhttpchallenge'

  ##############################################################################
  # FORMS
  ##############################################################################

  # Frontend per il formserver
  form-varnish:
    image: registry.gitlab.com/opencontent/varnish:1.2
    stop_grace_period: 45s
    networks:
      - oc-prod-internal
      - oc-prod-public
    deploy:
      <<: *deploy-snippet
      replicas: 1
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.1'
          memory: 128M
      labels:
        prometheus.enable: 'true'
        prometheus.port: 9131
        traefik.enable: 'true'
        traefik.http.services.formvarnish.loadbalancer.server.port: 6081
        traefik.http.services.formvarnish.loadbalancer.healthcheck.path: /.vchealthz
        traefik.http.routers.formvarnish.rule: 'Method(`GET`, `HEAD`, `OPTIONS`) && Host(`form.localtest.me`)'
        traefik.http.routers.formvarnish.tls: 'true'
        traefik.http.routers.formvarnish.tls.certresolver: 'myhttpchallenge'
        traefik.http.routers.formvarnish.middlewares: 'cors'


    environment:
      # https://github.com/wodby/varnish#environment-variables
      VARNISH_BACKEND_HOST: 'form-server'
      VARNISH_BACKEND_PORT: '8000'
      VARNISH_BACKEND_GRACE: '2880m'
      VARNISH_BACKEND_PROBE: '/form/5d7aa15b18fecd734051ae7f?varnish-health'
      VARNISH_CACHE_STATIC_FILES: 1
      VARNISHD_MEMORY_SIZE: '256m'
      VARNISH_BACKEND_BETWEEN_BYTES_TIMEOUT: 10s
      VARNISH_BACKEND_FIRST_BYTE_TIMEOUT: 30s
      VARNISH_KEEP_ALL_PARAMS: 1
      VARNISH_STRIP_COOKIES: 'cookie_consent|_[_a-z]+|wooTracker|VCKEY-[a-zA-Z0-9-_]+|_ga|_gid|_gat[a-zA-Z0-9-_]+'
      VARNISH_SECRET: 'nvXq3CkK'
      VARNISHD_PARAM_WORKSPACE_BACKEND: '256k'
      VARNISHD_PARAM_WORKSPACE_CLIENT: '256k'

  # Espone api rest per il crud dei form
  form-server:
    image: registry.gitlab.com/opencity-labs/area-personale/form-server:1.3.0
    networks:
      - oc-prod-public
      - oc-prod-internal
    environment:
      DB_URL: mongodb://mongo:27017/formmanager
      MAX_AGE: 5
      S_MAX_AGE: 60
    #healthcheck:
    #  test: [ "CMD", "wget", "--spider", "http://localhost:8000/form/5d7aa15b18fecd734051ae7f" ]
    #  start_period: 30s
    #  timeout: 1s
    #  retries: 1
    #  interval: 1m
    deploy:
      <<: *deploy-snippet
      labels:
        traefik.enable: 'true'
        traefik.http.services.formserver.loadbalancer.server.port: 8000

        # Espongo il formserver aperto in scrittura con oc-auth, sotto /api per il form-builder
        traefik.http.routers.formserver.rule: 'Method(`GET`, `HEAD`, `OPTIONS`, `POST`, `PUT`, `PATCH`, `DELETE`) && Host(`form-builder.localtest.me`) && PathPrefix(`/api/`)'
        traefik.http.routers.formserver.tls: 'true'
        traefik.http.routers.formserver.tls.certresolver: 'myhttpchallenge'
        traefik.http.middlewares.strip-forms-api.stripprefix.prefixes: '/api/'
        traefik.http.routers.formserver.middlewares: 'strip-forms-api'

        # Espongo anche readonly per gli admin da backend (per vedere subito aggiornamenti)
        traefik.http.routers.formserver-ro.rule: 'Method(`GET`, `POST`, `HEAD`, `OPTIONS`) && Host(`form-readonly.localtest.me`)'
        traefik.http.routers.formserver-ro.tls: 'true'
        traefik.http.routers.formserver-ro.tls.certresolver: 'myhttpchallenge'
        traefik.http.routers.formserver-ro.middlewares: 'oc-auth, cors'

  # Microservizio per la gestione dei componenti comuni dei form (nested form)
  form-builder:
    image: registry.gitlab.com/opencity-labs/area-personale/formbuilderjs:0-5-2
    healthcheck:
      test: [ "NONE" ]
    networks:
      - oc-prod-public
    deploy:
      <<: *deploy-snippet
      labels:
        traefik.enable: 'true'
        traefik.http.services.formbuilder.loadbalancer.server.port: 80

        traefik.http.routers.formbuilder.rule: 'Method(`GET`, `HEAD`, `OPTIONS`) && Host(`form-builder.localtest.me`)'
        traefik.http.routers.formbuilder.tls: 'true'
        traefik.http.routers.formbuilder.tls.certresolver: 'myhttpchallenge'
        traefik.http.routers.formbuilder.middlewares: 'oc-auth'

  ##############################################################################
  # FORMS
  ##############################################################################

  # Interroga la tabella PAYMENTS_STATUS  su ksqldb e richiede info ai proxy sui pagamenti pendenti
  payments-poller:
    image: registry.gitlab.com/opencity-labs/area-personale/payments-poller:1.0.2
    networks:
      - oc-prod-internal
    environment:
      KSQLDB_SERVER: http://ksqldb-server:8088
      LOOP_TIME: 900
      LOG_LEVEL: DEBUG
      HEALTHCHECK_ID: 88f0d2a5-0326-46a1-9093-42e83c3017c8
      PYTHONDEBUG: 1
    deploy:
      <<: *deploy-snippet
      resources:
        reservations:
          memory: 1G
        limits:
          cpus: '0.5'
          memory: 4G
    healthcheck:
      test: [ NONE ]

  # Legge gli eventi dal topic applications e crea un evento nel topic payments se necessario
  payment-dispatcher:
    image: registry.gitlab.com/opencity-labs/area-personale/payment-dispatcher:1.2.1
    networks:
      - oc-prod-internal
    environment:
      KAFKA_TOPIC_APPLICATIONS: applications
      KAFKA_TOPIC_PAYMENTS: payments
      KAFKA_BOOTSTRAP_SERVERS: kafka:9092
      KAFKA_CONSUMER_GROUP_PREFIX: payment-dispatcher
      PROMETHEUS_JOB_NAME: payment_dispatcher
      APP_ID: payment-dispatcher:1.2.1
      KSQLDB_SERVER: http://ksqldb-server:8088
      LOG_LEVEL: INFO
    healthcheck:
      test: [ NONE ]
    deploy:
      <<: *deploy-snippet
      resources:
        limits:
          cpus: '0.5'
          memory: 1G

  # Gateway proxy di iris (ambiente di test)
  iris-proxy:
    image: registry.gitlab.com/opencity-labs/area-personale/iris-payment-proxy:1.3.3
    stop_grace_period: 5m
    networks:
      - oc-prod-public
    environment:
      CANCEL_PAYMENT: "true"
      KAFKA_TOPIC_NAME: payments
      KAFKA_BOOTSTRAP_SERVERS: kafka:9092
      KAFKA_GROUP_ID: iris_payment_proxy
      IRIS_OTF_PAYMENT_URL: https://apistage.regione.toscana.it/C01/ComunicazionePosizioniDebitorieOTF/v3/IdpAllineamentoPendenzeEnteOTF
      IRIS_IUV_URL: https://iristest.rete.toscana.it/IdpBillerNdpServices/GenerazioneIUVService
      IRIS_OUTCOME_URL: https://apistage.regione.toscana.it/C01/InvioNotificaPagamentoEsito/v3/IdpInformativaPagamento.Esito
      IRIS_VERIFY_URL: https://apistage.regione.toscana.it/C01/VerificaStatoPagamento/v3/IdpVerificaStatoPagamento
      IRIS_NOTICE_URL: https://iristest.rete.toscana.it/IdpBillerNdpServices/GenerazioneAvvisiService
      EXTERNAL_API_URL: "https://iris-proxy.localtest.me"
      INTERNAL_API_URL: "http://iris-proxy.localtest.me"
      BASE_PATH_EVENT: "sdc-payments/iris-proxy/payments/"
      BASE_PATH_CONFIG: "sdc-payments/iris-proxy/tenants/"
      # STORAGE_TYPE="LOCAL|MINIO|S3"
      STORAGE_TYPE: "LOCAL"
      STORAGE_BUCKET_NAME: "payments"
      APP_ID: "iris-proxy:1.3.3"
    deploy:
      <<: *deploy-snippet
      labels:
        traefik.enable: 'true'
        traefik.http.services.iris-proxy-qa.loadbalancer.server.port: 8000

        traefik.http.routers.iris-proxy.entrypoints: 'websecure, web'
        traefik.http.routers.iris-proxy.rule: 'Host(`iris-proxy.localtest.me`) && PathPrefix(`/{op:(notice|online-payment|receipt|landing)}/{id:[^/]+}`, `/{op:(docs|redoc|openapi.json|tenants|services|notify-payment)}`, `/{op:(tenants|services)}/({id:[^/]+}|schema)`) && METHOD(`GET`, `POST`, `PUT` ,`PATCH`, `DELETE`, `HEAD`, `OPTIONS`)'
        traefik.http.routers.iris-proxy.tls: 'true'
        traefik.http.routers.iris-proxy.tls.certresolver: 'myhttpchallenge'

        traefik.http.middlewares.payment-proxy-qa-cors.headers.accesscontrolallowcredentials: 'true'
        traefik.http.middlewares.payment-proxy-qa-cors.headers.accesscontrolallowheaders: 'Authorization,Origin,Content-Type,Accept,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,access-control-allow-credentials,Referer'
        traefik.http.middlewares.payment-proxy-qa-cors.headers.accesscontrolallowmethods: 'GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS'
        # asterisco perché ognuno di essi è acceduto da tutti i domini custom che avranno gli enti
        traefik.http.middlewares.payment-proxy-qa-cors.headers.accesscontrolalloworiginlist: '*'
        traefik.http.middlewares.payment-proxy-qa-cors.headers.accesscontrolmaxage: 100
        traefik.http.middlewares.payment-proxy-qa-cors.headers.addvaryheader: 'true'

        traefik.http.routers.iris-proxy.middlewares: 'goodheaders, payment-proxy-qa-cors'
    volumes:
      - ./var/tenants:/app/payments/sdc-payments/iris-proxy/tenants
      - ./var/payments:/app/payments/sdc-payments/iris-proxy/payments

  # Aggiorna lo stato delle pratiche dopo che si sono verificati dei pagamenti
  krun-process-payments:
    image: registry.gitlab.com/opencity-labs/area-personale/core/app:2.27.0
    configs:
      - source: sf-config-instances
        target: /var/www/html/config/instances_prod.yml
    entrypoint: /bin/krun
    stop_grace_period: 300s
    healthcheck:
      disable: true
    volumes:
      - ./config/krun/process-payment:/bin/process-payment
      - ./config/krun/krun:/bin/krun
    networks:
      - oc-prod-internal
    environment:
      <<: *sdc-env
      KAFKA_TOPIC: "payments"
      KAFKA_SERVER: "kafka:9092"
      KAFKA_CONSUMER_GROUP: "oc-prod-krun-process-payments"
      COMMAND: "/bin/process-payment"
      #DEBUG: 1
      VERBOSE: 1
    deploy:
      <<: *deploy-snippet
      resources:
        limits:
          cpus: '0.2'
          memory: 1024M
      update_config:
        parallelism: 3
        order: stop-first


  ##############################################################################
  # REGISTRY
  ##############################################################################

  # Espone le api per la configurazione dei vari sistemi di protocollazione
  registry-rest-api:
    image: registry.gitlab.com/opencity-labs/area-personale/stanzadelcittadino-application-registry:1.10.2
    networks:
      - oc-prod-public
      - oc-prod-internal
    environment:
      <<: *registry-env
      DJANGO_LOG_LEVEL: INFO
      REGISTRY_API_KEY: change_me
    deploy:
      <<: *deploy-snippet
      labels:
        traefik.enable: "true"
        traefik.http.services.registry.loadbalancer.server.port: 8000
        traefik.http.routers.registry.rule: "Host(`registry.localtest.me`)"
        traefik.http.routers.registry.tls: "true"
        traefik.http.routers.registry.tls.certresolver: "myhttpchallenge"

        traefik.http.middlewares.registry-qa-home-redir.redirectregex.regex: 'registry.localtest.me/$$'
        traefik.http.middlewares.registry-qa-home-redir.redirectregex.replacement: "registry.localtest.me/admin"

        traefik.http.middlewares.registry-redirect-tenants.replacepathregex.regex: '^/(d3|datagraph|dedagroup|halley|halley-cloud|hypersic|infor|insiel|maggioli|pitre|tinn)/v1/tenants/(.*)'
        traefik.http.middlewares.registry-redirect-tenants.replacepathregex.replacement: '/api/v1/tenants/'

        traefik.http.middlewares.registry-redirect-services.replacepathregex.regex: '^/(d3|datagraph|dedagroup|halley|halley-cloud|hypersic|infor|insiel|maggioli|pitre|tinn)/v1/services/'
        traefik.http.middlewares.registry-redirect-services.replacepathregex.replacement: '/api/v1/services/$$1/'
        traefik.http.middlewares.registry-redirect-service.replacepathregex.regex: '^/(d3|datagraph|dedagroup|halley|halley-cloud|hypersic|infor|insiel|maggioli|pitre|tinn)/v1/services/(.+)'
        traefik.http.middlewares.registry-redirect-service.replacepathregex.replacement: '/api/v1/services/$$1/$$2/'

        traefik.http.middlewares.registry-redirect-service-schema.replacepathregex.regex: '^/(d3|datagraph|dedagroup|halley|halley-cloud|hypersic|infor|insiel|maggioli|pitre|tinn)/v1/schema'
        traefik.http.middlewares.registry-redirect-service-schema.replacepathregex.replacement: '/api/v1/services-schema/$$1'

        traefik.http.middlewares.registry-cors.headers.accesscontrolallowcredentials: 'true'
        traefik.http.middlewares.registry-cors.headers.accesscontrolallowheaders: '*'
        traefik.http.middlewares.registry-cors.headers.accesscontrolallowmethods: 'GET,POST,HEAD,PUT,OPTIONS'
        traefik.http.middlewares.registry-cors.headers.accesscontrolalloworiginlist: '*'
        traefik.http.middlewares.registry-cors.headers.accesscontrolmaxage: 100
        traefik.http.middlewares.registry-cors.headers.addvaryheader: 'true'

        traefik.http.routers.registry.middlewares: "goodheaders, registry-qa-home-redir, registry-redirect-tenants, registry-redirect-services, registry-redirect-service, registry-redirect-service-schema, registry-cors"

        prometheus.enable: "true"
        prometheus.port: 8000

  # Gestisce il sistema di retry della protocollazione
  registry-retry-scheduler:
    image: registry.gitlab.com/opencity-labs/area-personale/stanzadelcittadino-application-registry:1.10.2
    command: /app/venv/bin/python3.9 manage.py run_retry_scheduler
    networks:
      - oc-prod-internal
    environment:
      <<: *registry-env
      ENABLE_SENTRY_EVENT_FILTER: "true"
    deploy:
      <<: *deploy-snippet
      
      
  ##############################################################################
  # PDND
  ##############################################################################

  pdnd-connector:
    image: registry.gitlab.com/opencity-labs/area-personale/pdnd-connector:x.x.x
    networks:
      - oc-prod-internal
    deploy:
      <<: *deploy-snippet
    environment:
      ENVIRONMENT: app_environment
      SENTRY_TOKEN: sentry DSN
      HTTP_EXTERNAL_BASEPATH: https://api.qa.stanzadelcittadino.it/pdnd
      SDC_PUBLIC_KEY_ENDPOINT: https://core/.well-known/jwks.json
      CACHE_EXPIRATION: 5m
      CACHE_EVICTION: 10m
      STORAGE_TYPE: s3
      STORAGE_BUCKET: s3_bucket
      STORAGE_BASE_PATH: pdnd
      STORAGE_LOCAL_PATH: /data/pdnd
      STORAGE_S3_KEY: S3_key
      STORAGE_S3_SECRET: s3_secret
      STORAGE_S3_REGION: eu-west-1
      STORAGE_S3_ENDPOINT:  https://s3.eu-west-1.amazonaws.com
      STORAGE_S3_SSL: "false"
    labels:
      prometheus.enable: 'true'
      prometheus.port: 8000
      traefik.enable: 'true'
      traefik.docker.network: 'oc-prod-internal'
      traefik.http.services.pdnd-connector.loadbalancer.server.port: 8000
      traefik.http.routers.pdnd-connector.entrypoints: 'websecure, web'
      traefik.http.routers.pdnd-connector.rule: 'Host(`api.qa.stanzadelcittadino.it`) && PathPrefix(`/pdnd/{op:(status|metrics|docs|anpr/accertamento-residenza|anpr/stato-famiglia|tenants|keys|tenants/configs|tenants/clients|e-services)}`, `/pdnd/{op:(status|metrics|docs|anpr/accertamento-residenza|anpr/stato-famiglia|tenants|keys|tenants/configs|tenants/clients|e-services)}/({id:[^/]+})`) && METHOD(`GET`, `POST`, `PUT` ,`PATCH`, `DELETE`, `HEAD`, `OPTIONS`)'
      traefik.http.routers.pdnd-connector.tls: 'true'
      traefik.http.middlewares.pdnd-connector-stripper.stripprefix.prefixes: '/pdnd/'
      traefik.http.routers.pdnd-connector.tls.certresolver: 'myhttpchallenge'
      traefik.http.routers.pdnd-connector.middlewares: 'goodheaders, cors-free-all, pdnd-connector-stripper'




Prima di procedere all’avvio dei microservizi si devono compiere alcune operazioni:

Creazione del file delle istanze

Va creato un file instances.yml sull'ambiente che ospiterà la piattaforma, questo file dovrà poi essere condiviso con i servizi che ne hanno bisogno (vedi file di esempio di distribuzione).

Il file è così formato:

instances:
  # dominio dell'sitanza con prefisso
  stanzadelcittadino.localtest.me/comune-di-bugliano:
    # codice meccanografico dell'ente
    codice_meccanografico: c_cbug
    # identificativo dell'istanza
    identifier: comune-di-bugliano
    # lingue disponibili (it, de, en)
    app_locales: it|en
    # tipo di login (da scegliere in base al provider dell'ente)
    login_route: login_pat

Andrà creato un blocco istanza per ogni ente che si vorrà ospitare sulla piattaforma

Creazione dei database

Singole istanze degli enti (Symfony core)

Il tipo di multi-tenancy implementata in OpenCity Area personale è di singolo stack applicativo con database dedicato per tenant.

Per poter effettuare un’installazione dell’infrastruttura abbiamo quindi bisogno di un singolo database per tenant con le seguenti caratteristiche.

Postgres >= 11 con estensione postgis >= 3

Permessi

Per ridurre i rischi dovuti a errori o a compromissione delle credenziali vengono usati due utenti differenti:

  • un utente oc_manager che viene utilizzato per creare i database ed eseguire operazione di struttura (creazione, modifica e cancellazione di tabelle, viste, colonne ecc ecc)

  • un utente oc_user che viene utilizzato dall’applicativo per effettuare operazioni sui dati

Attualmente nella nostra infrastruttura la creazione del database avviene con l'utente oc_manager, a partire da un template preimpostato con i privilegi necessari.

Creazione del template

Come utente postgres si crea il database impostando i privilegi di default che verranno assegnati a tutti gli oggetti creati in seguito.

A postgres verranno dati privilegi ampi, mentre a oc_user verranno dati privilegi minimali:

CREATE DATABASE _oc ENCODING='utf8' CONNECTION LIMIT=50;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT ALL ON TYPES TO postgres WITH GRANT OPTION;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO postgres;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT ALL ON SEQUENCES TO postgres;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT ALL PRIVILEGES ON TABLES TO postgres;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT USAGE ON TYPES TO oc_user;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT EXECUTE ON FUNCTIONS TO oc_user;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT ALL ON SEQUENCES TO oc_user;
ALTER DEFAULT PRIVILEGES FOR USER oc_manager IN SCHEMA "public" GRANT SELECT,INSERT,UPDATE,DELETE ON TABLES TO oc_user;
CREATE EXTENSION IF NOT EXISTS postgis;

In seguito la proprietà del database viene assegnata ad oc_manager

ALTER DATABASE _oc OWNER TO oc_manager;
ALTER SCHEMA public OWNER TO oc_manager;

Per creare database come utente postgres gli viene dato il ruolo oc_manager

GRANT oc_manager TO postgres;

Creazione di un nuovo database

Per creare il database e impostarne subito come owner l'utente corretto, si esegue come utente postgres

CREATE DATABASE oc_firenze TEMPLATE = _oc OWNER='oc_manager' ENCODING='utf8' CONNECTION LIMIT=50;Some code

Sistema di protocollazione (registry)

Il sistema di protocollazione ha invece bisogno di un singolo database con le seguenti caratteristiche:

Postgres >= 11

Creazione di un nuovo utente

CREATE USER registry_user WITH PASSWORD 'password';

Tuning di alcuni parametri di connessione (questi comandi sono opzionali ma suggeriti da django)

ALTER ROLE registry_user SET client_encoding TO 'utf8';
ALTER ROLE registry_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE registry_user SET timezone TO 'UTC';

Creo il database e lo assegno all'utente

CREATE DATABASE oc_registry ENCODING='utf8' CONNECTION LIMIT=50;
GRANT ALL PRIVILEGES ON DATABASE oc_registry TO registry_user;

Configurazione dei proxy di pagamento

Per garantire una corretta comunicazione tra l'area personale e i proxy di pagamento è necessaria configurare appropriatamente gli endpoint sul proxy di riferimento.

Configurazione dei CORS

Il requisito di base affinchè questi endpoint siano correttamente funzionanti è che siano correttamente racchiuse all'interno di un middleware che gestisca i CORS in maniera adeguata. In particolare questo middleware dovrà essere configurato come segue:

HeaderValore

Access-Control-Allow-Credentials

true

Access-Control-Allow-Headers

Authorization,Origin,Content-Type,Accept,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,access-control-allow-credentials,Referer

Access-Control-Allow-Methods

GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS

Access-Control-Allow-Origin-List

*

Access-Control-Max-Age

100

Add-Vary-Header

true

Di seguito un esempio di configurazione del middleware:

traefik.http.middlewares.cors.headers.accesscontrolallowcredentials: 'true'
traefik.http.middlewares.cors.headers.accesscontrolallowheaders: 'Authorization,Origin,Content-Type,Accept,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,access-control-allow-credentials,Referer'
traefik.http.middlewares.cors.headers.accesscontrolallowmethods: 'GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS'
traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist: '*'
traefik.http.middlewares.cors.headers.accesscontrolmaxage: 100
traefik.http.middlewares.cors.headers.addvaryheader: 'true'

Endpoint interni (non necessita di CORS)

L'unico endpoint interno in questo caso è quello per richiedere l'aggiornamento dello stato di un pagamento. Per garantire quindi che questo venga chiamato solo ed esclusivamente dal microservizio di polling, è necessario che questo venga configurato come endpoint raggiungibile solo internamente alla rete di docker. Di seguito un esempio:

 networks:
 backplane-sdc:
   external: true
 internal:
 traefik-sdc:
   external: true
 swarm-backplane:
   external: true

...
 networks:
   - traefik-sdc
   - backplane-sdc
 deploy:
   <<: *deploy-snippet
   labels:
     traefik.enable: 'true'
     traefik.docker.network: 'traefik-sdc'
     traefik.http.routers.silfi-proxy-qa-internal.entrypoints: 'backplane'
     traefik.http.routers.silfi-proxy-qa-internal.rule: 'Host(`silfi-proxy-qa.boat-backplane.opencontent.io`) && PathPrefix(`/payment-proxy/silfi/update/{id:[^/]+}`) && METHOD(`GET`, `HEAD`, `OPTIONS`)'

Endpoint pubblici

Gli endpoint pubblici sono:

  • Pagamento online (/online-payment/{payment_id})

  • Download dell'avviso cartaceo (/offline-payment/{payment_id})

  • Download della ricevuta telematica (/receipt/{payment_id})

  • Ritorno alla area personale (detta anche landing url) (/landing/{payment_id})

  • Form schema per la configurazione del tenant (/tenants/schema)

  • Form schema per la configurazione del servizio (/services/schema)

  • Documentazione Swagger delle API (/docs, facoltativo)

  • Status (/status, facoltativo)

  • Metriche di monitoraggio (/metrics, facoltativo)

Di seguito un esempio di configurazione degli endpoint

traefik.http.routers.silfi-proxy-qa.entrypoints: 'web, websecure'
traefik.http.routers.silfi-proxy-qa.rule: 'Host(`api.qa.stanzadelcittadino.it`) && PathPrefix(`/payment-proxy/silfi/{op:(notice|online-payment|receipt|landing)}/{id:[^/]+}`, `/payment-proxy/silfi/{op:(docs|status|metrics)}`, `/payment-proxy/silfi/{op:(tenants|services)}/schema`) && METHOD(`GET`, `POST`, `PUT` ,`PATCH`, `DELETE`, `HEAD`, `OPTIONS`)'
traefik.http.routers.silfi-proxy-qa.tls: 'true'
traefik.http.routers.silfi-proxy-qa.tls.certresolver: 'mydnschallenge'
traefik.http.routers.silfi-proxy-qa.middlewares: 'goodheaders, cors'

Endpoint protetti

Gli endpoint protetti da autenticazione sono:

  • Inserimento, recupero, modifica e cancellazione della configurazione del tenant (/tenants/{id})

  • Inserimento, recupero, modifica e cancellazione della configurazione del servizio (/services/{id})

Di seguito un esempio di configurazione degli endpoint:

# Quando pronti aggiungere il middleware 
# sdc-qa-jwt-decode
traefik.http.routers.silfi-proxy-qa-protected.entrypoints: 'web, websecure'
traefik.http.routers.silfi-proxy-qa-protected.rule: 'Host(`api.qa.stanzadelcittadino.it`) && PathPrefix(`/payment-proxy/silfi/{op:(tenants|services)}`, `/payment-proxy/silfi/{op:(tenants|services)}/{id:[^/]+}`) && METHOD(`GET`, `POST`, `PUT` ,`PATCH`, `DELETE`,>
traefik.http.routers.silfi-proxy-qa-protected.tls: 'true'
traefik.http.routers.silfi-proxy-qa-protected.tls.certresolver: 'mydnschallenge'
# MIDDLEWARE ABILITATO PER CONTROLLO JWT
# traefik.http.routers.silfi-page-proxy-qa-protected.middlewares: 'sdc-qa-jwt-decode, goodheaders, cors'
traefik.http.routers.silfi-proxy-qa-protected.middlewares: 'goodheaders, cors'

Last updated

Logo

Documentazione Opencity Italia