# Guide de Deploiement - Wordly.art (Homelab)
> NPM + IONOS + Docker + Stripe + API Keys + NAS Backup + Monitoring

---

## Architecture

```
Internet
    |
    | DNS IONOS: wordly.art -> IP fixe
    |
[Routeur/Box] (port forwarding 80+443 -> machine NPM)
    |
    +-- Machine 1: Nginx Proxy Manager (NPM)
    |     - SSL Let's Encrypt auto
    |     - Reverse proxy
    |
    +-- Machine 2/3: Docker (192.168.1.151)
          |
          +-- wordly-backend    (FastAPI :8000)
          +-- wordly-frontend   (Next.js :3000)
          +-- wordly-postgres   (PostgreSQL :5432)
          +-- wordly-redis      (Redis :6379)
          +-- wordly-prometheus (:9090 interne)
          +-- wordly-grafana    (:3001)
          +-- wordly-node-exporter + cadvisor
          |
          +-- Backup quotidien -> NAS
```

---

## Etape 1 : DNS IONOS

1. Se connecter sur **ionos.fr** > **Domaines & SSL** > **wordly.art** > **DNS**
2. Modifier les enregistrements **A** existants :

| Type | Nom (hote) | Valeur | Attention |
|------|-----------|--------|-----------|
| **A** | *(vide ou @)* | Ton IP fixe | Pas dans "Sous-domaines", dans DNS principal |
| **A** | `www` | Ton IP fixe | Dans Sous-domaines, taper juste `www` |
| **A** | `monitoring` | Ton IP fixe | Dans Sous-domaines, taper juste `monitoring` |

> Ton IP fixe : ouvre https://api.ipify.org depuis ton reseau

Ne **pas** toucher aux lignes Mail (MX, SPF, DKIM, autodiscover).

Verifier apres 10 min :
```bash
nslookup wordly.art
```

---

## Etape 2 : Port forwarding + NPM

### 2.1 Routeur : ouvrir les ports

Sur ton routeur/box (souvent `192.168.1.1`) :

| Port externe | Port interne | IP destination |
|-------------|-------------|---------------|
| **80** | 80 | IP de la machine NPM |
| **443** | 443 | IP de la machine NPM |

### 2.2 NPM : creer les Proxy Hosts

Interface NPM : **http://IP_NPM:81**

#### Proxy Host 1 : wordly.art

**Details :**
- Domain Names : `wordly.art`
- Scheme : `http`
- Forward Hostname/IP : `192.168.1.151` (IP du serveur Wordly)
- Forward Port : `3000`
- Cocher Block Common Exploits + Websockets

**SSL :**
- Request a new SSL Certificate
- Cocher Force SSL + HTTP/2 + HSTS
- Email : `admin@wordly.art`

**Advanced** - coller cette config nginx :

```nginx
location /api/ {
    proxy_pass http://192.168.1.151:8000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Connection "";
    proxy_connect_timeout 60s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;
    add_header Access-Control-Allow-Origin https://wordly.art always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
    add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-API-Key" always;
    add_header Access-Control-Allow-Credentials "true" always;
    if ($request_method = 'OPTIONS') { return 204; }
}

location /translate {
    proxy_pass http://192.168.1.151:8000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_connect_timeout 60s;
    proxy_send_timeout 600s;
    proxy_read_timeout 600s;
    client_max_body_size 100M;
}

location /health {
    proxy_pass http://192.168.1.151:8000;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

location /docs { proxy_pass http://192.168.1.151:8000; proxy_set_header Host $host; }
location /redoc { proxy_pass http://192.168.1.151:8000; proxy_set_header Host $host; }
location /openapi.json { proxy_pass http://192.168.1.151:8000; proxy_set_header Host $host; }
location /admin { proxy_pass http://192.168.1.151:3000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
location /_next/static/ { proxy_pass http://192.168.1.151:3000; add_header Cache-Control "public, max-age=31536000, immutable"; }
```

> Si NPM et Wordly sont sur la meme machine Docker, remplace `192.168.1.151:8000` par `wordly-backend:8000` etc.

#### Proxy Host 2 : www.wordly.art

