This commit is contained in:
alma 2026-01-12 10:37:21 +01:00
parent c8364191df
commit 0358c87af3
21 changed files with 2238 additions and 417 deletions

233
.env.example Normal file
View File

@ -0,0 +1,233 @@
# ============================================
# NEAH - Variables d'environnement
# ============================================
# Ce fichier liste toutes les variables d'environnement nécessaires pour Neah.
# Copiez ce fichier vers .env.local (développement) ou configurez-les dans votre plateforme de déploiement (Vercel, Docker, etc.)
#
# IMPORTANT: Ne commitez JAMAIS de fichiers .env avec des valeurs réelles !
# ============================================
# ENVIRONNEMENT & BASE
# ============================================
NODE_ENV=development
# production | development | test
NEXTAUTH_URL=http://localhost:3000
# URL publique de l'application (utilisée par NextAuth pour les callbacks)
# En production: https://votre-domaine.com
# En développement: http://localhost:3000
NEXTAUTH_SECRET=
# Secret pour signer les cookies NextAuth (générer avec: openssl rand -base64 32)
# OBLIGATOIRE en production
# ============================================
# BASE DE DONNÉES
# ============================================
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/calendar_db
# URL de connexion PostgreSQL (format: postgresql://user:password@host:port/database)
# Pour production auto-hébergée: postgresql://user:password@votre-serveur:5432/calendar_db
# Pour SSL: postgresql://user:password@host:5432/db?sslmode=require
NEWSDB_URL=postgresql://postgres:postgres@localhost:5432/news_db
# Base de données séparée pour les actualités (si utilisée)
NEWS_API_URL=http://localhost:3000/api/news
# URL de l'API des actualités
# ============================================
# KEYCLOAK - AUTHENTIFICATION
# ============================================
KEYCLOAK_BASE_URL=https://keycloak.example.com
# URL de base de Keycloak (sans /realms/...)
KEYCLOAK_REALM=neah
# Nom du realm Keycloak
KEYCLOAK_CLIENT_ID=neah-app
# ID du client Keycloak
KEYCLOAK_CLIENT_SECRET=
# Secret du client Keycloak (OBLIGATOIRE)
KEYCLOAK_ISSUER=https://keycloak.example.com/realms/neah
# URL complète de l'issuer Keycloak (format: {BASE_URL}/realms/{REALM})
# Alternative à KEYCLOAK_BASE_URL + KEYCLOAK_REALM
NEXT_PUBLIC_KEYCLOAK_ISSUER=https://keycloak.example.com/realms/neah
# Issuer Keycloak accessible côté client (pour les redirections SSO)
# Optionnel: Authentification admin Keycloak (si nécessaire pour certaines opérations)
KEYCLOAK_ADMIN_USERNAME=admin
KEYCLOAK_ADMIN_PASSWORD=
# ============================================
# REDIS - CACHE & SESSIONS
# ============================================
# Option 1: URL complète Redis
REDIS_URL=redis://:password@localhost:6379
# Format: redis://[:password@]host[:port][/database]
# Option 2: Paramètres séparés (priorité si REDIS_URL n'est pas défini)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
# Mot de passe Redis (laisser vide si pas de mot de passe)
REDIS_ENCRYPTION_KEY=
# Clé de chiffrement pour les données sensibles dans Redis (générer avec: openssl rand -base64 32)
# Par défaut: 'default-encryption-key-change-in-production' (CHANGER EN PRODUCTION !)
# ============================================
# AWS S3 / MINIO - STOCKAGE FICHIERS
# ============================================
# Configuration S3 (pour MinIO ou AWS S3 réel)
S3_BUCKET=missions
# Nom du bucket S3
# Si utilisation de MinIO (auto-hébergé)
MINIO_S3_UPLOAD_BUCKET_URL=http://localhost:9000
# URL de l'endpoint MinIO
MINIO_AWS_REGION=us-east-1
# Région AWS (même pour MinIO)
MINIO_AWS_S3_UPLOAD_BUCKET_NAME=missions
# Nom du bucket pour les uploads
MINIO_ACCESS_KEY=minioadmin
# Clé d'accès MinIO
MINIO_SECRET_KEY=minioadmin
# Clé secrète MinIO
# Si utilisation d'AWS S3 réel (remplacer les variables MINIO_*)
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
AWS_S3_BUCKET=missions
# ============================================
# LEANTIME - GESTION DE PROJET
# ============================================
LEANTIME_API_URL=https://leantime.example.com
# URL de base de l'API Leantime
LEANTIME_TOKEN=
# Token d'API Leantime (OBLIGATOIRE si intégration activée)
# ============================================
# ROCKETCHAT - MESSAGERIE
# ============================================
ROCKET_CHAT_TOKEN=
# Token d'authentification admin RocketChat (OBLIGATOIRE si intégration activée)
ROCKET_CHAT_USER_ID=
# ID de l'utilisateur admin RocketChat (OBLIGATOIRE si intégration activée)
NEXT_PUBLIC_IFRAME_PAROLE_URL=https://rocketchat.example.com/channel/general
# URL publique de RocketChat (pour les iframes)
# ============================================
# N8N - AUTOMATISATION
# ============================================
N8N_API_KEY=
# Clé API N8N pour authentifier les webhooks (OBLIGATOIRE si intégration activée)
N8N_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-created
# URL du webhook N8N pour la création de missions
N8N_ROLLBACK_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-rollback
# URL du webhook N8N pour le rollback de missions
N8N_DELETE_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-delete
# URL du webhook N8N pour la suppression de missions
NEXT_PUBLIC_API_URL=https://api.slm-lab.net/api
# URL publique de l'API Neah (utilisée par N8N pour les callbacks)
# ============================================
# DOLIBARR - ERP
# ============================================
DOLIBARR_API_URL=https://dolibarr.example.com
# URL de base de l'API Dolibarr
DOLIBARR_API_KEY=
# Clé API Dolibarr (OBLIGATOIRE si intégration activée)
# ============================================
# MICROSOFT OAUTH (Optionnel)
# ============================================
MICROSOFT_TENANT_ID=common
# ID du tenant Microsoft (ou 'common' pour multi-tenant)
MICROSOFT_CLIENT_ID=
# ID du client Microsoft OAuth
MICROSOFT_CLIENT_SECRET=
# Secret du client Microsoft OAuth
MICROSOFT_REDIRECT_URI=http://localhost:3000/api/auth/callback/microsoft
# URI de redirection OAuth Microsoft
# ============================================
# IFRAMES - INTÉGRATIONS EXTERNES
# ============================================
# URLs des différentes applications intégrées via iframe
NEXT_PUBLIC_IFRAME_CARNET_URL=https://carnet.example.com
NEXT_PUBLIC_IFRAME_DRIVE_URL=https://drive.example.com
NEXT_PUBLIC_IFRAME_LEARN_URL=https://learn.example.com
NEXT_PUBLIC_IFRAME_PAROLE_URL=https://rocketchat.example.com/channel/general
NEXT_PUBLIC_IFRAME_MISSIONSBOARD_URL=https://missionsboard.example.com
NEXT_PUBLIC_IFRAME_CHAPTER_URL=https://chapter.example.com
NEXT_PUBLIC_IFRAME_AGILITY_URL=https://agility.example.com
NEXT_PUBLIC_IFRAME_ARTLAB_URL=https://artlab.example.com
NEXT_PUBLIC_IFRAME_GITE_URL=https://gite.example.com
NEXT_PUBLIC_IFRAME_CALCULATION_URL=https://calculation.example.com
NEXT_PUBLIC_IFRAME_MEDIATIONS_URL=https://mediations.example.com
NEXT_PUBLIC_IFRAME_LIVRE_URL=https://livre.example.com
NEXT_PUBLIC_IFRAME_SHOWCASE_URL=https://showcase.example.com
NEXT_PUBLIC_IFRAME_RADIO_URL=https://radio.example.com
NEXT_PUBLIC_IFRAME_OBSERVATORY_URL=https://observatory.example.com
NEXT_PUBLIC_IFRAME_TIMETRACKER_URL=https://timetracker.example.com
NEXT_PUBLIC_IFRAME_THEMESSAGE_URL=https://themessage.example.com
NEXT_PUBLIC_IFRAME_MISSIONVIEW_URL=https://missionview.example.com
NEXT_PUBLIC_IFRAME_CONFERENCE_URL=https://vision.slm-lab.net/MonMeeting
# ============================================
# AUTRES SERVICES
# ============================================
NEXT_PUBLIC_APP_URL=http://localhost:3000
# URL publique de l'application (pour les scripts et callbacks)
EQUIPES_API_URL=https://equipes-api.example.com/users
# URL de l'API Equipes (si utilisée)
EQUIPES_API_TOKEN=
# Token d'authentification pour l'API Equipes
# ============================================
# NOTES IMPORTANTES
# ============================================
# 1. Les variables préfixées par NEXT_PUBLIC_ sont exposées côté client (navigateur)
# Ne jamais y mettre de secrets !
#
# 2. Pour la production sur Vercel:
# - Configurez toutes ces variables dans: Project Settings > Environment Variables
# - Assurez-vous que DATABASE_URL pointe vers votre PostgreSQL auto-hébergé
# - Vérifiez que NEXTAUTH_URL correspond à votre domaine Vercel
#
# 3. Pour la production avec Docker:
# - Ajoutez ces variables dans docker-compose.yml ou un fichier .env
# - Utilisez des secrets Docker pour les valeurs sensibles
#
# 4. Génération de secrets:
# - NEXTAUTH_SECRET: openssl rand -base64 32
# - REDIS_ENCRYPTION_KEY: openssl rand -base64 32
# - KEYCLOAK_CLIENT_SECRET: généré dans Keycloak Admin Console
#
# 5. Variables optionnelles:
# - Les variables d'intégration (LEANTIME, ROCKETCHAT, DOLIBARR, N8N) peuvent être omises
# si ces services ne sont pas utilisés
# - Les variables IFRAME peuvent avoir des valeurs par défaut ou être omises
# si les fonctionnalités correspondantes ne sont pas nécessaires

