diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d920d73 --- /dev/null +++ b/.env.example @@ -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 diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 0000000..232a1e5 --- /dev/null +++ b/Dockerfile.prod @@ -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 npx prisma migrate deploy +CMD ["node", "server.js"] diff --git a/README.DEPLOYMENT.md b/README.DEPLOYMENT.md new file mode 100644 index 0000000..457ff8b --- /dev/null +++ b/README.DEPLOYMENT.md @@ -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. diff --git a/app/api/health/route.ts b/app/api/health/route.ts new file mode 100644 index 0000000..58cf52f --- /dev/null +++ b/app/api/health/route.ts @@ -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 = {}; + 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 }); +} diff --git a/components/electron/WindowControls.tsx b/components/electron/WindowControls.tsx deleted file mode 100644 index 0ddf679..0000000 --- a/components/electron/WindowControls.tsx +++ /dev/null @@ -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 ( -
- - - -
- ); -} \ No newline at end of file diff --git a/components/electron/window-controls.tsx b/components/electron/window-controls.tsx deleted file mode 100644 index 714a8be..0000000 --- a/components/electron/window-controls.tsx +++ /dev/null @@ -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; - maximize: () => Promise; - close: () => Promise; - }; - 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 ( -
- - - - - -
- ); -} \ No newline at end of file diff --git a/components/main-nav.tsx b/components/main-nav.tsx index 01cbb3d..9b9343b 100644 --- a/components/main-nav.tsx +++ b/components/main-nav.tsx @@ -39,7 +39,6 @@ import { } from "@/components/ui/dropdown-menu"; import { NotificationBadge } from "./notification-badge"; import { NotesDialog } from "./notes-dialog"; -import { WindowControls } from "@/components/electron/WindowControls"; import { MainNavTime } from "./main-nav-time"; const requestNotificationPermission = async () => { @@ -285,9 +284,6 @@ export function MainNav() { {/* Right side */}
- {/* Electron window controls - only shows in electron environment */} - - {status === "authenticated" && session?.user ? ( diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..3635504 --- /dev/null +++ b/docker-compose.prod.yml @@ -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 diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..0ff4998 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -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= + +# 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= +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= +REDIS_ENCRYPTION_KEY= +``` + +#### Variables optionnelles (selon vos intégrations) + +```env +# Leantime +LEANTIME_API_URL=https://leantime.example.com +LEANTIME_TOKEN= + +# RocketChat +ROCKET_CHAT_TOKEN= +ROCKET_CHAT_USER_ID= +NEXT_PUBLIC_IFRAME_PAROLE_URL=https://rocketchat.example.com/channel/general + +# N8N +N8N_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= + +# 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= +MINIO_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/) diff --git a/docs/OBSERVABILITY.md b/docs/OBSERVABILITY.md new file mode 100644 index 0000000..9dbcc74 --- /dev/null +++ b/docs/OBSERVABILITY.md @@ -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 ( + + + {children} + + + + + ); +} +``` + +### 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 diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md new file mode 100644 index 0000000..4225080 --- /dev/null +++ b/docs/RUNBOOK.md @@ -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é diff --git a/electron/build-helpers.js b/electron/build-helpers.js deleted file mode 100644 index 36c143e..0000000 --- a/electron/build-helpers.js +++ /dev/null @@ -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 -}; \ No newline at end of file diff --git a/electron/build.js b/electron/build.js deleted file mode 100644 index 6a1a826..0000000 --- a/electron/build.js +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/electron/main.js b/electron/main.js deleted file mode 100644 index e9d2703..0000000 --- a/electron/main.js +++ /dev/null @@ -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(); -}); \ No newline at end of file diff --git a/electron/preload.js b/electron/preload.js deleted file mode 100644 index 1a88afc..0000000 --- a/electron/preload.js +++ /dev/null @@ -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, - } -}); \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 97e5836..5c7e92b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -14,7 +14,9 @@ const nextConfig = { parallelServerBuildTraces: 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() { 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; diff --git a/package.json b/package.json index 0fbbf1d..982c913 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "main": "index.js", - "description": "Neah Desktop Application", + "description": "Neah Web Application", "author": "Neah Team", "scripts": { "dev": "next dev", @@ -11,38 +11,13 @@ "start": "next start", "lint": "next lint", "sync-users": "node scripts/sync-users.js", - "electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:3000 && electron electron/main.js\"", - "electron:build": "node electron/build.js", - "electron:start": "electron electron/main.js" - }, - "build": { - "appId": "com.neah.app", - "productName": "Neah", - "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" - ] - } + "migrate:dev": "prisma migrate dev", + "migrate:deploy": "prisma migrate deploy", + "migrate:status": "prisma migrate status", + "migrate:prod": "bash scripts/migrate-prod.sh", + "verify:config": "bash scripts/verify-vercel-config.sh", + "db:studio": "prisma studio", + "db:generate": "prisma generate" }, "dependencies": { "@aws-sdk/client-s3": "^3.802.0", @@ -150,9 +125,6 @@ "buffer": "^6.0.3", "concurrently": "^9.1.2", "cross-env": "^7.0.3", - "electron": "^36.1.0", - "electron-builder": "^26.0.12", - "electron-serve": "^2.1.1", "postcss": "^8", "prisma": "^6.4.1", "stream-browserify": "^3.0.0", diff --git a/scripts/migrate-prod.sh b/scripts/migrate-prod.sh new file mode 100755 index 0000000..cef7875 --- /dev/null +++ b/scripts/migrate-prod.sh @@ -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}" diff --git a/scripts/verify-vercel-config.sh b/scripts/verify-vercel-config.sh new file mode 100755 index 0000000..e9da0a2 --- /dev/null +++ b/scripts/verify-vercel-config.sh @@ -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 diff --git a/types/electron.d.ts b/types/electron.d.ts deleted file mode 100644 index 1ef41b4..0000000 --- a/types/electron.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -interface ElectronWindowControl { - minimize: () => Promise; - maximize: () => Promise; - close: () => Promise; -} - -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; - maximize: () => Promise; - close: () => Promise; - }; - windowState: { - onMaximized: (callback: () => void) => void; - onUnmaximized: (callback: () => void) => void; - removeMaximizedListener: () => void; - removeUnmaximizedListener: () => void; - }; - appInfo: { - isElectron: boolean; - version: string; - }; -} - -declare global { - interface Window { - electron?: ElectronAPI; - } -} \ No newline at end of file diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..cbf2dd6 --- /dev/null +++ b/vercel.json @@ -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*" + } + ] +}