Meme config, meme certificat SSL. Dans Advanced, ajoute :
```nginx
return 301 https://wordly.art$request_uri;
```

#### Proxy Host 3 : monitoring.wordly.art

- Forward : `192.168.1.151:3001`
- SSL Let's Encrypt

---

## Etape 3 : Serveur Docker (192.168.1.151)

### 3.1 Installer Docker

```bash
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
```

### 3.2 Cloner le projet

```bash
git clone -b production-deployment https://gitea.parsanet.org/sepehr/office_translator.git /opt/wordly
cd /opt/wordly
```

### 3.3 Configurer le .env avec le wizard

```bash
bash scripts/setup-env.sh
```

Le wizard demande le domaine, le mot de passe admin, le service de traduction, et genere tous les secrets automatiquement.

Ou configurer manuellement :
```bash
cp .env.production .env
nano .env
```

### 3.4 Lancer l'application

```bash
docker compose up -d --build
docker compose ps    # Verifier que tout est "Up (healthy)"
curl http://localhost:8000/health
```

---

## Etape 4 : Cles API - Services de traduction

L'application supporte **7 providers de traduction**. Tu n'es pas oblige de tous les configurer - commence avec 1 ou 2.

### Gerer les cles

```bash
cd /opt/wordly
bash scripts/manage-keys.sh
```

Menu interactif pour ajouter/modifier/supprimer des cles API.

### Ou trouver les cles

| Provider | URL d'inscription | Cle a recuperer | Cout |
|----------|-------------------|-----------------|------|
| **Google** | Aucune inscription | Aucune cle (gratuit via deep_translator) | Gratuit |
| **DeepL** | https://www.deepl.com/pro-api | Clé API DeepL (format: `xxx-xxx-...`) | Gratuit 500k car/mois, puis 5.49EUR/mois |
| **OpenAI** | https://platform.openai.com/api-keys | `sk-...` | ~0.15$/1000 traductions |
| **DeepSeek** | https://platform.deepseek.com/api_keys | `sk-...` | ~0.14$/M tokens (tres bon rapport Q/P) |
| **Minimax** | https://platform.minimaxi.com/ | Cle API | Variable |
| **OpenRouter** | https://openrouter.ai/keys | `sk-or-...` | Multi-modeles, pay-per-use |

### Activer un provider dans le .env

Exemple pour DeepSeek :

```bash
# Dans le .env
DEEPSEEK_ENABLED=true
DEEPSEEK_API_KEY=sk-votre-cle-ici
DEEPSEEK_MODEL=deepseek-chat
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
```

Puis `docker compose restart backend`.

### Recommandation pour demarrer

1. **Google** (active par defaut, gratuit, aucune cle)
2. **DeepSeek** (ajouter quand tu veux meilleure qualite, tres peu cher)

---

## Etape 5 : Stripe - Systeme de paiement

Stripe gere les abonnements (Starter, Pro, Business). Si tu ne configures pas Stripe, l'app fonctionne en mode gratuit sans limitation d'abonnement.

### 5.1 Creer un compte Stripe

1. Aller sur **https://dashboard.stripe.com/register**
2. Creer un compte avec ton email
3. Activer le compte (verifie identite + banque pour recevoir les paiements)

### 5.2 Recuperer les cles Stripe

Dans le dashboard Stripe : **Developers** > **API keys**

| Cle | Nom dans .env | Description |
|-----|---------------|-------------|
| `sk_test_...` ou `sk_live_...` | `STRIPE_SECRET_KEY` | Cle secrete (attention: NE PAS utiliser la cle publique `pk_...`) |
| `whsec_...` | `STRIPE_WEBHOOK_SECRET` | Cle du webhook (voir section 5.4) |

> **Test vs Live** : commence en mode test (`sk_test_...`), passe en live quand tout fonctionne.

### 5.3 Creer les produits et forfaits

Dans Stripe : **Products** > **Add product**

Cree 3 produits :

**Produit 1 : Starter**
- Name : `Starter`
- Price : `9.00 EUR` / Monthly (recurring)
- Une fois cree, clique sur le prix et copie le **Price ID** (`price_...`)

