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

git clone <url-du-repo> notesfrais
cd notesfrais

2.2 Créer le fichier .env

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

# 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 :

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.comMicrosoft Entra IDInscriptions d'applicationsNouvelle 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 secretsNouveau 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 APIAjouter une autorisationMicrosoft GraphAutorisations 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 (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

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

# 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

# Copie locale du volume uploads
docker cp $(docker compose ps -q backend):/app/uploads ./uploads_backup

Restauration de la base de données

# 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

docker cp ./uploads_backup/. $(docker compose ps -q backend):/app/uploads/

Script de sauvegarde automatique (cron)

Créer /etc/cron.daily/notesfrais-backup :

#!/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"
chmod +x /etc/cron.daily/notesfrais-backup

8. Logs et débogage

# 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
S
Description
NotesFrais PWA — 2026-04-29 09:57
Readme 445 KiB
Languages
TypeScript 94.6%
PLpgSQL 3.4%
Dockerfile 0.7%
CSS 0.7%
HTML 0.3%
Other 0.3%