# Daily Meal API

API Laravel de Daily Meal.

## Docker en production

Le fichier source du dépôt est `docker-compose.prod.yml`. Sur le serveur, il est
déployé sous le nom `docker-compose.yml`. Les commandes de ce guide sont donc
exécutées depuis le dossier de l'application sans option `-f`.

### Configuration

Le Compose charge la configuration applicative depuis le `.env` de production.
Les variables Laravel ne sont pas répétées dans le fichier Compose afin de
conserver une seule source de vérité.

Exemple des variables propres à l'infrastructure :

```dotenv
IMAGE_TAG=2026-08-13.1

APP_ENV=production
APP_DEBUG=false
LOG_CHANNEL=stderr
LOG_LEVEL=warning

DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=bowli
DB_USERNAME=bowli
DB_PASSWORD=change-me

REDIS_HOST=redis
QUEUE_CONNECTION=redis
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://meilisearch:7700
MEILISEARCH_KEY=change-me

FILESYSTEM_DISK=s3
AWS_ACCESS_KEY_ID=change-me
AWS_SECRET_ACCESS_KEY=change-me
AWS_DEFAULT_REGION=eu-west-3
```

`IMAGE_TAG` est obligatoire. Utiliser un tag de release immuable permet de savoir
exactement quelle version est déployée et évite l'utilisation accidentelle de
`latest`.

### Volumes et réseau externes

PostgreSQL, Redis, Meilisearch et les sauvegardes locales utilisent des volumes
externes. Ils restent présents quand le projet Compose est recréé.

À effectuer une seule fois sur un nouveau serveur :

```bash
docker volume create bemeal_pgsql_data
docker volume create bemeal_redis_data
docker volume create bemeal_meilisearch_data
docker volume create bemeal_database_backups
docker network create proxy
```

Ces commandes sont idempotentes. Le réseau `proxy` peut déjà avoir été créé par
le reverse proxy. Ne jamais supprimer `bemeal_pgsql_data` sans avoir validé une
sauvegarde.

### Premier démarrage

```bash
docker compose config -q
docker compose pull app db-backup postgres redis meilisearch
docker compose up -d
docker compose ps
```

L'image `db-backup` contient `pg_dump`, `pg_restore`, AWS CLI, le cron et les
scripts de `docker/database-backup`. Elle est construite et publiée dans le
registre par la CI. Le serveur la télécharge avec `docker compose pull` et n'a
donc pas besoin de posséder le code source ni de construire une image localement.

### Mettre à jour l'application

Après avoir changé `IMAGE_TAG` dans `.env` :

```bash
docker compose config -q
docker compose pull app db-backup
docker compose up -d --remove-orphans
docker compose ps
```

Si les migrations ne sont pas déjà gérées par le pipeline ou l'image :

```bash
docker compose exec app php artisan migrate --force
```

## Sauvegardes PostgreSQL vers S3

Le service `db-backup` lance une sauvegarde au démarrage, puis chaque jour à
03:00. Chaque archive est compressée, contrôlée avec `pg_restore`, accompagnée
d'un checksum SHA-256 puis envoyée vers S3 avec le chiffrement AES-256. Les
copies locales sont conservées 7 jours par défaut.

```dotenv
BACKUP_CRON="0 3 * * *"
BACKUP_RUN_ON_START=true
BACKUP_LOCAL_RETENTION_DAYS=7
BACKUP_S3_BUCKET=bowli-backup
BACKUP_S3_PREFIX=database-backups/postgres
BACKUP_S3_ENDPOINT=
TZ=Europe/Paris
```

Ces valeurs sont les valeurs par défaut et peuvent être omises du `.env`. Le
bucket `bowli-backup` doit rester entièrement privé. `BACKUP_S3_ENDPOINT` reste
vide avec AWS S3.

### Autorisations IAM minimales

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListDatabaseBackups",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::bowli-backup",
      "Condition": {
        "StringLike": {
          "s3:prefix": [
            "database-backups/postgres",
            "database-backups/postgres/*"
          ]
        }
      }
    },
    {
      "Sid": "ManageDatabaseBackupObjects",
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::bowli-backup/database-backups/postgres/*"
    }
  ]
}
```

Le conteneur ne supprime pas les sauvegardes distantes. Configurer une règle de
cycle de vie S3 sur ce préfixe, par exemple après 30 ou 90 jours.

### Sauvegarde manuelle et contrôle

```bash
docker compose exec db-backup /usr/local/bin/backup-database manual
docker compose ps db-backup
docker compose logs --tail=100 db-backup
```

## Restaurer une sauvegarde

Pour restaurer la sauvegarde S3 la plus récente :

```bash
./docker/database-backup/restore-production latest-s3
```

Autres sources acceptées :

```bash
# Dernière sauvegarde du volume local
./docker/database-backup/restore-production latest-local

# Sauvegarde S3 précise
./docker/database-backup/restore-production \
  s3://bowli-backup/database-backups/postgres/20260813T030000Z-bowli-scheduled.dump
```

La restauration demande une confirmation explicite. Elle arrête l'application
et les workers, crée une sauvegarde de sécurité, vérifie l'archive, restaure dans
une base temporaire puis échange les bases. L'ancienne base est conservée sous
un nom daté pour permettre un rollback.

Les services redémarrent uniquement si toute l'opération réussit. En cas
d'échec, ils restent arrêtés afin d'éviter d'écrire dans une base potentiellement
incomplète.

Pour utiliser exceptionnellement un autre nom de fichier Compose :

```bash
COMPOSE_FILE=docker-compose.prod.yml \
  ./docker/database-backup/restore-production latest-s3
```