**Produit 2 : Pro**
- Name : `Pro`
- Price : `19.00 EUR` / Monthly

**Produit 3 : Business**
- Name : `Business`
- Price : `49.00 EUR` / Monthly

Copie les 3 Price IDs.

### 5.4 Configurer le Webhook Stripe

Le webhook permet a Stripe de notifier ton app quand un paiement reussit, un abonnement est annule, etc.

Dans Stripe : **Developers** > **Webhooks** > **Add endpoint**

- **Endpoint URL** : `https://wordly.art/api/v1/stripe/webhook`
- **Events a ecouter** (cliquer "Select events") :
  - `checkout.session.completed`
  - `customer.subscription.updated`
  - `customer.subscription.deleted`
  - `invoice.payment_succeeded`
  - `invoice.payment_failed`

Une fois le webhook cree, clique dessus et copie le **Signing secret** (`whsec_...`).

### 5.5 Ajouter les cles Stripe dans le .env

```bash
cd /opt/wordly
bash scripts/manage-keys.sh
# Choisir option 4 (Stripe)
```

Ou manuellement dans `.env` :

```bash
STRIPE_SECRET_KEY=sk_live_votre_cle_secrete
STRIPE_WEBHOOK_SECRET=whsec_votre_signing_secret
STRIPE_STARTER_PRICE_ID=price_votre_price_id_starter
STRIPE_PRO_PRICE_ID=price_votre_price_id_pro
STRIPE_BUSINESS_PRICE_ID=price_votre_price_id_business
```

Puis `docker compose restart backend`.

### 5.6 Tester Stripe en mode test

1. Utilise `sk_test_...` comme STRIPE_SECRET_KEY
2. Cree des produits en mode test (meme process)
3. Pour tester un paiement, Stripe fournit des cartes test :
   - **Succes** : `4242 4242 4242 4242`
   - **Echec** : `4000 0000 0000 0002`
   - Exp : n'importe quelle date future
   - CVC : n'importe quel nombre
4. Verifie dans Stripe Dashboard > Payments que les paiements apparaissent

### 5.7 Passer en production

Quand tout fonctionne en test :

1. Dans Stripe Dashboard, clique **"Activate your account"** en haut a gauche
2. Remplie les infos business (identite, banque)
3. Change dans le .env : `sk_test_...` -> `sk_live_...`
4. Refais les webhooks et produits en mode live
5. `docker compose restart backend`

### 5.8 Checklist Stripe

- [ ] Compte Stripe cree et verifie
- [ ] 3 produits crees (Starter 9EUR, Pro 19EUR, Business 49EUR)
- [ ] 3 Price IDs copies
- [ ] STRIPE_SECRET_KEY configure
- [ ] Webhook configure avec les 5 events
- [ ] STRIPE_WEBHOOK_SECRET configure
- [ ] 3 STRIPE_*_PRICE_ID configures
- [ ] Backend redemarre
- [ ] Test avec carte 4242 reussi

---

## Etape 6 : Backup automatique vers NAS

### 6.1 Monter le NAS

```bash
sudo apt install cifs-utils
sudo mkdir -p /mnt/nas-backups/wordly

sudo tee /etc/nas-credentials <<EOF
username=wordly-backup
password=MOT_DE_PASSE_NAS
domain=WORKGROUP
EOF
sudo chmod 600 /etc/nas-credentials

echo "//IP_DU_NAS/wordly-backups /mnt/nas-backups/wordly cifs credentials=/etc/nas-credentials,uid=$(id -u),gid=$(id -g),iocharset=utf8,vers=3.0,noperm 0 0" | sudo tee -a /etc/fstab
sudo mount /mnt/nas-backups/wordly
```

### 6.2 Tester et programmer

```bash
cd /opt/wordly
bash scripts/backup-to-nas.sh --full
ls -lh /mnt/nas-backups/wordly/daily/

# Programmer le cron quotidien a 3h
crontab -e
# Ajouter :
0 3 * * * /opt/wordly/scripts/backup-to-nas.sh >> /var/log/wordly-backup.log 2>&1
```

---

## Etape 7 : Monitoring (Prometheus + Grafana)

### 7.1 Lancer

