# Configuration System v2

**Status**: 📋 SPECIFICA - Da implementare

**Versione**: 2.0.0

**Data**: 2025-01-02

## Overview

genro-asgi utilizza un sistema di configurazione YAML flat con naming gerarchico basato su `-` (hyphen) come separatore. Questo design permette:

1. **Struttura flat** - Tutte le sezioni al primo livello YAML
2. **Naming gerarchico** - `categoria-nome-sotto` per configurazioni nidificate
3. **Merge gerarchico** - App possono avere YAML propri, assorbiti durante il mounting
4. **Nessun quote necessario** - YAML valido senza escaping

## Principi di Design

### 1. Flat First-Level

Tutte le chiavi di configurazione sono al primo livello del documento YAML:

```yaml
# Corretto: flat
host: "127.0.0.1"
port: 8000
apps-shop:
  db_uri: "sqlite:///shop.db"

# Evitato: nesting profondo
server:
  host: "127.0.0.1"
  apps:
    shop:
      db_uri: "sqlite:///shop.db"
```

### 2. Hyphen as Hierarchy Separator

Il carattere `-` separa i livelli gerarchici:

```
{categoria}-{nome}[-{sotto}[-{sotto}...]]
```

Esempi:
- `middleware-cors` → configurazione del middleware cors
- `middleware-cors-production` → configurazione cors per production
- `apps-shop` → configurazione dell'app shop
- `apps-shop-cache` → configurazione cache dell'app shop

### 3. App-Level Configuration Inheritance

Ogni applicazione può avere un proprio `config.yaml` che viene **assorbito** durante il mounting:

```
Precedenza (successivi vincono):
1. App default config (app_dir/config.yaml)
2. Server config (server_dir/config.yaml)
3. Environment variables
4. CLI arguments
```

## Struttura Completa

### Sezioni Server (Flat)

```yaml
# ══════════════════════════════════════════════════════════════════
# SERVER ASGI - Configurazione uvicorn/hypercorn layer
# ══════════════════════════════════════════════════════════════════
host: "127.0.0.1"          # Bind address
port: 8000                  # Listen port
reload: false               # Auto-reload on file changes
main_app: shop              # App default per "/" redirect
```

| Chiave | Tipo | Default | Descrizione |
|--------|------|---------|-------------|
| `host` | `str` | `"127.0.0.1"` | Indirizzo di bind |
| `port` | `int` | `8000` | Porta di ascolto |
| `reload` | `bool` | `false` | Auto-reload in development |
| `main_app` | `str` | `None` | App per redirect da "/" |

### Sezione OpenAPI

```yaml
# ══════════════════════════════════════════════════════════════════
# OPENAPI - Metadati per documentazione API
# ══════════════════════════════════════════════════════════════════
openapi:
  title: "My API"
  version: "1.0.0"
  description: "API description"
  contact:
    name: "Support"
    email: "support@example.com"
  license:
    name: "Apache 2.0"
```

### Sezione Apps

```yaml
# ══════════════════════════════════════════════════════════════════
# APPS - Dichiarazione applicazioni utente
# ══════════════════════════════════════════════════════════════════
apps:
  shop: "shop_app:Application"
  admin: "admin_app:Application"
  docs: "genro_asgi.routers.static_router:StaticRouter"
```

**Formato**: `{nome}: "{modulo}:{classe}"`

Se la classe è `Application`, si può omettere:
```yaml
apps:
  shop: "shop_app"           # → shop_app:Application
  admin: "admin_app"         # → admin_app:Application
```

### Sezione Sys_Apps

```yaml
# ══════════════════════════════════════════════════════════════════
# SYS_APPS - Applicazioni di sistema
# ══════════════════════════════════════════════════════════════════
sys_apps:
  swagger: "genro_asgi.sys_applications.swagger.swagger_app:Application"
  genro_api: "genro_asgi.sys_applications.genro_api.genro_api_app:Application"
```

### Sezione Middleware

```yaml
# ══════════════════════════════════════════════════════════════════
# MIDDLEWARE - Enable/disable middleware
# ══════════════════════════════════════════════════════════════════
middleware:
  errors: on        # Default: on, Order: 100
  logging: off      # Default: off, Order: 200
  cors: on          # Default: off, Order: 300
  auth: on          # Default: off, Order: 400
  compression: off  # Default: off, Order: 900
  cache: off        # Default: off, Order: 900
```

**Valori accettati**: `on`, `off`, `true`, `false`, `yes`, `no`, `1`, `0`

### Sezione Plugins

```yaml
# ══════════════════════════════════════════════════════════════════
# PLUGINS - Plugin genro-routes globali
# ══════════════════════════════════════════════════════════════════
plugins:
  logging:
    level: debug
  pydantic:
    strict: true
```

## Sezioni Parametriche

### Pattern Naming

```
{categoria}-{nome}[-{sotto}]
```

