import { prisma } from './database'; import { readFile } from 'fs/promises'; import { join } from 'path'; import { getToday, parseDate } from '@/lib/date-utils'; export interface SystemInfo { version: string; environment: string; database: { totalTasks: number; totalUsers: number; totalBackups: number; databaseSize: string; totalTags: number; // Ajout pour compatibilité totalDailies: number; // Ajout pour compatibilité size: string; // Alias pour databaseSize }; backups: { totalBackups: number; lastBackup?: string; }; app: { version: string; environment: string; }; uptime: string; lastUpdate: string; } export class SystemInfoService { /** * Récupère les informations système complètes */ static async getSystemInfo(): Promise { try { const [packageInfo, dbStats] = await Promise.all([ this.getPackageInfo(), this.getDatabaseStats() ]); return { version: packageInfo.version, environment: process.env.NODE_ENV || 'development', database: { ...dbStats, totalTags: dbStats.totalTags || 0, totalDailies: dbStats.totalDailies || 0, size: dbStats.databaseSize }, backups: { totalBackups: dbStats.totalBackups, lastBackup: undefined // TODO: Implement backup tracking }, app: { version: packageInfo.version, environment: process.env.NODE_ENV || 'development' }, uptime: this.getUptime(), lastUpdate: this.getLastUpdate() }; } catch (error) { console.error('Error getting system info:', error); throw new Error('Failed to get system information'); } } /** * Lit les informations du package.json */ private static async getPackageInfo(): Promise<{ version: string; name: string }> { try { const packagePath = join(process.cwd(), 'package.json'); const packageContent = await readFile(packagePath, 'utf-8'); const packageJson = JSON.parse(packageContent); return { name: packageJson.name || 'TowerControl', version: packageJson.version || '1.0.0' }; } catch (error) { console.error('Error reading package.json:', error); return { name: 'TowerControl', version: '1.0.0' }; } } /** * Récupère les statistiques de la base de données */ private static async getDatabaseStats() { try { const [totalTasks, totalUsers, totalBackups, totalTags, totalDailies] = await Promise.all([ prisma.task.count(), prisma.userPreferences.count(), // Pour les backups, on compte les fichiers via le service backup this.getBackupCount(), prisma.tag.count(), prisma.dailyCheckbox.count() ]); return { totalTasks, totalUsers, totalBackups, totalTags, totalDailies, databaseSize: await this.getDatabaseSize() }; } catch (error) { console.error('Error getting database stats:', error); return { totalTasks: 0, totalUsers: 0, totalBackups: 0, databaseSize: 'N/A' }; } } /** * Compte le nombre de sauvegardes */ private static async getBackupCount(): Promise { try { // Import dynamique pour éviter les dépendances circulaires const { backupService } = await import('../data-management/backup'); const backups = await backupService.listBackups(); return backups.length; } catch (error) { console.error('Error counting backups:', error); return 0; } } /** * Estime la taille de la base de données */ private static async getDatabaseSize(): Promise { try { const { stat } = await import('fs/promises'); const { resolve } = await import('path'); // Utiliser la même logique que le service de backup pour trouver la DB let dbPath: string; if (process.env.BACKUP_DATABASE_PATH) { dbPath = resolve(process.cwd(), process.env.BACKUP_DATABASE_PATH); } else if (process.env.DATABASE_URL) { dbPath = resolve(process.env.DATABASE_URL.replace('file:', '')); } else { dbPath = resolve(process.cwd(), 'prisma', 'dev.db'); } const stats = await stat(dbPath); // Convertir en format lisible const bytes = stats.size; if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } catch (error) { console.error('Error getting database size:', error); return 'N/A'; } } /** * Calcule l'uptime du processus */ private static getUptime(): string { const uptime = process.uptime(); const hours = Math.floor(uptime / 3600); const minutes = Math.floor((uptime % 3600) / 60); const seconds = Math.floor(uptime % 60); if (hours > 0) { return `${hours}h ${minutes}m`; } else if (minutes > 0) { return `${minutes}m ${seconds}s`; } else { return `${seconds}s`; } } /** * Retourne une date de dernière mise à jour fictive * (dans un vrai projet, cela viendrait d'un système de déploiement) */ private static getLastUpdate(): string { // Pour l'instant, on utilise la date de modification du package.json try { const fs = require('fs'); // eslint-disable-line @typescript-eslint/no-require-imports const packagePath = join(process.cwd(), 'package.json'); const stats = fs.statSync(packagePath); const now = getToday(); const lastModified = parseDate(stats.mtime.toISOString()); const diffTime = Math.abs(now.getTime() - lastModified.getTime()); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); if (diffDays === 1) { return 'Il y a 1 jour'; } else if (diffDays < 7) { return `Il y a ${diffDays} jours`; } else if (diffDays < 30) { const weeks = Math.floor(diffDays / 7); return `Il y a ${weeks} semaine${weeks > 1 ? 's' : ''}`; } else { const months = Math.floor(diffDays / 30); return `Il y a ${months} mois`; } } catch { return 'Il y a 2 jours'; } } }