feat: refactor service organization and update imports
- Introduced a new structure for services in `src/services/` to improve organization by domain, including core, analytics, data management, integrations, and task management. - Moved relevant files to their new locations and updated all internal and external imports accordingly. - Updated `TODO.md` to reflect the new service organization and outlined phases for further refactoring.
This commit is contained in:
220
src/services/core/system-info.ts
Normal file
220
src/services/core/system-info.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
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<SystemInfo> {
|
||||
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<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'); // 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user