### Middleware Configuration

```yaml
# ────────────────── CORS ──────────────────
middleware-cors:
  allow_origins: ["*"]
  allow_methods: ["GET", "POST", "PUT", "DELETE"]
  allow_headers: ["Authorization", "Content-Type"]
  allow_credentials: false
  expose_headers: []
  max_age: 600

# Varianti per ambiente
middleware-cors-development:
  allow_origins: ["http://localhost:3000", "http://localhost:5173"]
  allow_credentials: true

middleware-cors-production:
  allow_origins: ["https://myapp.com"]
  max_age: 86400

# ────────────────── AUTH ──────────────────
middleware-auth:
  default_backend: bearer

middleware-auth-bearer:
  reader_token:
    token: "tk_abc123"
    tags: "read"
  admin_token:
    token: "tk_admin"
    tags: ["read", "write", "admin"]

middleware-auth-basic:
  admin:
    password: "secret"
    tags: "admin"
  readonly:
    password: "readonly123"
    tags: "read"

middleware-auth-jwt:
  secret: "jwt_secret"
  algorithm: "HS256"
  algorithms: ["HS256", "RS256"]

# ────────────────── COMPRESSION ──────────────────
middleware-compression:
  minimum_size: 500
  compression_level: 6

# ────────────────── LOGGING ──────────────────
middleware-logging:
  level: "INFO"
  include_headers: false
  include_query: true

# ────────────────── CACHE ──────────────────
middleware-cache:
  max_age: 3600
  immutable: false
  public: true

# ────────────────── ERRORS ──────────────────
middleware-errors:
  debug: false
  include_traceback: false
```

### Apps Configuration

```yaml
# ────────────────── SHOP APP ──────────────────
apps-shop:
  db_uri: "sqlite:///shop.db"
  cache_timeout: 300
  debug: false

# Sotto-configurazioni
apps-shop-cache:
  backend: redis
  host: "localhost"
  port: 6379
  ttl: 3600

apps-shop-notifications:
  enabled: true
  provider: email
  smtp_host: "smtp.example.com"

# ────────────────── ADMIN APP ──────────────────
apps-admin:
  require_auth: true
  allowed_roles: ["admin", "superuser"]

# ────────────────── STATIC/DOCS ──────────────────
apps-docs:
  base_dir: "./public"
  index: "index.html"
  spa_mode: true
```

### Sys_Apps Configuration

```yaml
# ────────────────── SWAGGER ──────────────────
sys_apps-swagger:
  theme: "dark"
  persist_authorization: true
  display_request_duration: true

# ────────────────── GENRO API ──────────────────
sys_apps-genro_api:
  enable_playground: true
  max_depth: 5
```

## App-Level Configuration

Ogni applicazione può avere un proprio file `config.yaml` nella sua directory:

```
my_project/
├── config.yaml              # Server config
├── shop_app/
│   ├── __init__.py
│   ├── shop_app.py          # Application class
│   └── config.yaml          # App-specific config (opzionale)
└── admin_app/
    ├── __init__.py
    ├── admin_app.py
    └── config.yaml
```

### App Config Example

```yaml
# shop_app/config.yaml - Default dell'app

# Parametri di default dell'applicazione
db_uri: "sqlite:///default.db"
cache_timeout: 600
debug: false

# Sotto-configurazioni
cache:
  backend: memory
  ttl: 300

notifications:
  enabled: false
```

### Merge Behavior

Durante il mounting, le configurazioni vengono fuse:

```python
# Pseudo-code del merge
final_config = (
    app_default_config          # Da app_dir/config.yaml
    + server_config["apps-{name}"]  # Da server config.yaml
    + server_config["apps-{name}-*"]  # Sotto-configurazioni
)
```

**Esempio di override:**

```yaml
# shop_app/config.yaml (app default)
db_uri: "sqlite:///default.db"
cache_timeout: 600

# server config.yaml (override)
apps-shop:
  db_uri: "postgresql://prod/shop"  # Override
  # cache_timeout usa il default: 600
```

Risultato finale per `shop`:
```python
{
    "db_uri": "postgresql://prod/shop",  # Da server
    "cache_timeout": 600                  # Da app default
}
```

## Esempio Completo