65
Dockerfile.prod Normal file
View File

@ -0,0 +1,65 @@
# Dockerfile optimisé pour la production Neah
# Utilisé uniquement si vous déployez l'application complète avec Docker
# Pour Vercel, ce fichier n'est pas nécessaire (Vercel build automatiquement)
FROM node:22-alpine AS base
# Installer les dépendances nécessaires pour Prisma
RUN apk add --no-cache libc6-compat openssl
WORKDIR /app
# ============================================
# Étape 1: Dépendances
# ============================================
FROM base AS deps
COPY package.json package-lock.json* ./
RUN npm ci
# ============================================
# Étape 2: Builder
# ============================================
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Générer le client Prisma
RUN npx prisma generate
# Build Next.js (sans migrations - elles seront appliquées séparément)
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# ============================================
# Étape 3: Runner (image finale)
# ============================================
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Créer un utilisateur non-root pour la sécurité
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copier les fichiers nécessaires depuis le builder
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
# Changer les permissions
RUN chown -R nextjs:nodejs /app
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Note: Les migrations Prisma doivent être appliquées séparément avant le démarrage
# Utilisez: docker exec <container> npx prisma migrate deploy
CMD ["node", "server.js"]

66
README.DEPLOYMENT.md Normal file
View File

@ -0,0 +1,66 @@
# Guide de Déploiement - Neah
Ce document fournit un aperçu rapide des ressources de déploiement disponibles pour Neah.
## 📚 Documentation
- **[DEPLOYMENT.md](docs/DEPLOYMENT.md)**: Guide complet de déploiement étape par étape
- **[RUNBOOK.md](docs/RUNBOOK.md)**: Procédures opérationnelles (déploiement, incidents, rollback)
- **[OBSERVABILITY.md](docs/OBSERVABILITY.md)**: Stratégie de monitoring et observabilité
## 🚀 Déploiement rapide
### Prérequis
1. Compte Vercel configuré
2. Serveur PostgreSQL auto-hébergé
3. Variables d'environnement configurées (voir `.env.example`)
### Étapes
1. **Configurer les variables d'environnement**
Copiez `.env.example` et remplissez les valeurs pour la production.
2. **Vérifier la configuration**
```bash
./scripts/verify-vercel-config.sh
```
3. **Appliquer les migrations Prisma**
```bash
export DATABASE_URL="postgresql://..."
./scripts/migrate-prod.sh
```
4. **Déployer sur Vercel**
```bash
git push origin main
# Vercel déploiera automatiquement
```
## 📁 Fichiers importants
- `.env.example`: Liste complète des variables d'environnement
- `docker-compose.prod.yml`: Configuration Docker pour PostgreSQL/Redis en production
- `vercel.json`: Configuration Vercel
- `scripts/migrate-prod.sh`: Script de migration Prisma pour la production
- `scripts/verify-vercel-config.sh`: Script de vérification de configuration
## 🔍 Vérifications post-déploiement
1. Health check: `GET /api/health`
2. Vérifier les logs Vercel
3. Tester l'authentification
4. Vérifier les fonctionnalités critiques
## 🆘 En cas de problème
Consultez [RUNBOOK.md](docs/RUNBOOK.md) pour les procédures d'incident et de rollback.
## 📞 Support
Pour toute question sur le déploiement, contactez l'équipe Neah.

78
app/api/health/route.ts Normal file
View File

@ -0,0 +1,78 @@
import { NextResponse } from 'next/server';
import { getRedisClient } from '@/lib/redis';
import { prisma } from '@/lib/prisma';
import { logger } from '@/lib/logger';
/**
* Health check endpoint
*
* Vérifie la santé de l'application et de ses dépendances:
* - Base de données PostgreSQL
* - Redis (si configuré)
*
* Usage:
* GET /api/health
*
* Réponses:
* 200: Tous les services sont opérationnels
* 503: Un ou plusieurs services sont indisponibles
*/
export async function GET() {
const startTime = Date.now();
const checks: Record<string, { status: string; message?: string; latency?: number }> = {};
let overallStatus: 'ok' | 'degraded' | 'down' = 'ok';
// Vérifier PostgreSQL
try {
const dbStart = Date.now();
await prisma.$queryRaw`SELECT 1`;
const dbLatency = Date.now() - dbStart;
checks.database = {
status: 'ok',
latency: dbLatency,
};
} catch (error) {
logger.error('[HEALTH] Database check failed', {
error: error instanceof Error ? error.message : String(error),
});
checks.database = {
status: 'error',
message: error instanceof Error ? error.message : 'Unknown error',
};
overallStatus = 'degraded';
}
// Vérifier Redis (optionnel)
try {
const redis = getRedisClient();
const redisStart = Date.now();
await redis.ping();
const redisLatency = Date.now() - redisStart;
checks.redis = {
status: 'ok',
latency: redisLatency,
};
} catch (error) {
logger.warn('[HEALTH] Redis check failed', {
error: error instanceof Error ? error.message : String(error),
});
// Redis n'est pas critique, donc on ne change pas le statut global
checks.redis = {
status: 'error',
message: error instanceof Error ? error.message : 'Unknown error',
};
}
const totalLatency = Date.now() - startTime;
const response = {
status: overallStatus,
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks,
latency: totalLatency,
};
const statusCode = overallStatus === 'ok' ? 200 : 503;
return NextResponse.json(response, { status: statusCode });
}

View File

@ -1,69 +0,0 @@
"use client";
import { useEffect, useState } from 'react';
import { Button } from "@/components/ui/button";
import { X, Minus, Square } from 'lucide-react';
// We're now using the global type declaration from types/electron.d.ts
export function WindowControls() {
const [isElectron, setIsElectron] = useState(false);
const [isMaximized, setIsMaximized] = useState(false);
useEffect(() => {
// Check if we're running in Electron
if (window.electron) {
setIsElectron(true);
// Set up listeners for window state
const handleMaximize = () => setIsMaximized(true);
const handleUnmaximize = () => setIsMaximized(false);
window.electron.windowState?.onMaximized(handleMaximize);
window.electron.windowState?.onUnmaximized(handleUnmaximize);
// Clean up listeners on unmount
return () => {
if (window.electron && window.electron.windowState) {
window.electron.windowState.removeMaximizedListener();
window.electron.windowState.removeUnmaximizedListener();
}
};
}
}, []);
// If not in Electron, don't render anything
if (!isElectron) return null;
return (
<div className="flex items-center">
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 text-white/70 hover:text-white hover:bg-transparent"
onClick={() => window.electron?.windowControl.minimize()}
aria-label="Minimize"
>
<Minus className="h-3 w-3" />
</Button>
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 text-white/70 hover:text-white hover:bg-transparent"
onClick={() => window.electron?.windowControl.maximize()}
aria-label="Maximize"
>
<Square className="h-3 w-3" />
</Button>
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 text-white/70 hover:text-white hover:bg-red-500"
onClick={() => window.electron?.windowControl.close()}
aria-label="Close"
>
<X className="h-3 w-3" />
</Button>
</div>
);
}

View File

@ -1,81 +0,0 @@
'use client';
import { useEffect, useState } from 'react';
import { Minus, Square, X } from 'lucide-react';
// Import types from our electron.d.ts file
declare global {
interface Window {
electron?: {
windowControl: {
minimize: () => Promise<void>;
maximize: () => Promise<void>;
close: () => Promise<void>;
};
windowState: {
onMaximized: (callback: () => void) => void;
onUnmaximized: (callback: () => void) => void;
removeMaximizedListener: () => void;
removeUnmaximizedListener: () => void;
};
appInfo: {
isElectron: boolean;
version: string;
};
};
}
}
export function WindowControls() {
const [isElectron, setIsElectron] = useState(false);
const [isMaximized, setIsMaximized] = useState(false);
useEffect(() => {
// Check if running in Electron
if (typeof window !== 'undefined' && window.electron) {
setIsElectron(true);
// Set up event listeners for window state
const maximizedHandler = () => setIsMaximized(true);
const unmaximizedHandler = () => setIsMaximized(false);
window.electron.windowState.onMaximized(maximizedHandler);
window.electron.windowState.onUnmaximized(unmaximizedHandler);
return () => {
window.electron?.windowState.removeMaximizedListener();
window.electron?.windowState.removeUnmaximizedListener();
};
}
}, []);
if (!isElectron) return null;
return (
<div className="flex -mr-2 items-center">
<button
onClick={() => window.electron?.windowControl.minimize()}
className="px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-700"
aria-label="Minimize"
>
<Minus size={16} />
</button>
<button
onClick={() => window.electron?.windowControl.maximize()}
className="px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-700"
aria-label={isMaximized ? "Restore" : "Maximize"}
>
<Square size={16} />
</button>
<button
onClick={() => window.electron?.windowControl.close()}
className="px-4 py-2 hover:bg-red-500 dark:hover:bg-red-600"
aria-label="Close"
>
<X size={16} className="hover:text-white" />
</button>
</div>
);
}