```bash
cd /opt/wordly
docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d
```

### 7.2 Acceder

- **URL** : `https://monitoring.wordly.art`
- **Login** : `admin` / mot de passe defini dans le .env

### 7.3 Dashboards

| Dashboard | Contenu |
|-----------|---------|
| Wordly - Application | Traductions, latence, providers, taux d'erreur |
| Wordly - Infrastructure | CPU, RAM, disque, reseau, status containers |

Alertes pre-configures : backend down, erreur > 10%, RAM > 90%, disque < 15%.

---

## Etape 8 : Gitea Actions - Deploiement automatique

### 8.1 Installer le runner sur 192.168.1.151

```bash
mkdir -p /opt/gitea-runner && cd /opt/gitea-runner
wget https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-linux-amd64
chmod +x act_runner-linux-amd64 && mv act_runner-linux-amd64 act_runner
./act_runner generate-config > config.yaml
```

### 8.2 Enregistrer le runner

1. Gitea > office_translator > Settings > Actions > Runners > Create new Runner
2. Copier le token

```bash
cd /opt/gitea-runner
./act_runner register \
  --instance https://gitea.parsanet.org \
  --token LE_TOKEN \
  --name homelab-runner \
  --labels self-hosted
```

### 8.3 Service systemd

```bash
sudo tee /etc/systemd/system/gitea-runner.service <<EOF
[Unit]
Description=Gitea Act Runner
After=docker.service
Requires=docker.service

[Service]
Type=simple
User=root
WorkingDirectory=/opt/gitea-runner
ExecStart=/opt/gitea-runner/act_runner daemon --config config.yaml
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now gitea-runner
sudo systemctl status gitea-runner
```

Desormais, chaque `git push` sur `production-deployment` deploye automatiquement.

---

## Etape 9 : Operations courantes

| Action | Commande |
|--------|----------|
| Voir les logs | `docker compose logs -f --tail=100` |
| Redemarrer backend | `docker compose restart backend` |
| Mettre a jour | `cd /opt/wordly && git pull && docker compose up -d --build` |
| Restaurer backup | `bash scripts/backup-to-nas.sh --restore` |
| Gerer les cles API | `bash scripts/manage-keys.sh` |
| Changer mot de passe admin | `bash scripts/manage-keys.sh` > option 5 |
| Espace disque | `df -h && docker system df` |
| SSL | NPM gere le renouvellement auto |

---

## Checklist complete

### DNS IONOS
- [ ] A record `@` -> IP fixe
- [ ] A record `www` -> IP fixe
- [ ] A record `monitoring` -> IP fixe
- [ ] Propagation verifiee

### Routeur + NPM
- [ ] Ports 80+443 ouverts vers machine NPM
- [ ] Proxy Host wordly.art avec custom config
- [ ] SSL Let's Encrypt obtenu
- [ ] Proxy Host monitoring.wordly.art

### Serveur Docker
- [ ] Docker installe
- [ ] Code clone dans /opt/wordly
- [ ] `bash scripts/setup-env.sh` executed
- [ ] `docker compose up -d --build` reussi
- [ ] Tous les containers healthy

### Cles API (minimum 1 provider)
- [ ] Provider de traduction configure et teste
- [ ] `bash scripts/manage-keys.sh` pour verifier

### Stripe (optionnel)
- [ ] Compte Stripe cree et active
- [ ] 3 produits + Price IDs crees
- [ ] Webhook configure (5 events)
- [ ] Cles Stripe dans le .env
- [ ] Test avec carte 4242 reussi

### Backup NAS
- [ ] NAS monte sur /mnt/nas-backups/wordly
- [ ] Backup manuel OK
- [ ] Cron programme a 3h

### Monitoring
- [ ] `docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d`
- [ ] Grafana accessible sur monitoring.wordly.art

### CI/CD
- [ ] Gitea runner installe et enregistre
- [ ] Service systemd active

### Verification finale
- [ ] https://wordly.art accessible depuis Internet
- [ ] HTTPS OK (cadenas vert)
- [ ] Inscription utilisateur OK
- [ ] Upload + traduction OK
- [ ] Page admin /admin OK
- [ ] Monitoring OK
