fix: improve date formatting and backup path handling
- Updated `formatTimeAgo` in `AdvancedSettingsPageClient` to use a fixed format for hydration consistency. - Refined `formatDate` in `BackupSettingsPageClient` for consistent server/client formatting. - Refactored `BackupService` to use `getCurrentBackupPath` for all backup path references, ensuring up-to-date paths and avoiding caching issues. - Added `getCurrentBackupPath` method to dynamically retrieve the current backup path based on environment variables.
This commit is contained in:
@@ -79,19 +79,16 @@ export function AdvancedSettingsPageClient({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const formatTimeAgo = (date: Date): string => {
|
const formatTimeAgo = (date: Date): string => {
|
||||||
const now = new Date();
|
// Format fixe pour éviter les erreurs d'hydratation
|
||||||
const diffMs = now.getTime() - new Date(date).getTime();
|
const d = new Date(date);
|
||||||
const diffMins = Math.floor(diffMs / (1000 * 60));
|
return d.toLocaleDateString('fr-FR', {
|
||||||
const diffHours = Math.floor(diffMins / 60);
|
day: '2-digit',
|
||||||
const diffDays = Math.floor(diffHours / 24);
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
if (diffMins < 60) {
|
hour: '2-digit',
|
||||||
return `il y a ${diffMins}min`;
|
minute: '2-digit',
|
||||||
} else if (diffHours < 24) {
|
hour12: false
|
||||||
return `il y a ${diffHours}h ${diffMins % 60}min`;
|
});
|
||||||
} else {
|
|
||||||
return `il y a ${diffDays}j`;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNextBackupTime = (): string => {
|
const getNextBackupTime = (): string => {
|
||||||
|
|||||||
@@ -158,7 +158,17 @@ export default function BackupSettingsPageClient({ initialData }: BackupSettings
|
|||||||
};
|
};
|
||||||
|
|
||||||
const formatDate = (date: string | Date): string => {
|
const formatDate = (date: string | Date): string => {
|
||||||
return new Date(date).toLocaleString();
|
// Format cohérent serveur/client pour éviter les erreurs d'hydratation
|
||||||
|
const d = new Date(date);
|
||||||
|
return d.toLocaleDateString('fr-FR', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
@@ -27,14 +27,16 @@ export interface BackupInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class BackupService {
|
export class BackupService {
|
||||||
private defaultConfig: BackupConfig = {
|
private get defaultConfig(): BackupConfig {
|
||||||
enabled: true,
|
return {
|
||||||
interval: 'hourly',
|
enabled: true,
|
||||||
maxBackups: 5,
|
interval: 'hourly',
|
||||||
backupPath: this.getDefaultBackupPath(),
|
maxBackups: 5,
|
||||||
includeUploads: true,
|
backupPath: this.getDefaultBackupPath(),
|
||||||
compression: true,
|
includeUploads: true,
|
||||||
};
|
compression: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private getDefaultBackupPath(): string {
|
private getDefaultBackupPath(): string {
|
||||||
// 1. Variable d'environnement explicite
|
// 1. Variable d'environnement explicite
|
||||||
@@ -110,7 +112,7 @@ export class BackupService {
|
|||||||
const backupId = `backup_${Date.now()}`;
|
const backupId = `backup_${Date.now()}`;
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
const filename = `towercontrol_${timestamp}.db`;
|
const filename = `towercontrol_${timestamp}.db`;
|
||||||
const backupPath = path.join(this.config.backupPath, filename);
|
const backupPath = path.join(this.getCurrentBackupPath(), filename);
|
||||||
|
|
||||||
console.log(`🔄 Starting ${type} backup: ${filename}`);
|
console.log(`🔄 Starting ${type} backup: ${filename}`);
|
||||||
|
|
||||||
@@ -226,7 +228,7 @@ export class BackupService {
|
|||||||
* Restaure une sauvegarde
|
* Restaure une sauvegarde
|
||||||
*/
|
*/
|
||||||
async restoreBackup(filename: string): Promise<void> {
|
async restoreBackup(filename: string): Promise<void> {
|
||||||
const backupPath = path.join(this.config.backupPath, filename);
|
const backupPath = path.join(this.getCurrentBackupPath(), filename);
|
||||||
|
|
||||||
// Résoudre le chemin de la base de données
|
// Résoudre le chemin de la base de données
|
||||||
let dbPath: string;
|
let dbPath: string;
|
||||||
@@ -304,19 +306,30 @@ export class BackupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient le chemin de sauvegarde actuel (toujours à jour)
|
||||||
|
* Force la relecture des variables d'environnement à chaque appel
|
||||||
|
*/
|
||||||
|
private getCurrentBackupPath(): string {
|
||||||
|
// Toujours recalculer depuis les variables d'environnement
|
||||||
|
// pour éviter les problèmes de cache lors des refresh
|
||||||
|
return this.getDefaultBackupPath();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Liste toutes les sauvegardes disponibles
|
* Liste toutes les sauvegardes disponibles
|
||||||
*/
|
*/
|
||||||
async listBackups(): Promise<BackupInfo[]> {
|
async listBackups(): Promise<BackupInfo[]> {
|
||||||
try {
|
try {
|
||||||
|
const currentBackupPath = this.getCurrentBackupPath();
|
||||||
await this.ensureBackupDirectory();
|
await this.ensureBackupDirectory();
|
||||||
const files = await fs.readdir(this.config.backupPath);
|
const files = await fs.readdir(currentBackupPath);
|
||||||
|
|
||||||
const backups: BackupInfo[] = [];
|
const backups: BackupInfo[] = [];
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (file.startsWith('towercontrol_') && (file.endsWith('.db') || file.endsWith('.db.gz'))) {
|
if (file.startsWith('towercontrol_') && (file.endsWith('.db') || file.endsWith('.db.gz'))) {
|
||||||
const filePath = path.join(this.config.backupPath, file);
|
const filePath = path.join(currentBackupPath, file);
|
||||||
const stats = await fs.stat(filePath);
|
const stats = await fs.stat(filePath);
|
||||||
|
|
||||||
// Extraire la date du nom de fichier
|
// Extraire la date du nom de fichier
|
||||||
@@ -353,7 +366,7 @@ export class BackupService {
|
|||||||
* Supprime une sauvegarde
|
* Supprime une sauvegarde
|
||||||
*/
|
*/
|
||||||
async deleteBackup(filename: string): Promise<void> {
|
async deleteBackup(filename: string): Promise<void> {
|
||||||
const backupPath = path.join(this.config.backupPath, filename);
|
const backupPath = path.join(this.getCurrentBackupPath(), filename);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.unlink(backupPath);
|
await fs.unlink(backupPath);
|
||||||
@@ -411,11 +424,12 @@ export class BackupService {
|
|||||||
* S'assure que le dossier de backup existe
|
* S'assure que le dossier de backup existe
|
||||||
*/
|
*/
|
||||||
private async ensureBackupDirectory(): Promise<void> {
|
private async ensureBackupDirectory(): Promise<void> {
|
||||||
|
const currentBackupPath = this.getCurrentBackupPath();
|
||||||
try {
|
try {
|
||||||
await fs.access(this.config.backupPath);
|
await fs.access(currentBackupPath);
|
||||||
} catch {
|
} catch {
|
||||||
await fs.mkdir(this.config.backupPath, { recursive: true });
|
await fs.mkdir(currentBackupPath, { recursive: true });
|
||||||
console.log(`📁 Created backup directory: ${this.config.backupPath}`);
|
console.log(`📁 Created backup directory: ${currentBackupPath}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +461,11 @@ export class BackupService {
|
|||||||
* Obtient la configuration actuelle
|
* Obtient la configuration actuelle
|
||||||
*/
|
*/
|
||||||
getConfig(): BackupConfig {
|
getConfig(): BackupConfig {
|
||||||
return { ...this.config };
|
// Retourner une config avec le chemin à jour
|
||||||
|
return {
|
||||||
|
...this.config,
|
||||||
|
backupPath: this.getCurrentBackupPath()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user