View File

@ -39,7 +39,6 @@ import {
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { NotificationBadge } from "./notification-badge"; import { NotificationBadge } from "./notification-badge";
import { NotesDialog } from "./notes-dialog"; import { NotesDialog } from "./notes-dialog";
import { WindowControls } from "@/components/electron/WindowControls";
import { MainNavTime } from "./main-nav-time"; import { MainNavTime } from "./main-nav-time";
const requestNotificationPermission = async () => { const requestNotificationPermission = async () => {
@ -285,9 +284,6 @@ export function MainNav() {
{/* Right side */} {/* Right side */}
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
{/* Electron window controls - only shows in electron environment */}
<WindowControls />
<NotificationBadge /> <NotificationBadge />
{status === "authenticated" && session?.user ? ( {status === "authenticated" && session?.user ? (

179
docker-compose.prod.yml Normal file
View File

@ -0,0 +1,179 @@
# ============================================
# Docker Compose pour Production Neah
# ============================================
# Ce fichier est optimisé pour un déploiement en production.
# Il configure PostgreSQL et Redis pour être utilisés avec Vercel (Next.js déployé séparément).
#
# Usage:
# docker-compose -f docker-compose.prod.yml up -d
#
# IMPORTANT:
# - Modifiez les mots de passe par défaut avant le déploiement !
# - Configurez les volumes pour la persistance des données
# - Ajustez les ports selon votre infrastructure réseau
version: '3.8'
services:
# ============================================
# PostgreSQL - Base de données principale
# ============================================
db:
image: postgres:15-alpine
container_name: neah-postgres-prod
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-neah_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-CHANGE_ME_IN_PRODUCTION}
POSTGRES_DB: ${POSTGRES_DB:-calendar_db}
# Configuration pour la production
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
ports:
# Exposer uniquement sur localhost pour la sécurité (ou utilisez un réseau Docker)
- "127.0.0.1:5432:5432"
# Pour accès externe depuis Vercel, utilisez plutôt un tunnel SSH ou VPN
volumes:
# Volume nommé pour la persistance (géré par Docker)
- postgres_data:/var/lib/postgresql/data
# Optionnel: sauvegardes automatiques
# - ./backups:/backups
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-neah_user}"]
interval: 10s
timeout: 5s
retries: 5
# Configuration réseau (optionnel: créer un réseau isolé)
# networks:
# - neah_network
# Sécurité: limiter les ressources
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
# ============================================
# Redis - Cache et sessions
# ============================================
redis:
image: redis:7-alpine
container_name: neah-redis-prod
restart: unless-stopped
command: >
redis-server
--requirepass ${REDIS_PASSWORD:-CHANGE_ME_IN_PRODUCTION}
--appendonly yes
--maxmemory 512mb
--maxmemory-policy allkeys-lru
ports:
# Exposer uniquement sur localhost pour la sécurité
- "127.0.0.1:6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 3s
retries: 5
# Sécurité: limiter les ressources
deploy:
resources:
limits:
cpus: '1'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
# ============================================
# MinIO - Stockage S3-compatible (optionnel)
# ============================================
# Décommentez si vous utilisez MinIO pour le stockage de fichiers
# minio:
# image: minio/minio:latest
# container_name: neah-minio-prod
# restart: unless-stopped
# environment:
# MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
# MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-CHANGE_ME_IN_PRODUCTION}
# ports:
# - "127.0.0.1:9000:9000" # API
# - "127.0.0.1:9001:9001" # Console
# volumes:
# - minio_data:/data
# command: server /data --console-address ":9001"
# healthcheck:
# test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
# interval: 30s
# timeout: 20s
# retries: 3
# deploy:
# resources:
# limits:
# cpus: '1'
# memory: 1G
volumes:
# Volume pour PostgreSQL - persistance des données
postgres_data:
driver: local
# Optionnel: utiliser un driver de volume externe pour les sauvegardes
# driver_opts:
# type: nfs
# o: addr=nfs-server.example.com,nolock,soft,rw
# device: ":/path/to/nfs/share"
# Volume pour Redis - persistance AOF
redis_data:
driver: local
# Volume pour MinIO (si activé)
# minio_data:
# driver: local
# ============================================
# Réseaux (optionnel)
# ============================================
# Décommentez pour isoler les services dans un réseau Docker
# networks:
# neah_network:
# driver: bridge
# ipam:
# config:
# - subnet: 172.20.0.0/16
# ============================================
# NOTES IMPORTANTES POUR LA PRODUCTION
# ============================================
#
# 1. SÉCURITÉ:
# - Changez TOUS les mots de passe par défaut
# - Utilisez des secrets Docker ou un gestionnaire de secrets (HashiCorp Vault, etc.)
# - Limitez l'exposition des ports (utilisez 127.0.0.1 au lieu de 0.0.0.0)
# - Configurez un firewall pour limiter l'accès aux ports exposés
# - Activez SSL/TLS pour PostgreSQL si accessible depuis l'extérieur
#
# 2. CONNEXION DEPUIS VERCEL:
# - Option A: Tunnel SSH (recommandé)
# ssh -L 5432:localhost:5432 user@your-server
# - Option B: VPN (si disponible)
# - Option C: Exposer PostgreSQL avec SSL (moins sécurisé, nécessite configuration SSL)
#
# 3. SAUVEGARDES:
# - Configurez des sauvegardes automatiques de PostgreSQL
# - Sauvegardez régulièrement les volumes Docker
# - Testez la restauration des sauvegardes
#
# 4. MONITORING:
# - Surveillez l'utilisation des ressources (CPU, mémoire, disque)
# - Configurez des alertes pour les problèmes de santé
# - Utilisez des outils comme Prometheus + Grafana
#
# 5. MIGRATIONS PRISMA:
# - Exécutez les migrations avant chaque déploiement:
# DATABASE_URL="postgresql://..." npx prisma migrate deploy
# - Testez les migrations en staging avant la production
# - Gardez un plan de rollback pour chaque migration

369
docs/DEPLOYMENT.md Normal file
View File

@ -0,0 +1,369 @@
# Guide de Déploiement en Production - Neah
Ce document décrit les procédures pour déployer Neah en production avec Vercel (Next.js) et PostgreSQL auto-hébergé.
## Architecture de Production
```
┌─────────────────┐
│ Utilisateurs │
└────────┬────────┘
┌─────────────────┐
│ Vercel (Next.js)│
│ - Frontend │
│ - API Routes │
└────────┬────────┘
├─────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ PostgreSQL │ │ Redis │
│ (Auto-hébergé) │ │ (Auto-hébergé) │
└─────────────────┘ └─────────────────┘
```
## Prérequis
- Un compte Vercel
- Un serveur pour PostgreSQL et Redis (VPS, VM, ou conteneur Docker)
- Accès SSH au serveur de base de données
- Node.js 22+ installé localement (pour les migrations)
## Étape 1: Configuration PostgreSQL en Production
### 1.1 Déployer PostgreSQL avec Docker Compose
Sur votre serveur de production:
```bash
# Copier le fichier docker-compose.prod.yml
scp docker-compose.prod.yml user@your-server:/opt/neah/
# Se connecter au serveur
ssh user@your-server
# Créer un fichier .env pour les secrets
cd /opt/neah
cat > .env << EOF
POSTGRES_USER=neah_prod_user
POSTGRES_PASSWORD=$(openssl rand -base64 32)
POSTGRES_DB=calendar_db
REDIS_PASSWORD=$(openssl rand -base64 32)
EOF
# Démarrer les services
docker-compose -f docker-compose.prod.yml up -d
# Vérifier que les services sont en cours d'exécution
docker-compose -f docker-compose.prod.yml ps
```
### 1.2 Configurer l'accès réseau
**Option A: Tunnel SSH (Recommandé pour Vercel)**
Depuis votre machine locale ou un serveur bastion:
```bash
# Créer un tunnel SSH vers PostgreSQL
ssh -L 5432:localhost:5432 -N user@your-server
# Dans un autre terminal, tester la connexion
psql postgresql://neah_prod_user:password@localhost:5432/calendar_db
```
**Option B: Exposer PostgreSQL avec SSL**
Modifiez `docker-compose.prod.yml` pour activer SSL:
```yaml
db:
environment:
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
command: >
postgres
-c ssl=on
-c ssl_cert_file=/var/lib/postgresql/server.crt
-c ssl_key_file=/var/lib/postgresql/server.key
volumes:
- postgres_data:/var/lib/postgresql/data
- ./ssl:/var/lib/postgresql
```
Puis exposez le port avec un firewall configuré pour n'accepter que les IPs Vercel.
### 1.3 Créer la base de données et appliquer les migrations
```bash
# Se connecter au conteneur PostgreSQL
docker exec -it neah-postgres-prod psql -U neah_prod_user -d calendar_db
# Ou depuis l'extérieur (si accessible)
export DATABASE_URL="postgresql://neah_prod_user:password@your-server:5432/calendar_db"
npx prisma migrate deploy
```
## Étape 2: Configuration Vercel
### 2.1 Créer un projet Vercel
1. Connectez-vous à [Vercel](https://vercel.com)
2. Importez votre repository GitHub/GitLab
3. Configurez le projet:
- **Framework Preset**: Next.js
- **Build Command**: `npm run build`
- **Output Directory**: `.next` (par défaut)
- **Install Command**: `npm ci`
### 2.2 Configurer les variables d'environnement
Dans Vercel Dashboard → Project Settings → Environment Variables, ajoutez:
#### Variables obligatoires
```env
# Environnement
NODE_ENV=production
NEXTAUTH_URL=https://votre-domaine.vercel.app
NEXTAUTH_SECRET=<généré avec: openssl rand -base64 32>
# Base de données (via tunnel SSH ou URL publique avec SSL)
DATABASE_URL=postgresql://user:password@host:5432/calendar_db?sslmode=require
# Keycloak
KEYCLOAK_BASE_URL=https://keycloak.example.com
KEYCLOAK_REALM=neah
KEYCLOAK_CLIENT_ID=neah-app
KEYCLOAK_CLIENT_SECRET=<secret depuis Keycloak>
KEYCLOAK_ISSUER=https://keycloak.example.com/realms/neah
NEXT_PUBLIC_KEYCLOAK_ISSUER=https://keycloak.example.com/realms/neah
# Redis (si accessible depuis Vercel)
REDIS_URL=redis://:password@your-server:6379
# OU
REDIS_HOST=your-server
REDIS_PORT=6379
REDIS_PASSWORD=<password>
REDIS_ENCRYPTION_KEY=<généré avec: openssl rand -base64 32>
```
#### Variables optionnelles (selon vos intégrations)
```env
# Leantime
LEANTIME_API_URL=https://leantime.example.com
LEANTIME_TOKEN=<token>
# RocketChat
ROCKET_CHAT_TOKEN=<token>
ROCKET_CHAT_USER_ID=<user-id>
NEXT_PUBLIC_IFRAME_PAROLE_URL=https://rocketchat.example.com/channel/general
# N8N
N8N_API_KEY=<api-key>
N8N_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-created
N8N_ROLLBACK_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-rollback
N8N_DELETE_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-delete
NEXT_PUBLIC_API_URL=https://api.slm-lab.net/api
# Dolibarr
DOLIBARR_API_URL=https://dolibarr.example.com
DOLIBARR_API_KEY=<api-key>
# S3 / MinIO
S3_BUCKET=missions
MINIO_S3_UPLOAD_BUCKET_URL=https://dome-api.slm-lab.net
MINIO_AWS_REGION=us-east-1
MINIO_AWS_S3_UPLOAD_BUCKET_NAME=missions
MINIO_ACCESS_KEY=<access-key>
MINIO_SECRET_KEY=<secret-key>
# Iframes (selon vos besoins)
NEXT_PUBLIC_IFRAME_CARNET_URL=https://carnet.example.com
NEXT_PUBLIC_IFRAME_DRIVE_URL=https://drive.example.com
# ... (voir .env.example pour la liste complète)
```
### 2.3 Configurer le domaine personnalisé (optionnel)
1. Dans Vercel Dashboard → Settings → Domains
2. Ajoutez votre domaine
3. Configurez les enregistrements DNS selon les instructions Vercel
## Étape 3: Migrations Prisma en Production
### 3.1 Préparer les migrations
```bash
# Vérifier l'état des migrations
npx prisma migrate status
# Créer une nouvelle migration (si nécessaire)
npx prisma migrate dev --name nom_de_la_migration
```
### 3.2 Appliquer les migrations en production
**Méthode 1: Via Vercel Build Hook (Recommandé)**
Créez un script de migration dans `package.json`:
```json
{
"scripts": {
"migrate:deploy": "prisma migrate deploy",
"postbuild": "npm run migrate:deploy"
}
}
```
**Méthode 2: Manuellement avant chaque déploiement**
```bash
# Depuis votre machine locale (avec tunnel SSH actif)
export DATABASE_URL="postgresql://user:password@localhost:5432/calendar_db"
npx prisma migrate deploy
```
**Méthode 3: Via GitHub Actions (CI/CD)**
Créez `.github/workflows/migrate.yml`:
```yaml
name: Database Migrations
on:
push:
branches: [main]
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '22'
- run: npm ci
- run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
```
## Étape 4: Vérification et Tests
### 4.1 Vérifier la connexion à la base de données
```bash
# Tester la connexion PostgreSQL
psql $DATABASE_URL -c "SELECT version();"
# Vérifier les tables Prisma
npx prisma db pull
```
### 4.2 Vérifier les services externes
- Testez l'authentification Keycloak
- Vérifiez la connexion Redis (si utilisée)
- Testez les intégrations (Leantime, RocketChat, etc.)
### 4.3 Tests de bout en bout
1. Accédez à votre application Vercel
2. Testez la connexion
3. Testez les fonctionnalités critiques:
- Création de compte
- Authentification
- Création de mission
- Upload de fichiers
- Notifications
## Étape 5: Monitoring et Maintenance
### 5.1 Logs Vercel
- Consultez les logs dans Vercel Dashboard → Deployments → [Déploiement] → Logs
- Configurez des alertes pour les erreurs
### 5.2 Monitoring PostgreSQL
```bash
# Vérifier l'état de PostgreSQL
docker exec neah-postgres-prod pg_isready
# Vérifier l'utilisation des ressources
docker stats neah-postgres-prod
# Vérifier les connexions actives
docker exec neah-postgres-prod psql -U neah_prod_user -d calendar_db -c "SELECT count(*) FROM pg_stat_activity;"
```
### 5.3 Sauvegardes
**Sauvegarde PostgreSQL:**
```bash
# Sauvegarde complète
docker exec neah-postgres-prod pg_dump -U neah_prod_user calendar_db > backup_$(date +%Y%m%d).sql
# Sauvegarde avec compression
docker exec neah-postgres-prod pg_dump -U neah_prod_user calendar_db | gzip > backup_$(date +%Y%m%d).sql.gz
```
**Restauration:**
```bash
# Restaurer depuis une sauvegarde
cat backup_20240112.sql | docker exec -i neah-postgres-prod psql -U neah_prod_user calendar_db
```
## Procédures de Rollback
### Rollback Vercel
1. Dans Vercel Dashboard → Deployments
2. Trouvez le déploiement précédent
3. Cliquez sur "..." → "Promote to Production"
### Rollback de migration Prisma
```bash
# Lister les migrations
npx prisma migrate status
# Rollback manuel (si nécessaire)
# ATTENTION: Testez d'abord en staging !
psql $DATABASE_URL -f prisma/migrations/[migration_to_rollback]/migration.sql
```
## Dépannage
### Problème: Connexion PostgreSQL échoue depuis Vercel
- Vérifiez que le tunnel SSH est actif (si utilisé)
- Vérifiez les credentials dans Vercel
- Vérifiez les règles de firewall
- Testez la connexion depuis votre machine locale
### Problème: Migrations échouent
- Vérifiez que `DATABASE_URL` est correcte
- Vérifiez les permissions de l'utilisateur PostgreSQL
- Consultez les logs: `npx prisma migrate deploy --verbose`
### Problème: Erreurs NextAuth
- Vérifiez que `NEXTAUTH_URL` correspond à votre domaine Vercel
- Vérifiez que `NEXTAUTH_SECRET` est défini
- Vérifiez la configuration Keycloak
## Ressources
- [Documentation Vercel](https://vercel.com/docs)
- [Documentation Prisma](https://www.prisma.io/docs)
- [Documentation NextAuth](https://next-auth.js.org)
- [Documentation Docker Compose](https://docs.docker.com/compose/)

369
docs/OBSERVABILITY.md Normal file
View File

@ -0,0 +1,369 @@
# Observabilité et Monitoring - Neah
Ce document décrit la stratégie d'observabilité pour Neah en production.
## Vue d'ensemble
L'observabilité comprend trois piliers:
- **Logs**: Enregistrement des événements et erreurs
- **Métriques**: Mesures de performance et santé du système
- **Traces**: Suivi des requêtes à travers le système
## 1. Logs
### 1.1 Logs Vercel
Vercel fournit des logs intégrés pour chaque déploiement:
**Accès:**
- Dashboard Vercel → Project → Deployments → [Déploiement] → Logs
- Ou via CLI: `vercel logs [deployment-url]`
**Types de logs:**
- Build logs: Erreurs de compilation
- Runtime logs: Erreurs d'exécution et logs applicatifs
- Edge logs: Logs des fonctions Edge
**Configuration:**
Les logs sont automatiquement collectés. Pour améliorer la visibilité:
```typescript
// lib/logger.ts (déjà présent dans le projet)
import { logger } from '@/lib/logger';
// Utilisation
logger.info('User logged in', { userId: user.id });
logger.error('Database connection failed', { error: error.message });
logger.warn('Rate limit approaching', { requests: count });
```
### 1.2 Logs PostgreSQL
**Via Docker:**
```bash
# Voir les logs PostgreSQL
docker logs neah-postgres-prod -f
# Logs avec timestamps
docker logs neah-postgres-prod --timestamps -f
```
**Configuration PostgreSQL pour les logs:**
Modifiez `docker-compose.prod.yml`:
```yaml
db:
environment:
POSTGRES_LOG_STATEMENT: "all" # ou "ddl", "mod", "none"
POSTGRES_LOG_DESTINATION: "stderr"
POSTGRES_LOG_TIMESTAMP: "on"
```
### 1.3 Centralisation des logs (Optionnel)
**Option A: Logtail (Recommandé pour Vercel)**
1. Créez un compte sur [Logtail](https://logtail.com)
2. Ajoutez l'intégration Vercel
3. Les logs Vercel seront automatiquement envoyés à Logtail
**Option B: Papertrail**
1. Créez un compte sur [Papertrail](https://papertrailapp.com)
2. Configurez un endpoint syslog
3. Redirigez les logs Docker vers Papertrail:
```yaml
# docker-compose.prod.yml
logging:
driver: "syslog"
options:
syslog-address: "tcp://logs.papertrailapp.com:XXXXX"
```
**Option C: Self-hosted (Loki + Grafana)**
Pour une solution auto-hébergée, utilisez Loki + Grafana:
```yaml
# docker-compose.prod.yml (ajout)
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- loki_data:/loki
promtail:
image: grafana/promtail:latest
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
```
## 2. Métriques
### 2.1 Métriques Vercel
Vercel fournit des métriques intégrées:
- **Analytics**: Trafic, pages vues, temps de chargement
- **Speed Insights**: Core Web Vitals, temps de réponse
- **Web Vitals**: LCP, FID, CLS
**Activation:**
```bash
npm install @vercel/analytics @vercel/speed-insights
```
```typescript
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
```
### 2.2 Métriques PostgreSQL
**Via pg_stat_statements:**
```sql
-- Activer l'extension
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- Voir les requêtes les plus lentes
SELECT
query,
calls,
total_exec_time,
mean_exec_time,
max_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
```
**Via Docker:**
```bash
# Statistiques du conteneur
docker stats neah-postgres-prod
# Métriques détaillées
docker exec neah-postgres-prod psql -U neah_prod_user -d calendar_db -c "
SELECT
datname,
numbackends,
xact_commit,
xact_rollback,
blks_read,
blks_hit,
tup_returned,
tup_fetched
FROM pg_stat_database
WHERE datname = 'calendar_db';
"
```
### 2.3 Métriques Redis
```bash
# Statistiques Redis
docker exec neah-redis-prod redis-cli INFO stats
# Mémoire utilisée
docker exec neah-redis-prod redis-cli INFO memory
# Commandes les plus utilisées
docker exec neah-redis-prod redis-cli INFO commandstats
```
### 2.4 Monitoring avec Prometheus (Optionnel)
Pour un monitoring avancé, utilisez Prometheus + Grafana:
```yaml
# docker-compose.prod.yml (ajout)
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
```
## 3. Alertes
### 3.1 Alertes Vercel
Vercel envoie automatiquement des alertes pour:
- Échecs de déploiement
- Erreurs critiques
- Dépassement de quotas
**Configuration:**
- Dashboard Vercel → Project → Settings → Notifications
### 3.2 Alertes personnalisées
**Option A: Sentry (Recommandé)**
Sentry fournit un suivi d'erreurs avancé:
```bash
npm install @sentry/nextjs
```
```bash
npx @sentry/wizard@latest -i nextjs
```
**Option B: Uptime Robot**
Pour surveiller la disponibilité:
1. Créez un compte sur [Uptime Robot](https://uptimerobot.com)
2. Ajoutez un monitor HTTP pour votre domaine Vercel
3. Configurez les alertes (email, Slack, etc.)
**Option C: Health Check Endpoint**
Créez un endpoint de santé:
```typescript
// app/api/health/route.ts
import { NextResponse } from 'next/server';
import { getRedisClient } from '@/lib/redis';
import { prisma } from '@/lib/prisma';
export async function GET() {
const checks = {
status: 'ok',
timestamp: new Date().toISOString(),
checks: {
database: 'unknown',
redis: 'unknown',
},
};
// Vérifier PostgreSQL
try {
await prisma.$queryRaw`SELECT 1`;
checks.checks.database = 'ok';
} catch (error) {
checks.checks.database = 'error';
checks.status = 'degraded';
}
// Vérifier Redis
try {
const redis = getRedisClient();
await redis.ping();
checks.checks.redis = 'ok';
} catch (error) {
checks.checks.redis = 'error';
checks.status = 'degraded';
}
const statusCode = checks.status === 'ok' ? 200 : 503;
return NextResponse.json(checks, { status: statusCode });
}
```
## 4. Dashboards
### 4.1 Dashboard Vercel
Le dashboard Vercel fournit:
- Vue d'ensemble des déploiements
- Analytics en temps réel
- Logs intégrés
### 4.2 Dashboard personnalisé (Grafana)
Si vous utilisez Prometheus + Grafana:
1. Créez un dashboard Grafana
2. Ajoutez des panels pour:
- Taux d'erreur HTTP
- Temps de réponse
- Utilisation de la base de données
- Utilisation de Redis
- Métriques applicatives
## 5. Bonnes pratiques
### 5.1 Logging
- **Niveau approprié**: Utilisez `info`, `warn`, `error` selon le contexte
- **Contexte structuré**: Ajoutez des métadonnées pertinentes
- **Pas de secrets**: Ne loguez jamais de mots de passe, tokens, etc.
- **Format cohérent**: Utilisez un format JSON structuré
```typescript
// ✅ Bon
logger.info('User created', {
userId: user.id,
email: user.email
});
// ❌ Mauvais
logger.info(`User created: ${user.password}`); // Ne jamais logger de secrets
```
### 5.2 Monitoring
- **Métriques clés**: Surveillez le taux d'erreur, latence, débit
- **Alertes pertinentes**: Configurez des alertes pour les problèmes critiques uniquement
- **SLIs/SLOs**: Définissez des objectifs de niveau de service
### 5.3 Performance
- **APM**: Utilisez un outil d'APM (Application Performance Monitoring)
- **Profiling**: Profilez régulièrement pour identifier les goulots d'étranglement
- **Optimisation**: Optimisez les requêtes lentes identifiées
## 6. Outils recommandés
| Outil | Usage | Coût |
|-------|-------|------|
| Vercel Analytics | Métriques intégrées | Gratuit (plan Hobby) |
| Sentry | Suivi d'erreurs | Gratuit (plan Developer) |
| Logtail | Centralisation logs | Payant |
| Uptime Robot | Monitoring uptime | Gratuit (50 monitors) |
| Grafana Cloud | Dashboards | Gratuit (limité) |
## 7. Checklist de mise en place
- [ ] Activer Vercel Analytics et Speed Insights
- [ ] Configurer Sentry pour le suivi d'erreurs
- [ ] Créer un endpoint `/api/health`
- [ ] Configurer les alertes Vercel
- [ ] Activer les logs PostgreSQL
- [ ] Configurer un service de centralisation de logs (optionnel)
- [ ] Créer des dashboards de monitoring (optionnel)
- [ ] Documenter les procédures d'alerte

588
docs/RUNBOOK.md Normal file
View File

@ -0,0 +1,588 @@
# Runbook de Production - Neah
Ce document contient toutes les procédures opérationnelles pour gérer Neah en production.
## Table des matières
1. [Déploiement](#déploiement)
2. [Exploitation quotidienne](#exploitation-quotidienne)
3. [Incidents](#incidents)
4. [Rollback](#rollback)
5. [Maintenance](#maintenance)
6. [Contacts](#contacts)
---
## Déploiement
### Déploiement standard (Vercel)
#### Prérequis
- [ ] Toutes les migrations Prisma sont testées en staging
- [ ] Les variables d'environnement sont à jour dans Vercel
- [ ] Les tests passent localement: `npm run build`
#### Étapes
1. **Vérifier l'état actuel**
```bash
# Vérifier les migrations en attente
npx prisma migrate status
# Vérifier la configuration
./scripts/verify-vercel-config.sh
```
2. **Appliquer les migrations Prisma**
```bash
# Se connecter au serveur PostgreSQL (via tunnel SSH si nécessaire)
export DATABASE_URL="postgresql://user:password@host:5432/calendar_db"
# Appliquer les migrations
./scripts/migrate-prod.sh
# OU manuellement
npx prisma migrate deploy
```
3. **Déployer sur Vercel**
Le déploiement se fait automatiquement via Git:
```bash
# Pousser les changements vers la branche main
git push origin main
# Vercel déploiera automatiquement
# Surveiller le déploiement dans Vercel Dashboard
```
**OU manuellement via CLI:**
```bash
vercel --prod
```
4. **Vérifier le déploiement**
- [ ] Vérifier les logs Vercel pour les erreurs
- [ ] Tester l'endpoint de santé: `GET https://votre-domaine.vercel.app/api/health`
- [ ] Tester l'authentification
- [ ] Vérifier les fonctionnalités critiques
### Déploiement avec migrations critiques
Si les migrations modifient des données existantes:
1. **Sauvegarder la base de données**
```bash
# Sauvegarde complète
docker exec neah-postgres-prod pg_dump -U neah_prod_user calendar_db > backup_$(date +%Y%m%d_%H%M%S).sql
# Sauvegarde compressée
docker exec neah-postgres-prod pg_dump -U neah_prod_user calendar_db | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
```
2. **Tester les migrations en staging**
3. **Appliquer en production** (voir procédure standard)
4. **Vérifier l'intégrité des données**
```sql
-- Exemples de vérifications
SELECT COUNT(*) FROM "User";
SELECT COUNT(*) FROM "Mission";
SELECT COUNT(*) FROM "Event";
```
---
## Exploitation quotidienne
### Vérifications quotidiennes
#### 1. Santé de l'application
```bash
# Vérifier l'endpoint de santé
curl https://votre-domaine.vercel.app/api/health
# Vérifier les logs Vercel récents
vercel logs --follow
```
#### 2. Santé de PostgreSQL
```bash
# Se connecter au serveur
ssh user@your-server
# Vérifier l'état du conteneur
docker ps | grep neah-postgres-prod
# Vérifier les connexions actives
docker exec neah-postgres-prod psql -U neah_prod_user -d calendar_db -c "
SELECT count(*) as active_connections
FROM pg_stat_activity
WHERE datname = 'calendar_db';
"
# Vérifier l'espace disque
docker exec neah-postgres-prod df -h
```
#### 3. Santé de Redis
```bash
# Vérifier l'état du conteneur
docker ps | grep neah-redis-prod
# Vérifier la mémoire utilisée
docker exec neah-redis-prod redis-cli INFO memory
# Vérifier les clés
docker exec neah-redis-prod redis-cli DBSIZE
```
#### 4. Métriques Vercel
- Consulter Vercel Dashboard → Analytics
- Vérifier:
- Taux d'erreur (< 1%)
- Temps de réponse (< 500ms)
- Trafic normal
### Tâches hebdomadaires
#### Sauvegarde de la base de données
```bash
#!/bin/bash
# scripts/backup-db.sh
BACKUP_DIR="/opt/neah/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.sql.gz"
mkdir -p "$BACKUP_DIR"
docker exec neah-postgres-prod pg_dump -U neah_prod_user calendar_db | gzip > "$BACKUP_FILE"
# Garder uniquement les 7 derniers backups
ls -t $BACKUP_DIR/backup_*.sql.gz | tail -n +8 | xargs rm -f
echo "Backup créé: $BACKUP_FILE"
```
**Automatisation avec cron:**
```bash
# Ajouter à crontab (crontab -e)
0 2 * * * /opt/neah/scripts/backup-db.sh >> /var/log/neah-backup.log 2>&1
```
#### Nettoyage des logs
```bash
# Nettoyer les anciens logs Docker (garder 7 jours)
docker system prune -f --filter "until=168h"
```
---
## Incidents
### Procédure générale
1. **Identifier le problème**
- Consulter les logs Vercel
- Vérifier l'endpoint `/api/health`
- Vérifier les logs PostgreSQL/Redis
2. **Évaluer l'impact**
- Nombre d'utilisateurs affectés
- Fonctionnalités impactées
- Criticité (P1, P2, P3)
3. **Contenir le problème**
- Rollback si nécessaire (voir section Rollback)
- Désactiver les fonctionnalités problématiques
- Communiquer avec les utilisateurs
4. **Résoudre**
- Corriger le problème
- Tester en staging
- Déployer en production
5. **Post-mortem**
- Documenter l'incident
- Identifier les causes racines
- Mettre en place des mesures préventives
### Scénarios courants
#### Scénario 1: Application inaccessible (503)
**Symptômes:**
- Erreur 503 sur toutes les pages
- Health check échoue
**Actions:**
1. Vérifier Vercel Dashboard → Deployments
2. Vérifier les logs Vercel
3. Vérifier la connexion à PostgreSQL:
```bash
# Tester la connexion
psql $DATABASE_URL -c "SELECT 1;"
```
4. Vérifier les variables d'environnement dans Vercel
5. Si problème persistant, rollback vers version précédente
#### Scénario 2: Erreurs de base de données
**Symptômes:**
- Erreurs "Connection refused" ou "Connection timeout"
- Health check indique `database: error`
**Actions:**
1. Vérifier l'état du conteneur PostgreSQL:
```bash
docker ps | grep neah-postgres-prod
docker logs neah-postgres-prod --tail 50
```
2. Vérifier les ressources:
```bash
docker stats neah-postgres-prod
```
3. Redémarrer si nécessaire:
```bash
docker restart neah-postgres-prod
```
4. Vérifier la connexion après redémarrage
#### Scénario 3: Performance dégradée
**Symptômes:**
- Temps de réponse élevés
- Timeouts fréquents
**Actions:**
1. Identifier les requêtes lentes:
```sql
-- Requêtes les plus lentes
SELECT
query,
calls,
mean_exec_time,
max_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
```
2. Vérifier les index manquants:
```sql
-- Tables sans index
SELECT
schemaname,
tablename,
attname,
n_distinct,
correlation
FROM pg_stats
WHERE schemaname = 'public'
AND n_distinct > 100
AND correlation < 0.1;
```
3. Optimiser les requêtes ou ajouter des index
4. Vérifier l'utilisation de Redis (cache)
#### Scénario 4: Erreurs d'authentification Keycloak
**Symptômes:**
- Utilisateurs ne peuvent pas se connecter
- Erreurs "Invalid token" ou "Authentication failed"
**Actions:**
1. Vérifier la configuration Keycloak dans Vercel
2. Vérifier que Keycloak est accessible:
```bash
curl https://keycloak.example.com/realms/neah/.well-known/openid-configuration
```
3. Vérifier les tokens dans les logs
4. Contacter l'administrateur Keycloak si nécessaire
---
## Rollback
### Rollback Vercel
#### Méthode 1: Via Dashboard (Recommandé)
1. Aller dans Vercel Dashboard → Deployments
2. Trouver le déploiement précédent (stable)
3. Cliquer sur "..." → "Promote to Production"
4. Confirmer le rollback
#### Méthode 2: Via CLI
```bash
# Lister les déploiements
vercel ls
# Rollback vers un déploiement spécifique
vercel rollback [deployment-url]
```
### Rollback de migration Prisma
**ATTENTION:** Les rollbacks de migration peuvent être destructifs. Toujours tester en staging d'abord.
#### Méthode 1: Migration de rollback
Si une migration de rollback existe:
```bash
export DATABASE_URL="postgresql://..."
npx prisma migrate resolve --rolled-back [migration-name]
npx prisma migrate deploy
```
#### Méthode 2: Restauration depuis sauvegarde
```bash
# Arrêter l'application si nécessaire
# Restaurer la sauvegarde
cat backup_20240112_120000.sql | docker exec -i neah-postgres-prod psql -U neah_prod_user calendar_db
# OU avec compression
gunzip < backup_20240112_120000.sql.gz | docker exec -i neah-postgres-prod psql -U neah_prod_user calendar_db
# Vérifier l'intégrité
docker exec neah-postgres-prod psql -U neah_prod_user -d calendar_db -c "SELECT COUNT(*) FROM \"User\";"
```
### Procédure complète de rollback
1. **Évaluer l'impact**
- Identifier les changements à annuler
- Vérifier les dépendances
2. **Sauvegarder l'état actuel**
```bash
# Sauvegarde avant rollback
docker exec neah-postgres-prod pg_dump -U neah_prod_user calendar_db > backup_before_rollback_$(date +%Y%m%d_%H%M%S).sql
```
3. **Rollback Vercel** (voir ci-dessus)
4. **Rollback base de données** (si nécessaire)
5. **Vérifier**
```bash
# Tester l'endpoint de santé
curl https://votre-domaine.vercel.app/api/health
# Tester les fonctionnalités critiques
```
6. **Communiquer**
- Informer l'équipe du rollback
- Documenter la raison du rollback
- Planifier la correction du problème
---
## Maintenance
### Maintenance planifiée
#### Mise à jour des dépendances
```bash
# Vérifier les mises à jour
npm outdated
# Mettre à jour (une dépendance à la fois)
npm update [package-name]
# Tester en local
npm run build
npm run dev
# Tester en staging avant production
```
#### Mise à jour PostgreSQL
```bash
# Arrêter le conteneur
docker stop neah-postgres-prod
# Sauvegarder
docker exec neah-postgres-prod pg_dump -U neah_prod_user calendar_db > backup_before_upgrade.sql
# Mettre à jour l'image dans docker-compose.prod.yml
# postgres:15-alpine → postgres:16-alpine
# Redémarrer
docker-compose -f docker-compose.prod.yml up -d db
# Vérifier
docker exec neah-postgres-prod psql -U neah_prod_user -d calendar_db -c "SELECT version();"
```
#### Nettoyage de la base de données
```sql
-- Analyser les tables
ANALYZE;
-- VACUUM (nettoyage)
VACUUM ANALYZE;
-- Vérifier les tables orphelines
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
```
### Maintenance d'urgence
#### Redémarrage des services
```bash
# Redémarrer PostgreSQL
docker restart neah-postgres-prod
# Redémarrer Redis
docker restart neah-redis-prod
# Redémarrer tous les services
docker-compose -f docker-compose.prod.yml restart
```
#### Libération d'espace disque
```bash
# Vérifier l'espace disque
df -h
# Nettoyer les images Docker non utilisées
docker image prune -a
# Nettoyer les volumes non utilisés
docker volume prune
# Nettoyer les anciens logs
journalctl --vacuum-time=7d
```
---
## Contacts
### Équipe Neah
- **DevOps**: [email]
- **Développement**: [email]
- **Support**: [email]
### Services externes
- **Vercel Support**: https://vercel.com/support
- **Keycloak Admin**: [contact]
- **PostgreSQL**: [contact serveur]
### Escalade
1. **Niveau 1**: Équipe Neah
2. **Niveau 2**: Administrateur système
3. **Niveau 3**: Direction technique
---
## Annexes
### Commandes utiles
```bash
# Vérifier les logs en temps réel
vercel logs --follow
# Vérifier l'état des migrations
npx prisma migrate status
# Tester la connexion PostgreSQL
psql $DATABASE_URL -c "SELECT version();"
# Statistiques PostgreSQL
docker exec neah-postgres-prod psql -U neah_prod_user -d calendar_db -c "
SELECT
datname,
numbackends,
xact_commit,
xact_rollback,
blks_read,
blks_hit
FROM pg_stat_database
WHERE datname = 'calendar_db';
"
# Statistiques Redis
docker exec neah-redis-prod redis-cli INFO stats
```
### Checklist de déploiement
- [ ] Migrations testées en staging
- [ ] Variables d'environnement à jour
- [ ] Build local réussi
- [ ] Sauvegarde de la base de données
- [ ] Migrations appliquées
- [ ] Déploiement Vercel réussi
- [ ] Health check OK
- [ ] Tests fonctionnels passés
- [ ] Logs vérifiés (pas d'erreurs)
### Checklist de rollback
- [ ] Sauvegarde avant rollback créée
- [ ] Déploiement précédent identifié
- [ ] Rollback Vercel effectué
- [ ] Rollback base de données (si nécessaire)
- [ ] Health check OK
- [ ] Tests fonctionnels passés
- [ ] Équipe informée
- [ ] Problème documenté

View File

@ -1,47 +0,0 @@
// This file contains helper functions for Electron builds
const { writeFileSync, existsSync, mkdirSync } = require('fs');
const path = require('path');
/**
* Create a CSC_LINK placeholder file for macOS builds
* In a real production environment, you would use a real certificate
*/
function createDummyCertificateFile() {
const buildDir = path.join(__dirname, '../build');
// Create build directory if it doesn't exist
if (!existsSync(buildDir)) {
mkdirSync(buildDir, { recursive: true });
}
// Create a dummy cert file
const certPath = path.join(buildDir, 'dummy-cert.p12');
writeFileSync(certPath, 'DUMMY CERTIFICATE', 'utf8');
console.log(`Created dummy certificate at ${certPath}`);
return certPath;
}
/**
* Set up the environment for code signing
* This is a placeholder - in production, you'd use real certificates
*/
function setupCodeSigning() {
if (process.platform === 'darwin') {
console.log('Setting up macOS code signing');
const certPath = createDummyCertificateFile();
// In a real environment, you would have a real certificate and password
process.env.CSC_LINK = certPath;
process.env.CSC_KEY_PASSWORD = 'dummy-password';
console.log('Code signing environment variables set');
} else {
console.log(`Code signing setup not implemented for ${process.platform}`);
}
}
module.exports = {
setupCodeSigning
};

View File

@ -1,32 +0,0 @@
#!/usr/bin/env node
const { execSync } = require('child_process');
const { setupCodeSigning } = require('./build-helpers');
// Set NODE_ENV
process.env.NODE_ENV = 'production';
console.log('Starting Electron build process...');
try {
// Set up code signing if needed
setupCodeSigning();
// Build Next.js app with static export
console.log('Building Next.js application...');
execSync('cross-env ELECTRON_BUILD=true next build', {
stdio: 'inherit',
env: { ...process.env }
});
// Build Electron app
console.log('Building Electron application...');
execSync('electron-builder', {
stdio: 'inherit',
env: { ...process.env }
});
console.log('Electron build completed successfully!');
} catch (error) {
console.error('Electron build failed:', error);
process.exit(1);
}

View File

@ -1,81 +0,0 @@
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const isDev = process.env.NODE_ENV === 'development';
const port = process.env.PORT || 3000;
const fs = require('fs');
const serve = require('electron-serve');
// Setup for production with static files
const loadURL = process.env.NODE_ENV !== 'development'
? serve({ directory: path.join(__dirname, '../.next') })
: null;
// Keep a global reference of the window object to prevent garbage collection
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
},
icon: path.join(__dirname, '../public/favicon.ico')
});
// Load the app
if (isDev) {
// In development, load from local server
mainWindow.loadURL(`http://localhost:${port}`);
mainWindow.webContents.openDevTools();
} else {
// In production, load from static files
loadURL(mainWindow);
}
mainWindow.on('closed', () => {
mainWindow = null;
});
// Handle window maximize/unmaximize
mainWindow.on('maximize', () => {
mainWindow.webContents.send('window-maximized');
});
mainWindow.on('unmaximize', () => {
mainWindow.webContents.send('window-unmaximized');
});
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// IPC handlers for window controls
ipcMain.handle('minimize-window', () => {
mainWindow.minimize();
});
ipcMain.handle('maximize-window', () => {
if (mainWindow.isMaximized()) {
mainWindow.unmaximize();
} else {
mainWindow.maximize();
}
});
ipcMain.handle('close-window', () => {
mainWindow.close();
});

View File

@ -1,24 +0,0 @@
const { contextBridge, ipcRenderer } = require('electron');
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('electron', {
// Window control methods
windowControl: {
minimize: () => ipcRenderer.invoke('minimize-window'),
maximize: () => ipcRenderer.invoke('maximize-window'),
close: () => ipcRenderer.invoke('close-window'),
},
// Window state listeners
windowState: {
onMaximized: (callback) => ipcRenderer.on('window-maximized', callback),
onUnmaximized: (callback) => ipcRenderer.on('window-unmaximized', callback),
removeMaximizedListener: () => ipcRenderer.removeAllListeners('window-maximized'),
removeUnmaximizedListener: () => ipcRenderer.removeAllListeners('window-unmaximized'),
},
// App info
appInfo: {
isElectron: true,
version: process.env.npm_package_version,
}
});

View File

@ -14,7 +14,9 @@ const nextConfig = {
parallelServerBuildTraces: true, parallelServerBuildTraces: true,
parallelServerCompiles: true, parallelServerCompiles: true,
}, },
turbopack: {}, // Turbopack: activé pour le développement, mais peut causer des problèmes en production
// Vercel utilisera son propre système de build optimisé
// turbopack: {},
async headers() { async headers() {
return [ return [
{ {
@ -27,7 +29,13 @@ const nextConfig = {
] ]
} }
] ]
} },
// Configuration pour Vercel
// Assurez-vous que les variables d'environnement sont correctement configurées
env: {
// Les variables NEXT_PUBLIC_* sont automatiquement exposées par Next.js
// Pas besoin de les déclarer ici explicitement
},
}; };
export default nextConfig; export default nextConfig;

View File

@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"main": "index.js", "main": "index.js",
"description": "Neah Desktop Application", "description": "Neah Web Application",
"author": "Neah Team", "author": "Neah Team",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@ -11,38 +11,13 @@
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"sync-users": "node scripts/sync-users.js", "sync-users": "node scripts/sync-users.js",
"electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:3000 && electron electron/main.js\"", "migrate:dev": "prisma migrate dev",
"electron:build": "node electron/build.js", "migrate:deploy": "prisma migrate deploy",
"electron:start": "electron electron/main.js" "migrate:status": "prisma migrate status",
}, "migrate:prod": "bash scripts/migrate-prod.sh",
"build": { "verify:config": "bash scripts/verify-vercel-config.sh",
"appId": "com.neah.app", "db:studio": "prisma studio",
"productName": "Neah", "db:generate": "prisma generate"
"files": [
"index.js",
".next/**/*",
"node_modules/**/*",
"electron/**/*",
"public/**/*",
"package.json"
],
"directories": {
"buildResources": "public"
},
"mac": {
"category": "public.app-category.productivity"
},
"win": {
"target": [
"nsis"
]
},
"linux": {
"target": [
"AppImage",
"deb"
]
}
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.802.0", "@aws-sdk/client-s3": "^3.802.0",
@ -150,9 +125,6 @@
"buffer": "^6.0.3", "buffer": "^6.0.3",
"concurrently": "^9.1.2", "concurrently": "^9.1.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"electron": "^36.1.0",
"electron-builder": "^26.0.12",
"electron-serve": "^2.1.1",
"postcss": "^8", "postcss": "^8",
"prisma": "^6.4.1", "prisma": "^6.4.1",
"stream-browserify": "^3.0.0", "stream-browserify": "^3.0.0",

66
scripts/migrate-prod.sh Executable file
View File

@ -0,0 +1,66 @@
#!/bin/bash
# Script de migration Prisma pour la production
# Usage: ./scripts/migrate-prod.sh [--dry-run]
set -e
# Couleurs pour les messages
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Vérifier que DATABASE_URL est définie
if [ -z "$DATABASE_URL" ]; then
echo -e "${RED}❌ ERREUR: DATABASE_URL n'est pas définie${NC}"
echo "Définissez-la avec: export DATABASE_URL='postgresql://...'"
exit 1
fi
# Mode dry-run
DRY_RUN=false
if [ "$1" == "--dry-run" ]; then
DRY_RUN=true
echo -e "${YELLOW}⚠️ Mode dry-run activé - aucune modification ne sera appliquée${NC}"
fi
echo -e "${GREEN}🚀 Migration Prisma pour la production${NC}"
echo ""
# Vérifier la connexion à la base de données
echo -e "${YELLOW}📡 Vérification de la connexion à la base de données...${NC}"
if ! npx prisma db execute --stdin <<< "SELECT 1;" > /dev/null 2>&1; then
echo -e "${RED}❌ Impossible de se connecter à la base de données${NC}"
echo "Vérifiez que DATABASE_URL est correcte et que la base de données est accessible"
exit 1
fi
echo -e "${GREEN}✅ Connexion réussie${NC}"
echo ""
# Afficher l'état actuel des migrations
echo -e "${YELLOW}📊 État actuel des migrations:${NC}"
npx prisma migrate status
echo ""
# Demander confirmation si pas en dry-run
if [ "$DRY_RUN" = false ]; then
echo -e "${YELLOW}⚠️ Vous êtes sur le point d'appliquer les migrations en PRODUCTION${NC}"
read -p "Continuer? (oui/non): " -r
if [[ ! $REPLY =~ ^[Oo]ui$ ]]; then
echo -e "${RED}❌ Migration annulée${NC}"
exit 0
fi
echo ""
fi
# Appliquer les migrations
if [ "$DRY_RUN" = false ]; then
echo -e "${YELLOW}🔄 Application des migrations...${NC}"
npx prisma migrate deploy
echo -e "${GREEN}✅ Migrations appliquées avec succès${NC}"
else
echo -e "${YELLOW} Mode dry-run: les migrations ne seraient pas appliquées${NC}"
fi
echo ""
echo -e "${GREEN}✨ Migration terminée${NC}"

160
scripts/verify-vercel-config.sh Executable file
View File

@ -0,0 +1,160 @@
#!/bin/bash
# Script de vérification de la configuration pour Vercel
# Usage: ./scripts/verify-vercel-config.sh
set -e
# Couleurs
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}🔍 Vérification de la configuration pour Vercel${NC}"
echo ""
ERRORS=0
WARNINGS=0
# Fonction pour vérifier une variable d'environnement
check_env_var() {
local var_name=$1
local required=${2:-false}
if [ -z "${!var_name}" ]; then
if [ "$required" = true ]; then
echo -e "${RED}${var_name} n'est pas définie (OBLIGATOIRE)${NC}"
ERRORS=$((ERRORS + 1))
else
echo -e "${YELLOW}⚠️ ${var_name} n'est pas définie (optionnelle)${NC}"
WARNINGS=$((WARNINGS + 1))
fi
else
echo -e "${GREEN}${var_name} est définie${NC}"
fi
}
# Charger les variables d'environnement depuis .env.local si présent
if [ -f .env.local ]; then
echo -e "${BLUE}📄 Chargement de .env.local...${NC}"
set -a
source .env.local
set +a
fi
echo -e "${BLUE}📋 Vérification des variables d'environnement critiques:${NC}"
echo ""
# Variables obligatoires
check_env_var "NODE_ENV" true
check_env_var "NEXTAUTH_URL" true
check_env_var "NEXTAUTH_SECRET" true
check_env_var "DATABASE_URL" true
echo ""
echo -e "${BLUE}🔐 Vérification de l'authentification Keycloak:${NC}"
check_env_var "KEYCLOAK_BASE_URL" true
check_env_var "KEYCLOAK_REALM" true
check_env_var "KEYCLOAK_CLIENT_ID" true
check_env_var "KEYCLOAK_CLIENT_SECRET" true
check_env_var "KEYCLOAK_ISSUER" false
check_env_var "NEXT_PUBLIC_KEYCLOAK_ISSUER" false
echo ""
echo -e "${BLUE}💾 Vérification de la base de données:${NC}"
if [ -n "$DATABASE_URL" ]; then
if [[ "$DATABASE_URL" == postgresql://* ]]; then
echo -e "${GREEN}✅ DATABASE_URL semble être une URL PostgreSQL valide${NC}"
# Vérifier si SSL est activé (recommandé pour la production)
if [[ "$DATABASE_URL" != *"sslmode=require"* ]] && [[ "$DATABASE_URL" != *"sslmode=prefer"* ]]; then
echo -e "${YELLOW}⚠️ DATABASE_URL ne contient pas sslmode=require (recommandé pour la production)${NC}"
WARNINGS=$((WARNINGS + 1))
fi
else
echo -e "${RED}❌ DATABASE_URL ne semble pas être une URL PostgreSQL valide${NC}"
ERRORS=$((ERRORS + 1))
fi
fi
echo ""
echo -e "${BLUE}🔴 Vérification de Redis (optionnel):${NC}"
check_env_var "REDIS_URL" false
if [ -z "$REDIS_URL" ]; then
check_env_var "REDIS_HOST" false
check_env_var "REDIS_PORT" false
fi
check_env_var "REDIS_ENCRYPTION_KEY" false
echo ""
echo -e "${BLUE}🔧 Vérification des intégrations (optionnelles):${NC}"
check_env_var "LEANTIME_API_URL" false
check_env_var "LEANTIME_TOKEN" false
check_env_var "ROCKET_CHAT_TOKEN" false
check_env_var "ROCKET_CHAT_USER_ID" false
check_env_var "N8N_API_KEY" false
check_env_var "DOLIBARR_API_URL" false
check_env_var "DOLIBARR_API_KEY" false
echo ""
echo -e "${BLUE}📦 Vérification de la configuration Next.js:${NC}"
# Vérifier que next.config.mjs existe
if [ -f "next.config.mjs" ]; then
echo -e "${GREEN}✅ next.config.mjs existe${NC}"
# Vérifier que Turbopack n'est pas forcé en production
if grep -q "turbopack:" next.config.mjs && [ "$NODE_ENV" = "production" ]; then
echo -e "${YELLOW}⚠️ Turbopack est activé dans next.config.mjs (peut causer des problèmes sur Vercel)${NC}"
WARNINGS=$((WARNINGS + 1))
fi
else
echo -e "${RED}❌ next.config.mjs n'existe pas${NC}"
ERRORS=$((ERRORS + 1))
fi
# Vérifier package.json
if [ -f "package.json" ]; then
echo -e "${GREEN}✅ package.json existe${NC}"
# Vérifier que le script build existe
if grep -q '"build"' package.json; then
echo -e "${GREEN}✅ Script 'build' trouvé dans package.json${NC}"
else
echo -e "${RED}❌ Script 'build' manquant dans package.json${NC}"
ERRORS=$((ERRORS + 1))
fi
else
echo -e "${RED}❌ package.json n'existe pas${NC}"
ERRORS=$((ERRORS + 1))
fi
echo ""
echo -e "${BLUE}🧪 Test de build (simulation):${NC}"
if command -v npm &> /dev/null; then
echo -e "${YELLOW} Pour tester le build réellement, exécutez: npm run build${NC}"
else
echo -e "${YELLOW}⚠️ npm n'est pas installé, impossible de tester le build${NC}"
WARNINGS=$((WARNINGS + 1))
fi
echo ""
echo -e "${BLUE}📊 Résumé:${NC}"
echo -e " ${GREEN}✅ Variables correctes${NC}"
if [ $WARNINGS -gt 0 ]; then
echo -e " ${YELLOW}⚠️ $WARNINGS avertissement(s)${NC}"
fi
if [ $ERRORS -gt 0 ]; then
echo -e " ${RED}$ERRORS erreur(s)${NC}"
echo ""
echo -e "${RED}❌ La configuration n'est pas prête pour Vercel${NC}"
exit 1
else
echo ""
echo -e "${GREEN}✅ Configuration prête pour Vercel${NC}"
if [ $WARNINGS -gt 0 ]; then
echo -e "${YELLOW}⚠️ Vérifiez les avertissements ci-dessus${NC}"
fi
exit 0
fi

41
types/electron.d.ts vendored
View File

@ -1,41 +0,0 @@
interface ElectronWindowControl {
minimize: () => Promise<void>;
maximize: () => Promise<void>;
close: () => Promise<void>;
}
interface ElectronWindowState {
onMaximized: (callback: () => void) => void;
onUnmaximized: (callback: () => void) => void;
removeMaximizedListener: () => void;
removeUnmaximizedListener: () => void;
}
interface ElectronAppInfo {
isElectron: boolean;
version: string;
}
interface ElectronAPI {
windowControl: {
minimize: () => Promise<void>;
maximize: () => Promise<void>;
close: () => Promise<void>;
};
windowState: {
onMaximized: (callback: () => void) => void;
onUnmaximized: (callback: () => void) => void;
removeMaximizedListener: () => void;
removeUnmaximizedListener: () => void;
};
appInfo: {
isElectron: boolean;
version: string;
};
}
declare global {
interface Window {
electron?: ElectronAPI;
}
}

47
vercel.json Normal file
View File

@ -0,0 +1,47 @@
{
"buildCommand": "npm run build",
"devCommand": "npm run dev",
"installCommand": "npm ci",
"framework": "nextjs",
"regions": ["cdg1"],
"env": {
"NEXT_TELEMETRY_DISABLED": "1"
},
"functions": {
"app/**/*.ts": {
"maxDuration": 30
},
"app/**/*.tsx": {
"maxDuration": 30
}
},
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
},
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
}
]
}
],
"rewrites": [
{
"source": "/api/:path*",
"destination": "/api/:path*"
}
]
}