```yaml
# ══════════════════════════════════════════════════════════════════
# config.yaml - Configurazione completa genro-asgi
# ══════════════════════════════════════════════════════════════════

# ── Server ──
host: "0.0.0.0"
port: 8000
reload: true
main_app: shop

# ── OpenAPI ──
openapi:
  title: "My E-Commerce API"
  version: "1.0.0"
  description: "API for shop management"

# ── Middleware ──
middleware:
  errors: on
  cors: on
  auth: on
  compression: on
  logging: on

# ── Plugins ──
plugins:
  pydantic:
    strict: true

# ── Apps ──
apps:
  shop: "shop_app:Application"
  admin: "admin_app:Application"
  docs: "genro_asgi.routers.static_router:StaticRouter"

# ── Sys Apps ──
sys_apps:
  swagger: "genro_asgi.sys_applications.swagger.swagger_app:Application"
  genro_api: "genro_asgi.sys_applications.genro_api.genro_api_app:Application"

# ══════════════════════════════════════════════════════════════════
# SEZIONI PARAMETRICHE
# ══════════════════════════════════════════════════════════════════

# ── Middleware Config ──
middleware-cors:
  allow_origins: ["https://myapp.com"]
  allow_methods: ["GET", "POST", "PUT", "DELETE"]
  allow_credentials: true
  max_age: 3600

middleware-auth:
  default_backend: bearer

middleware-auth-bearer:
  api_reader:
    token: "tk_read_xxx"
    tags: "read"
  api_writer:
    token: "tk_write_xxx"
    tags: "read,write"
  api_admin:
    token: "tk_admin_xxx"
    tags: "read,write,admin"

middleware-auth-basic:
  admin:
    password: "supersecret"
    tags: "admin"

middleware-compression:
  minimum_size: 1000
  compression_level: 6

middleware-logging:
  level: "DEBUG"
  include_headers: true

# ── Apps Config ──
apps-shop:
  db_uri: "postgresql://localhost/shop"
  cache_timeout: 300

apps-shop-cache:
  backend: redis
  host: "localhost"
  port: 6379

apps-admin:
  require_auth: true
  allowed_roles: ["admin"]

apps-docs:
  base_dir: "./public/docs"
  index: "index.html"
  spa_mode: true

# ── Sys Apps Config ──
sys_apps-swagger:
  theme: "dark"
  persist_authorization: true
```

## Environment Variables

Le variabili d'ambiente usano il prefisso `GENRO_ASGI_`:

| Variable | Maps to |
|----------|---------|
| `GENRO_ASGI_HOST` | `host` |
| `GENRO_ASGI_PORT` | `port` |
| `GENRO_ASGI_RELOAD` | `reload` |
| `GENRO_ASGI_MAIN_APP` | `main_app` |

Per sezioni parametriche, usare `__` come separatore:

| Variable | Maps to |
|----------|---------|
| `GENRO_ASGI_MIDDLEWARE_CORS__ALLOW_ORIGINS` | `middleware-cors.allow_origins` |
| `GENRO_ASGI_APPS_SHOP__DB_URI` | `apps-shop.db_uri` |

## CLI Arguments

```bash
genro-asgi serve <server_dir> [options]

Options:
  --host HOST       Server host (default: 127.0.0.1)
  --port PORT       Server port (default: 8000)
  --reload          Enable auto-reload
  --config FILE     Config file name (default: config.yaml)
  --main-app NAME   Default app for "/" redirect
```

## Configuration Precedence

```
1. Built-in DEFAULTS              (lowest priority)
2. App config: app_dir/config.yaml
3. Global config: ~/.genro-asgi/config.yaml
4. Project config: server_dir/config.yaml
5. Environment variables: GENRO_ASGI_*
6. CLI arguments
7. Explicit constructor parameters  (highest priority)
```

## Parsing Rules

### Boolean Values

```yaml
# Tutti equivalenti a True
middleware:
  cors: on
  cors: true
  cors: yes
  cors: 1

# Tutti equivalenti a False
middleware:
  cors: off
  cors: false
  cors: no
  cors: 0
```

### Tags (String or List)

```yaml
middleware-auth-bearer:
  token1:
    tags: "read,write"           # → ["read", "write"]
  token2:
    tags: ["read", "write"]      # → ["read", "write"]
  token3:
    # tags omesso → []
```

### Module Shorthand

```yaml
apps:
  # Full form
  shop: "shop_app:Application"

  # Shorthand (assume :Application)
  shop: "shop_app"
```

## Migration from v1

### Mapping delle chiavi

| v1 | v2 |
|----|-----|
| `server.host` | `host` |
| `server.port` | `port` |
| `server.reload` | `reload` |
| `server.main_app` | `main_app` |
| `cors_middleware` | `middleware-cors` |
| `auth_middleware` | `middleware-auth` |
| `auth_middleware.bearer` | `middleware-auth-bearer` |
| `apps.shop.module` | `apps.shop` (value) |
| `apps.shop.db_uri` | `apps-shop.db_uri` |

### Script di migrazione

Sarà fornito uno script `migrate-config-v1-to-v2.py` per la migrazione automatica.

## Related Documents

- [AsgiServer](01_asgi_server.md) - Server architecture
- [Middleware Chain](../06_security_and_middleware/01_middleware_chain.md) - Middleware system
- [genro-toolbox SmartOptions](../../spec_details/dependencies/genro-toolbox.md) - Configuration engine

---

**Last Updated**: 2025-01-02
