Files
notesfrais/README.md
T
2026-04-29 09:57:19 +02:00

324 lines
9.5 KiB
Markdown

# NotesFrais
PWA de gestion des notes de frais — capture photo, OCR, génération PDF, envoi par e-mail et export SharePoint.
## Stack technique
| Couche | Techno |
|--------|--------|
| Frontend | React 18 + Vite + TailwindCSS + TanStack Query v5 |
| Backend | Node.js 20 + Express + TypeScript |
| Base de données | PostgreSQL 16 |
| Stockage fichiers | Volume Docker (`/app/uploads`) |
| Authentification | JWT (access 15 min + refresh 30 j) |
| PDF | pdf-lib |
| E-mail | Nodemailer (SMTP par société) |
| SharePoint | Microsoft Graph API (client_credentials) |
| PWA / offline | vite-plugin-pwa + Workbox + IndexedDB queue |
| Déploiement | Docker multi-stage + Coolify + Traefik (HTTPS) |
---
## 1. Pré-requis
- Docker ≥ 24 et Docker Compose v2
- Un domaine pointant sur le VPS (ex. `frais.domench.fr`)
- Coolify installé sur le VPS (gère le routage HTTPS via Traefik)
- Un **App Registration Azure / Entra ID** pour Microsoft Graph (voir §5)
---
## 2. Installation
### 2.1 Cloner le dépôt
```bash
git clone <url-du-repo> notesfrais
cd notesfrais
```
### 2.2 Créer le fichier `.env`
```bash
cp .env.example .env
```
Éditer `.env` et remplir toutes les valeurs `CHANGE_ME` :
| Variable | Description | Commande de génération |
|----------|-------------|------------------------|
| `DOMAIN` | Domaine public (sans `https://`) | — |
| `DB_PASSWORD` | Mot de passe PostgreSQL | `openssl rand -hex 32` |
| `JWT_SECRET` | Secret de signature JWT | `openssl rand -hex 64` |
| `APP_SECRET` | Clé AES-256 pour chiffrement BDD | `openssl rand -hex 32` |
| `GREG_EMAIL` | E-mail du compte Greg (init uniquement) | — |
| `GREG_PASSWORD` | Mot de passe du compte Greg | — |
| `GAEL_EMAIL` | E-mail du compte Gaël (init uniquement) | — |
| `GAEL_PASSWORD` | Mot de passe du compte Gaël | — |
### 2.3 Premier démarrage
```bash
# Build et démarrage des conteneurs
docker compose up -d --build
# Vérifier que tout est sain
docker compose ps
docker compose logs backend --tail=50
```
Le script `docker-entrypoint.sh` attend automatiquement que PostgreSQL soit prêt, puis exécute la migration.
### 2.4 Créer les utilisateurs initiaux
Cette commande n'est à exécuter **qu'une seule fois**, juste après le premier démarrage :
```bash
docker compose exec backend node dist/scripts/initUsers.js
```
Une fois les comptes créés, vous pouvez supprimer les variables `GREG_*` et `GAEL_*` du `.env`.
---
## 3. Déploiement avec Coolify
1. Dans Coolify, créer un nouveau service de type **Docker Compose**.
2. Pointer sur le dépôt Git (branche `main`).
3. Dans **Variables d'environnement**, renseigner les mêmes clés que dans `.env`.
4. Dans **Domaines**, configurer `frais.domench.fr` → port `80` (Traefik gère le TLS).
5. Lancer le build ; Coolify s'occupe du certificat Let's Encrypt.
> **Astuce** : le `docker-compose.yml` expose uniquement le port 80 du frontend.
> Traefik route le trafic HTTPS → HTTP interne. Le backend n'est pas exposé directement.
---
## 4. Paramétrage in-app
Une fois connecté, aller dans **Réglages** :
### 4.1 Sociétés
Ajouter chaque société avec son nom et son adresse e-mail de contact.
### 4.2 Configuration e-mail (par société)
Renseigner les paramètres SMTP pour chaque société (hôte, port, TLS, utilisateur, mot de passe).
Le mot de passe SMTP est chiffré en AES-256-GCM avant d'être stocké en base.
### 4.3 Microsoft Graph / SharePoint
Renseigner une seule fois pour **toutes les sociétés** :
| Champ | Description |
|-------|-------------|
| Tenant ID | ID du tenant Azure (voir §5) |
| Client ID | ID de l'App Registration |
| Client Secret | Secret de l'App Registration |
| Site ID | ID du site SharePoint (voir §5.4) |
| Item ID | ID du fichier Excel (voir §5.5) |
| Nom de feuille | Nom de l'onglet Excel (ex. `App`) |
---
## 5. Configuration Microsoft Graph (Azure / Entra ID)
### 5.1 Créer l'App Registration
1. Aller sur [portal.azure.com](https://portal.azure.com) → **Microsoft Entra ID****Inscriptions d'applications****Nouvelle inscription**.
2. Nom : `NotesFrais` — Type de compte : **Locataire unique**.
3. URI de redirection : aucune (flux client_credentials).
4. **Créer**.
### 5.2 Créer un secret client
1. Dans l'app → **Certificats et secrets****Nouveau secret client**.
2. Durée recommandée : 24 mois.
3. **Copier la valeur immédiatement** (elle n'est visible qu'une fois).
### 5.3 Accorder les permissions API
1. **Autorisations API****Ajouter une autorisation****Microsoft Graph****Autorisations d'application**.
2. Ajouter :
- `Files.ReadWrite.All`
- `Sites.ReadWrite.All`
3. Cliquer **Accorder le consentement administrateur** (bouton vert) — indispensable pour les permissions d'application.
### 5.4 Obtenir le Site ID SharePoint
Dans [Graph Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer) (connecté avec un compte admin) :
```
GET https://graph.microsoft.com/v1.0/sites/<tenant>.sharepoint.com:/sites/<nom-du-site>
```
Remplacer `<tenant>` par le nom du tenant (ex. `1dotech`) et `<nom-du-site>` par le chemin du site.
Récupérer la valeur du champ `id` dans la réponse (format : `host,guid1,guid2`).
### 5.5 Obtenir l'Item ID du fichier Excel
```
GET https://graph.microsoft.com/v1.0/sites/<site-id>/drive/root/children
```
Trouver le fichier Excel dans la liste et copier son champ `id`.
> **Alternative** : naviguer dans l'arborescence avec
> `GET /sites/<site-id>/drive/root:/<chemin/vers/fichier.xlsx>`
---
## 6. Mise à jour
```bash
git pull
docker compose up -d --build
```
Le `docker-entrypoint.sh` ré-applique la migration à chaque démarrage (toutes les instructions SQL utilisent `IF NOT EXISTS` / `ON CONFLICT DO NOTHING` — elles sont idempotentes).
---
## 7. Sauvegarde et restauration
### Sauvegarde de la base de données
```bash
# Dump compressé horodaté
docker compose exec db pg_dump -U notesfrais notesfrais \
| gzip > "backup_notesfrais_$(date +%Y%m%d_%H%M%S).sql.gz"
```
### Sauvegarde des fichiers uploadés
```bash
# Copie locale du volume uploads
docker cp $(docker compose ps -q backend):/app/uploads ./uploads_backup
```
### Restauration de la base de données
```bash
# Arrêter le backend le temps de la restauration
docker compose stop backend
# Restaurer
gunzip -c backup_notesfrais_YYYYMMDD_HHMMSS.sql.gz \
| docker compose exec -T db psql -U notesfrais notesfrais
# Redémarrer
docker compose start backend
```
### Restauration des fichiers
```bash
docker cp ./uploads_backup/. $(docker compose ps -q backend):/app/uploads/
```
### Script de sauvegarde automatique (cron)
Créer `/etc/cron.daily/notesfrais-backup` :
```bash
#!/bin/bash
set -e
BACKUP_DIR=/var/backups/notesfrais
mkdir -p "$BACKUP_DIR"
# Garder 30 jours
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
# Dump BDD
cd /chemin/vers/notesfrais
docker compose exec -T db pg_dump -U notesfrais notesfrais \
| gzip > "$BACKUP_DIR/db_$(date +%Y%m%d_%H%M%S).sql.gz"
```
```bash
chmod +x /etc/cron.daily/notesfrais-backup
```
---
## 8. Logs et débogage
```bash
# Tous les services
docker compose logs -f
# Backend uniquement
docker compose logs -f backend
# Vérifier l'état des conteneurs
docker compose ps
# Accéder au shell backend
docker compose exec backend sh
# Console PostgreSQL
docker compose exec db psql -U notesfrais notesfrais
```
---
## 9. Variables d'environnement — référence complète
### Fichier `.env` (racine, pour docker-compose)
| Variable | Obligatoire | Défaut | Description |
|----------|-------------|--------|-------------|
| `DOMAIN` | Oui | `frais.domench.fr` | Domaine public |
| `DB_PASSWORD` | Oui | — | Mot de passe PostgreSQL |
| `JWT_SECRET` | Oui | — | Secret JWT (≥ 64 octets hex) |
| `APP_SECRET` | Oui | — | Clé AES-256 (32 octets hex) |
| `GREG_EMAIL` | Init seult. | — | E-mail compte Greg |
| `GREG_PASSWORD` | Init seult. | — | Mot de passe compte Greg |
| `GAEL_EMAIL` | Init seult. | — | E-mail compte Gaël |
| `GAEL_PASSWORD` | Init seult. | — | Mot de passe compte Gaël |
### Variables injectées par docker-compose (non à configurer dans `.env`)
| Variable | Valeur fixée dans docker-compose.yml |
|----------|--------------------------------------|
| `NODE_ENV` | `production` |
| `PORT` | `3001` |
| `DATABASE_URL` | construit depuis `DB_PASSWORD` |
| `FRONTEND_URL` | construit depuis `DOMAIN` |
| `UPLOADS_DIR` | `/app/uploads` |
---
## 10. Structure du projet
```
notesfrais/
├── backend/
│ ├── src/
│ │ ├── index.ts # Point d'entrée Express
│ │ ├── config.ts # Variables d'env
│ │ ├── db.ts # Pool PostgreSQL
│ │ ├── middleware/ # Auth JWT, error handler
│ │ ├── routes/ # auth, invoices, companies, settings, guests
│ │ ├── services/ # pdf, email, sharepoint
│ │ ├── migrations/ # 001_init.sql
│ │ └── scripts/ # migrate.ts, initUsers.ts
│ ├── Dockerfile
│ ├── docker-entrypoint.sh
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── pages/ # Login, NewInvoice, MyInvoices, Settings…
│ │ ├── components/ # Layout, modaux…
│ │ ├── hooks/ # useOfflineQueue, useOnline…
│ │ ├── utils/ # offlineQueue (IndexedDB), api…
│ │ └── types/ # index.ts
│ ├── Dockerfile
│ ├── nginx.conf
│ └── package.json
├── docker-compose.yml
├── .env.example
└── README.md
```