diff --git a/README.md b/README.md index eb5305d..3193c90 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,17 @@ Applicazione web per la **gestione dell'assegnazione e rientro di territori** (c - [Panoramica](#panoramica) - [Stack tecnologico](#stack-tecnologico) - [Requisiti di sistema](#requisiti-di-sistema) -- [Installazione rapida](#installazione-rapida) +- [Installazione da zero](#installazione-da-zero) - [Configurazione](#configurazione) +- [Migrazione su nuovo server](#migrazione-su-nuovo-server) +- [Comandi utili](#comandi-utili) +- [Troubleshooting](#troubleshooting) - [Struttura del progetto](#struttura-del-progetto) - [Funzionalità principali](#funzionalità-principali) - [Ruoli e permessi (RBAC)](#ruoli-e-permessi-rbac) - [Sicurezza e GDPR](#sicurezza-e-gdpr) - [Modello dati](#modello-dati) - [Regole di business](#regole-di-business) -- [Pagine dell'applicazione](#pagine-dellapplicazione) -- [Comandi utili](#comandi-utili) - [Produzione](#produzione) - [Licenza](#licenza) @@ -54,72 +55,142 @@ Applicazione web per la **gestione dell'assegnazione e rientro di territori** (c | **Asset Build** | Vite 6.0 + @tailwindcss/vite | | **Node.js** | 20 LTS (nel container PHP) | | **Mail (dev)** | Mailpit | -| **Container** | Docker Compose (5 servizi) | +| **Container** | Docker Compose (6 servizi) | --- ## Requisiti di sistema - **Docker** >= 24.0 e **Docker Compose** >= 2.20 -- Oppure, senza Docker: - - PHP >= 8.3 con estensioni: pdo_mysql, mbstring, gd, intl, zip, bcmath, redis, opcache - - Composer >= 2.7 - - Node.js >= 20 LTS + npm - - MariaDB >= 11 (o MySQL 8) - - Redis >= 7 +- **Git** per clonare il repository +- **Porte libere** (configurabili nel `.env`): + +| Porta | Servizio | Variabile `.env` | +|---------|-------------------------|-------------------| +| `8080` | Applicazione web (Nginx)| `APP_PORT` | +| `1025` | SMTP Mailpit | `MAIL_PORT` | +| `8025` | UI Mailpit | `MAILPIT_UI_PORT` | + +> MariaDB (`3306`) e Redis (`6379`) non espongono porte sull'host: sono accessibili solo dalla rete interna Docker. --- -## Installazione +## Installazione da zero -### Prerequisiti - -- **Docker** e **Docker Compose** (v2) installati -- Porte libere: `8080` (app), `3306` (MariaDB), `6379` (Redis), `8025` (Mailpit) - -### Procedura +### Passo 1 — Clona il repository ```bash -# 1. Clona il repository git clone termanager2 cd termanager2 - -# 2. Configura l'ambiente -cp .env.example .env - -# 3. Imposta i permessi dei file (UID 1000 = appuser nel container) -chown -R 1000:1000 . -mkdir -p storage/app/public storage/framework/{cache/data,sessions,views} storage/logs bootstrap/cache -chmod -R 775 storage bootstrap/cache - -# 4. Avvia i container (build al primo avvio) -docker compose up -d --build ``` -Il codice applicativo e montato direttamente dal filesystem host, quindi ogni modifica locale e immediatamente visibile nel container (senza rebuild ad ogni edit). - -Al primo avvio del container `app` vengono eseguiti automaticamente: - -- setup `.env` e `APP_KEY` -- install/build dipendenze -- `php artisan migrate --force` -- `php artisan db:seed --force` **solo al primo avvio** (marker persistente) -- creazione admin iniziale se il database non ha utenti - -> **Nota**: La `APP_KEY` viene generata automaticamente al primo avvio se assente nel `.env`. - -Al termine l'applicazione sarà disponibile su: **http://localhost:8080** - -### Reset completo - -Per ripartire da zero (elimina database e volumi): +### Passo 2 — Crea il file `.env` ```bash -docker compose down -v --remove-orphans -docker compose up -d --build -# Ripetere i passaggi dal punto 4 in poi +cp .env.example .env ``` +### Passo 3 — Configura le variabili obbligatorie + +Apri `.env` con un editor e imposta **almeno** queste variabili: + +```bash +nano .env +``` + +```dotenv +# --- Credenziali admin iniziale (OBBLIGATORIE al primo avvio) --- +INITIAL_ADMIN_NAME="Mario Rossi" +INITIAL_ADMIN_EMAIL=admin@esempio.it +INITIAL_ADMIN_PASSWORD=UnaPasswordSicura123 + +# --- URL dell'applicazione (adatta al tuo dominio/IP) --- +APP_URL=http://localhost:8080 +ASSET_URL=http://localhost:8080 + +# --- Password database (cambia i default in produzione!) --- +DB_PASSWORD=una_password_sicura +DB_ROOT_PASSWORD=una_root_password_sicura +REDIS_PASSWORD=una_redis_password_sicura +``` + +> **Importante**: `INITIAL_ADMIN_NAME`, `INITIAL_ADMIN_EMAIL` e `INITIAL_ADMIN_PASSWORD` sono obbligatorie. +> Se mancano e il database è vuoto, il container si avvia ma mostra un warning e non potrai accedere. + +### Passo 4 — Imposta i permessi + +Il container PHP gira con UID/GID `1000`. I file devono appartenere a questo utente: + +```bash +# Crea le cartelle necessarie +mkdir -p storage/app/public \ + storage/framework/cache/data \ + storage/framework/sessions \ + storage/framework/views \ + storage/logs \ + bootstrap/cache + +# Imposta proprietario e permessi +sudo chown -R 1000:1000 . +chmod -R 775 storage bootstrap/cache +``` + +### Passo 5 — Avvia i container (primo avvio) + +```bash +docker compose up -d --build +``` + +Questo comando: +1. **Costruisce** le immagini Docker (PHP + Nginx) +2. **Avvia** 6 servizi: `app`, `nginx`, `queue-worker`, `mariadb`, `redis`, `mailpit` +3. L'entrypoint del container `app` esegue automaticamente: + - Copia `.env.example` → `.env` (se mancante) + - Installa le dipendenze Composer (`vendor/`) + - Genera `APP_KEY` (se vuota) e la salva in `storage/app/.app_key` + - Installa le dipendenze NPM (`node_modules/`) + - Compila gli asset frontend CSS/JS (`public/build/`) + - Crea il symlink `public/storage` → `storage/app/public` + - Esegue le migrazioni database + - Esegue il seed iniziale (ruoli e permessi) + - Crea l'account admin iniziale + - Mette in cache config, routes e views + +### Passo 6 — Verifica che tutto funzioni + +```bash +# Controlla che tutti i container siano "healthy" +docker compose ps +``` + +Output atteso — tutti i container devono essere `Up` (e `healthy` dove previsto): + +``` +NAME STATUS PORTS +termanager2_app Up (healthy) 9000/tcp +termanager2_nginx Up 0.0.0.0:8080->80/tcp +termanager2_queue Up 9000/tcp +termanager2_db Up (healthy) 3306/tcp +termanager2_redis Up (healthy) 6379/tcp +termanager2_mail Up 0.0.0.0:1025->1025/tcp, 0.0.0.0:8025->8025/tcp +``` + +Se un container non è healthy, controlla i log: + +```bash +# Log del container app (il più importante) +docker compose logs app --tail=100 + +# Log di tutti i container +docker compose logs --tail=50 +``` + +### Passo 7 — Accedi all'applicazione + +Apri il browser su: **http://localhost:8080** (o la porta configurata in `APP_PORT`) + +Accedi con le credenziali impostate in `INITIAL_ADMIN_EMAIL` e `INITIAL_ADMIN_PASSWORD`. + --- ## Configurazione @@ -128,36 +199,125 @@ docker compose up -d --build | Variabile | Default | Descrizione | |-----------------------|----------------------|------------------------------------------| -| `APP_KEY` | (generata) | Chiave AES-256 per cifratura. **Mai condividere** | -| `APP_PORT` | `8080` | Porta host per l'applicazione | -| `SEED_DEV_DATA` | `false` | Se `true`, `php artisan db:seed` include anche i dati demo | -| `RUN_DB_SEED_ON_FIRST_START` | `true` | Se `true`, esegue il seed automatico solo al primo avvio container | -| `ENSURE_INITIAL_ADMIN_ON_EMPTY_DB` | `true` | Se `true`, richiede/crea admin iniziale quando non esistono utenti | -| `INITIAL_ADMIN_NAME` | vuoto | Nome admin creato al primo avvio | -| `INITIAL_ADMIN_EMAIL` | vuoto | Email admin creata al primo avvio | -| `INITIAL_ADMIN_PASSWORD` | vuoto | Password admin creata al primo avvio (min 8) | +| `APP_KEY` | (auto-generata) | Chiave AES-256 per cifratura dati. **Mai condividere.** Viene generata automaticamente al primo avvio | +| `APP_URL` | da `.env.example` | URL completo dell'applicazione (es. `https://miodominio.it`) | +| `ASSET_URL` | da `.env.example` | URL base per gli asset CSS/JS (normalmente uguale a `APP_URL`) | +| `APP_PORT` | `8080` | Porta host su cui Nginx espone l'app | +| `APP_ENV` | `local` | Ambiente: `local` (sviluppo) o `production` | +| `APP_DEBUG` | `true` | Mostra errori dettagliati. **Impostare `false` in produzione** | +| `SEED_DEV_DATA` | `false` | Se `true`, il seed include dati demo di test | +| `RUN_DB_SEED_ON_FIRST_START` | `true` | Se `true`, esegue il seed automatico solo al primo avvio | +| `ENSURE_INITIAL_ADMIN_ON_EMPTY_DB` | `true` | Se `true`, crea admin iniziale quando non esistono utenti | +| `INITIAL_ADMIN_NAME` | (vuoto) | Nome dell'admin iniziale — **obbligatorio al primo avvio** | +| `INITIAL_ADMIN_EMAIL` | (vuoto) | Email dell'admin iniziale — **obbligatorio al primo avvio** | +| `INITIAL_ADMIN_PASSWORD` | (vuoto) | Password dell'admin iniziale (min 8 caratteri) — **obbligatorio al primo avvio** | | `DB_DATABASE` | `termanager2` | Nome database MariaDB | | `DB_USERNAME` | `termanager2` | Utente database | -| `DB_PASSWORD` | `secret` | Password database | -| `DB_ROOT_PASSWORD` | `rootsecret` | Password root MariaDB | -| `REDIS_PASSWORD` | `redissecret` | Password Redis | -| `MAIL_PORT` | `1025` | Porta SMTP Mailpit | -| `MAILPIT_UI_PORT` | `8025` | UI Mailpit per debug email | -| `USER_ID` / `GROUP_ID`| `1000` | UID/GID container (match con host) | +| `DB_PASSWORD` | `secret` | Password database — **cambiare in produzione** | +| `DB_ROOT_PASSWORD` | `rootsecret` | Password root MariaDB — **cambiare in produzione** | +| `REDIS_PASSWORD` | `redissecret` | Password Redis — **cambiare in produzione** | +| `MAIL_PORT` | `1025` | Porta SMTP (Mailpit in dev, SMTP reale in prod) | +| `MAILPIT_UI_PORT` | `8025` | Porta UI Mailpit per debug email | -### Configurazione iniziale +### Configurazione applicativa -La configurazione viene gestita dalla sezione **Impostazioni** (menu amministrazione), senza wizard iniziale. +Dopo il primo accesso, la configurazione si gestisce dalla sezione **Impostazioni** nel menu (solo Amministratore): + +- Nome congregazione +- Soglia mesi per priorità automatica territori +- Soglia giorni per "da rientrare" +- Retention giorni audit log ### Credenziali iniziali -Le credenziali non sono hardcoded nel progetto. Al primo avvio devi impostare in `.env`: +Le credenziali admin non sono hardcoded. Al primo avvio devi impostare nel `.env`: - `INITIAL_ADMIN_NAME` - `INITIAL_ADMIN_EMAIL` - `INITIAL_ADMIN_PASSWORD` -Se il database e vuoto e queste variabili non sono valorizzate, il container `app` interrompe l'avvio con errore esplicito. +Se il database è vuoto e queste variabili non sono valorizzate, il container `app` mostra un warning e non verrà creato l'account admin. + +--- + +## Migrazione su nuovo server + +Quando sposti TerManager2 su un nuovo server, segui questa procedura: + +### 1. Copia i file sul nuovo server + +```bash +# Dal vecchio server: crea un archivio (escludi vendor, node_modules e volumi Docker) +tar czf termanager2-backup.tar.gz \ + --exclude='vendor' \ + --exclude='node_modules' \ + --exclude='public/build' \ + --exclude='.git' \ + termanager2/ + +# Copia sul nuovo server +scp termanager2-backup.tar.gz utente@nuovo-server:/home/utente/Docker/ + +# Sul nuovo server: estrai +cd /home/utente/Docker +tar xzf termanager2-backup.tar.gz +cd termanager2 +``` + +### 2. Verifica il file `.env` + +```bash +# Controlla che .env esista e contenga i valori corretti +cat .env + +# Adatta APP_URL e ASSET_URL al nuovo dominio/IP +nano .env +``` + +> **Critico**: se il nuovo server ha un URL/IP diverso, aggiorna `APP_URL` e `ASSET_URL`. + +### 3. Imposta i permessi + +```bash +sudo chown -R 1000:1000 . +chmod -R 775 storage bootstrap/cache +``` + +### 4. Ricostruisci e avvia + +```bash +docker compose up -d --build +``` + +### 5. Forza la ricompilazione degli asset + +Se la pagina appare senza stile (CSS mancante), ricompila gli asset: + +```bash +# Ricompila CSS e JS dentro il container +docker compose exec -u root app bash -c "npm install --no-audit --no-fund && npm run build" + +# Correggi i permessi dei file generati +docker compose exec -u root app chown -R 1000:1000 public/build node_modules +``` + +### 6. (Opzionale) Ripristina il database + +Se hai un dump SQL dal vecchio server: + +```bash +# Copia il dump nel container MariaDB +docker cp backup.sql termanager2_db:/tmp/backup.sql + +# Importa il dump +docker compose exec mariadb mysql -u root -p"$(grep DB_ROOT_PASSWORD .env | cut -d= -f2)" termanager2 < /tmp/backup.sql + +# Esegui le eventuali migrazioni mancanti +docker compose exec app php artisan migrate --force + +# Pulisci la cache +docker compose exec app php artisan optimize:clear +``` --- @@ -377,27 +537,57 @@ Il menu sidebar mostra solo le voci per cui l'utente ha permesso. ## Comandi utili +### Gestione container + ```bash -# Avviare i container +# Avviare i container (dopo il primo build) docker compose up -d -# Fermare i container +# Avviare con ricostruzione immagini (dopo modifiche a Dockerfile o dipendenze) +docker compose up -d --build + +# Fermare i container (i dati persistono nei volumi) docker compose down -# Shell nel container app +# Fermare e CANCELLARE tutti i dati (database, redis, uploads) +docker compose down -v --remove-orphans + +# Stato dei container +docker compose ps + +# Log in tempo reale (tutti i container) +docker compose logs -f + +# Log di un singolo container +docker compose logs app --tail=100 +docker compose logs nginx --tail=100 +docker compose logs mariadb --tail=100 + +# Riavviare un singolo container +docker compose restart app +``` + +### Shell interattiva + +```bash +# Shell nel container app (come utente 1000) docker compose exec app bash +# Shell nel container app come root (per operazioni di sistema) +docker compose exec -u root app bash + +# Shell nel container MariaDB +docker compose exec mariadb mysql -u root -p +``` + +### Laravel / Artisan + +```bash # Eseguire migrazioni -docker compose exec app php artisan migrate +docker compose exec app php artisan migrate --force -# Seed dati di sviluppo -docker compose exec app php artisan db:seed - -# Compilare asset (dev con hot reload) -docker compose exec app npm run dev - -# Compilare asset (produzione) -docker compose exec app npm run build +# Seed del database +docker compose exec app php artisan db:seed --force # Pulizia manuale audit log docker compose exec app php artisan audit:cleanup @@ -407,36 +597,160 @@ docker compose exec app php artisan config:cache docker compose exec app php artisan route:cache docker compose exec app php artisan view:cache -# Svuotare cache +# Svuotare TUTTA la cache docker compose exec app php artisan optimize:clear ``` +### Asset frontend (CSS/JS) + +```bash +# Compilare asset per produzione +docker compose exec -u root app bash -c "npm install --no-audit --no-fund && npm run build" +docker compose exec -u root app chown -R 1000:1000 public/build node_modules + +# Compilare asset per sviluppo (hot reload, richiede porta 5173 libera) +docker compose exec app npm run dev +``` + +### Backup e ripristino database + +```bash +# Backup del database (sostituisci le credenziali) +docker compose exec mariadb mysqldump -u root -prootsecret termanager2 > backup_$(date +%Y%m%d).sql + +# Ripristino da backup +docker compose exec -T mariadb mysql -u root -prootsecret termanager2 < backup_20250101.sql +docker compose exec app php artisan migrate --force +docker compose exec app php artisan optimize:clear +``` + +--- + +## Troubleshooting + +### La pagina appare senza stile (CSS mancante) + +**Sintomo**: la pagina di login appare con testo non formattato, senza colori né layout. + +**Causa**: il file CSS compilato manca da `public/build/assets/`. Succede tipicamente dopo una migrazione su nuovo server. + +**Soluzione**: + +```bash +# Ricompila gli asset come root (evita problemi di permessi) +docker compose exec -u root app bash -c "npm install --no-audit --no-fund && npm run build" + +# Correggi i permessi +docker compose exec -u root app chown -R 1000:1000 public/build node_modules +``` + +### Il container `app` non diventa healthy + +**Sintomo**: `docker compose ps` mostra `app` come `starting` o `unhealthy`. + +**Cosa controllare**: + +```bash +# Guarda i log per capire dove si blocca +docker compose logs app --tail=200 +``` + +Cause comuni: +- **MariaDB non pronto**: l'entrypoint ripete il tentativo 10 volte. Attendi qualche secondo. +- **Credenziali admin mancanti**: se `ENSURE_INITIAL_ADMIN_ON_EMPTY_DB=true` e le variabili `INITIAL_ADMIN_*` sono vuote, il container mostra un warning. +- **Errore di permessi**: esegui `sudo chown -R 1000:1000 .` sulla cartella del progetto. + +### Errore "Permission denied" su storage o bootstrap/cache + +```bash +sudo chown -R 1000:1000 storage bootstrap/cache +chmod -R 775 storage bootstrap/cache +``` + +### La `APP_KEY` è cambiata e i dati cifrati non sono leggibili + +La `APP_KEY` viene salvata in `storage/app/.app_key` per persistere tra i riavvii. Se perdi questo file: + +- I **nomi e cognomi dei proclamatori** (cifrati con AES-256) diventano illeggibili +- Le **sessioni** vengono invalidate + +**Prevenzione**: dopo il primo avvio, salva il valore di `APP_KEY` dal file `.env` in un luogo sicuro. + +### Errore npm "EACCES" o "vite: not found" + +```bash +# Installa le dipendenze npm come root +docker compose exec -u root app npm install --no-audit --no-fund + +# Verifica che vite sia installato +docker compose exec app ls node_modules/.bin/vite + +# Compila come root +docker compose exec -u root app npm run build + +# Poi correggi i permessi +docker compose exec -u root app chown -R 1000:1000 public/build node_modules +``` + +### Reset completo (ripartire da zero) + +> **Attenzione**: questo cancella il database, i file Redis e tutti i dati applicativi. + +```bash +docker compose down -v --remove-orphans +sudo chown -R 1000:1000 . +rm -rf node_modules public/build vendor storage/app/.app_key storage/framework/.runtime_db_seeded +docker compose up -d --build +``` + --- ## Produzione -Per il deploy in produzione: +### Checklist deploy in produzione -1. **Variabili d'ambiente**: - - `APP_ENV=production` - - `APP_DEBUG=false` - - Password sicure per DB e Redis (non i default) - - `APP_KEY` generata e conservata come secret +1. **Variabili d'ambiente** — modifica nel `.env`: + ```dotenv + APP_ENV=production + APP_DEBUG=false + APP_URL=https://tuodominio.it + ASSET_URL=https://tuodominio.it + ``` -2. **HTTPS**: configurare un reverse proxy (Traefik, Nginx) con certificato SSL/TLS e HSTS +2. **Password sicure** — cambia TUTTI i default: + ```dotenv + DB_PASSWORD= + DB_ROOT_PASSWORD= + REDIS_PASSWORD= + ``` -3. **Immagini Docker**: build senza volume codice montato, asset pre-compilati +3. **HTTPS** — configura un reverse proxy (Traefik, Nginx, Caddy) con certificato SSL/TLS davanti alla porta `APP_PORT` -4. **Backup**: backup cifrati del database con rotazione automatica +4. **Cache** — al primo avvio viene fatto automaticamente dall'entrypoint, ma dopo modifiche: + ```bash + docker compose exec app php artisan config:cache + docker compose exec app php artisan route:cache + docker compose exec app php artisan view:cache + ``` -5. **Segreti**: gestire `APP_KEY`, password DB/Redis con Docker secrets o secret manager +5. **Backup** — programma backup regolari del database: + ```bash + # Cron job giornaliero (esempio) + 0 2 * * * cd /path/to/termanager2 && docker compose exec -T mariadb mysqldump -u root -prootsecret termanager2 | gzip > /backups/termanager2_$(date +\%Y\%m\%d).sql.gz + ``` -6. **Performance**: - - `php artisan config:cache && route:cache && view:cache` - - OPcache abilitato (già configurato in `php.ini`) - - Redis per cache, sessioni e code +6. **APP_KEY** — salva il valore di `APP_KEY` dal `.env` in un luogo sicuro (password manager, vault). Senza di essa i dati cifrati dei proclamatori sono irrecuperabili. -7. **Monitoraggio**: configurare health checks e log aggregation +7. **Mailpit** — in produzione rimuovi il servizio `mailpit` dal `docker-compose.yml` e configura un server SMTP reale: + ```dotenv + MAIL_MAILER=smtp + MAIL_HOST=smtp.tuoprovider.it + MAIL_PORT=587 + MAIL_USERNAME=utente@tuodominio.it + MAIL_PASSWORD=password-smtp + MAIL_ENCRYPTION=tls + MAIL_FROM_ADDRESS=noreply@tuodominio.it + ``` --- diff --git a/public/build/manifest.json b/public/build/manifest.json index 39bb95d..47447d6 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -1,6 +1,6 @@ { "resources/css/app.css": { - "file": "assets/app-D-SuF610.css", + "file": "assets/app-DL92hBto.css", "src": "resources/css/app.css", "isEntry": true },