Files
towercontrol/services/system-info.ts
Julien Froidefond 3c20df95d9 feat: add system info and backup functionalities to settings page
- Integrated system info fetching in `SettingsPage` for improved user insights.
- Enhanced `SettingsIndexPageClient` with manual backup creation and Jira connection testing features.
- Added loading states and auto-dismiss messages for user feedback during actions.
- Updated UI to display system info and backup statistics dynamically.
2025-09-20 16:38:33 +02:00

192 lines
5.4 KiB
TypeScript

import { prisma } from './database';
import { readFile } from 'fs/promises';
import { join } from 'path';
export interface SystemInfo {
version: string;
environment: string;
database: {
totalTasks: number;
totalUsers: number;
totalBackups: number;
databaseSize: string;
};
uptime: string;
lastUpdate: string;
}
export class SystemInfoService {
/**
* Récupère les informations système complètes
*/
static async getSystemInfo(): Promise<SystemInfo> {
try {
const [packageInfo, dbStats] = await Promise.all([
this.getPackageInfo(),
this.getDatabaseStats()
]);
return {
version: packageInfo.version,
environment: process.env.NODE_ENV || 'development',
database: dbStats,
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] = await Promise.all([
prisma.task.count(),
prisma.userPreferences.count(),
// Pour les backups, on compte les fichiers via le service backup
this.getBackupCount()
]);
return {
totalTasks,
totalUsers,
totalBackups,
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<number> {
try {
// Import dynamique pour éviter les dépendances circulaires
const { backupService } = await import('./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<string> {
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');
const packagePath = join(process.cwd(), 'package.json');
const stats = fs.statSync(packagePath);
const now = new Date();
const lastModified = new Date(stats.mtime);
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';
}
}
}