vako 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/README.md +1944 -0
- package/bin/commands/quick-setup.js +111 -0
- package/bin/commands/setup-executor.js +203 -0
- package/bin/commands/setup.js +737 -0
- package/bin/create-veko-app.js +75 -0
- package/bin/veko-update.js +205 -0
- package/bin/veko.js +188 -0
- package/error/error.ejs +382 -0
- package/index.js +36 -0
- package/lib/adapters/nextjs-adapter.js +241 -0
- package/lib/app.js +749 -0
- package/lib/core/auth-manager.js +1353 -0
- package/lib/core/auto-updater.js +1118 -0
- package/lib/core/logger.js +97 -0
- package/lib/core/module-installer.js +86 -0
- package/lib/dev/dev-server.js +292 -0
- package/lib/layout/layout-manager.js +834 -0
- package/lib/plugin-manager.js +1795 -0
- package/lib/routing/route-manager.js +1000 -0
- package/package.json +231 -0
- package/templates/public/css/style.css +2 -0
- package/templates/public/js/main.js +1 -0
- package/tsconfig.json +50 -0
- package/types/index.d.ts +238 -0
|
@@ -0,0 +1,1118 @@
|
|
|
1
|
+
// Fichier de l'auto-updater qui va vérifier si c'est la bonne version de veko
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const https = require('https');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const { execSync, spawn } = require('child_process');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
|
|
10
|
+
class AutoUpdater {
|
|
11
|
+
static packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
12
|
+
static backupDir = path.join(process.cwd(), '.veko-backups');
|
|
13
|
+
static configPath = path.join(process.cwd(), '.veko-updater.json');
|
|
14
|
+
static logPath = path.join(process.cwd(), '.veko-updater.log');
|
|
15
|
+
static currentVersion = null;
|
|
16
|
+
static latestVersion = null;
|
|
17
|
+
static config = {};
|
|
18
|
+
static stats = {
|
|
19
|
+
totalUpdates: 0,
|
|
20
|
+
lastUpdate: null,
|
|
21
|
+
lastCheck: null,
|
|
22
|
+
rollbacks: 0
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// 🎨 Styles visuels simplifiés
|
|
26
|
+
static styles = {
|
|
27
|
+
title: chalk.bold.cyan,
|
|
28
|
+
success: chalk.bold.green,
|
|
29
|
+
error: chalk.bold.red,
|
|
30
|
+
warning: chalk.bold.yellow,
|
|
31
|
+
info: chalk.bold.blue,
|
|
32
|
+
dim: chalk.dim.gray,
|
|
33
|
+
highlight: chalk.bold.white,
|
|
34
|
+
accent: chalk.magenta,
|
|
35
|
+
progress: chalk.green.bold,
|
|
36
|
+
version: chalk.cyan.bold,
|
|
37
|
+
menu: chalk.yellow.bold,
|
|
38
|
+
separator: chalk.dim('─'.repeat(60))
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// 🔧 Configuration par défaut
|
|
42
|
+
static defaultConfig = {
|
|
43
|
+
autoCheck: true,
|
|
44
|
+
autoUpdate: false,
|
|
45
|
+
checkInterval: 3600000, // 1 heure
|
|
46
|
+
backupCount: 5,
|
|
47
|
+
allowPrerelease: false,
|
|
48
|
+
allowBeta: false,
|
|
49
|
+
securityCheck: true,
|
|
50
|
+
progressBar: true,
|
|
51
|
+
notifications: true,
|
|
52
|
+
rollbackOnFailure: true,
|
|
53
|
+
updateChannel: 'stable', // stable, beta, alpha
|
|
54
|
+
customRegistry: null,
|
|
55
|
+
excludeFiles: ['.git', 'node_modules', '.veko-backups'],
|
|
56
|
+
skipDependencies: false
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// 🚀 Initialisation robuste
|
|
60
|
+
static async init() {
|
|
61
|
+
try {
|
|
62
|
+
await this.loadConfig();
|
|
63
|
+
await this.loadStats();
|
|
64
|
+
this.createDirectories();
|
|
65
|
+
|
|
66
|
+
if (this.config.autoCheck) {
|
|
67
|
+
this.scheduleAutoCheck();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return true;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(`[Auto-updater] Erreur d'initialisation: ${error.message}`);
|
|
73
|
+
return false; // Ne pas bloquer l'application en cas d'erreur
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 📁 Création des répertoires nécessaires avec gestion d'erreurs
|
|
78
|
+
static createDirectories() {
|
|
79
|
+
try {
|
|
80
|
+
[this.backupDir].forEach(dir => {
|
|
81
|
+
if (!fs.existsSync(dir)) {
|
|
82
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.warn(`[Auto-updater] Impossible de créer les répertoires: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ⚙️ Chargement de la configuration avec fallback
|
|
91
|
+
static async loadConfig() {
|
|
92
|
+
try {
|
|
93
|
+
if (fs.existsSync(this.configPath)) {
|
|
94
|
+
const configData = fs.readFileSync(this.configPath, 'utf8');
|
|
95
|
+
this.config = { ...this.defaultConfig, ...JSON.parse(configData) };
|
|
96
|
+
} else {
|
|
97
|
+
this.config = { ...this.defaultConfig };
|
|
98
|
+
await this.saveConfig();
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.warn(`[Auto-updater] Erreur de configuration: ${error.message}`);
|
|
102
|
+
this.config = { ...this.defaultConfig };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 💾 Sauvegarde de la configuration avec sécurité
|
|
107
|
+
static async saveConfig() {
|
|
108
|
+
try {
|
|
109
|
+
fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.warn(`[Auto-updater] Impossible de sauvegarder la configuration: ${error.message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 📊 Chargement des statistiques de manière sécurisée
|
|
116
|
+
static async loadStats() {
|
|
117
|
+
try {
|
|
118
|
+
if (fs.existsSync(this.packageJsonPath)) {
|
|
119
|
+
const packageJson = JSON.parse(fs.readFileSync(this.packageJsonPath, 'utf8'));
|
|
120
|
+
if (packageJson.vekoUpdaterStats) {
|
|
121
|
+
this.stats = { ...this.stats, ...packageJson.vekoUpdaterStats };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.warn(`[Auto-updater] Impossible de charger les statistiques: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 🔄 Programmation de la vérification automatique sécurisée
|
|
130
|
+
static scheduleAutoCheck() {
|
|
131
|
+
try {
|
|
132
|
+
setInterval(async () => {
|
|
133
|
+
try {
|
|
134
|
+
await this.checkForUpdates(true);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// Capture l'erreur pour ne pas arrêter le processus
|
|
137
|
+
console.error(`[Auto-updater] Erreur de vérification: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
}, this.config.checkInterval);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`[Auto-updater] Erreur de programmation: ${error.message}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 📊 Barre de progression
|
|
146
|
+
static showProgress(current, total, message = '') {
|
|
147
|
+
if (!this.config.progressBar) return;
|
|
148
|
+
|
|
149
|
+
const percentage = Math.round((current / total) * 100);
|
|
150
|
+
const barLength = 40;
|
|
151
|
+
const filledLength = Math.round(barLength * percentage / 100);
|
|
152
|
+
const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength);
|
|
153
|
+
|
|
154
|
+
process.stdout.write(`\r${this.styles.progress(bar)} ${percentage}% ${message}`);
|
|
155
|
+
|
|
156
|
+
if (current === total) {
|
|
157
|
+
console.log(''); // Nouvelle ligne à la fin
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 🎯 Animation de chargement
|
|
162
|
+
static loadingAnimation(message) {
|
|
163
|
+
if (!process.stdout.isTTY) return { stop: () => {} };
|
|
164
|
+
|
|
165
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
166
|
+
let i = 0;
|
|
167
|
+
const loader = setInterval(() => {
|
|
168
|
+
process.stdout.write(`\r${this.styles.info(frames[i++ % frames.length])} ${message}`);
|
|
169
|
+
}, 80);
|
|
170
|
+
|
|
171
|
+
// Retourne une fonction pour arrêter l'animation
|
|
172
|
+
return {
|
|
173
|
+
stop: (finalMessage = '') => {
|
|
174
|
+
clearInterval(loader);
|
|
175
|
+
process.stdout.write(`\r${' '.repeat(message.length + 10)}\r`);
|
|
176
|
+
if (finalMessage) {
|
|
177
|
+
console.log(finalMessage);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 🔍 Vérification de mise à jour avec timeout et animation
|
|
184
|
+
static async checkForUpdates(silent = false) {
|
|
185
|
+
try {
|
|
186
|
+
// Animation si pas en mode silencieux
|
|
187
|
+
const animation = !silent ?
|
|
188
|
+
this.loadingAnimation('Vérification des mises à jour...') :
|
|
189
|
+
{ stop: () => {} };
|
|
190
|
+
|
|
191
|
+
this.stats.lastCheck = new Date().toISOString();
|
|
192
|
+
|
|
193
|
+
const currentVersion = this.getCurrentVersion();
|
|
194
|
+
if (!currentVersion) {
|
|
195
|
+
animation.stop(!silent ?
|
|
196
|
+
this.styles.warning('⚠️ Veko n\'est pas installé.') : '');
|
|
197
|
+
return { hasUpdate: false, needsInstall: true };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Timeout pour éviter les boucles infinies
|
|
201
|
+
const versionInfoPromise = Promise.race([
|
|
202
|
+
this.getVersionInfo(),
|
|
203
|
+
new Promise((_, reject) =>
|
|
204
|
+
setTimeout(() => reject(new Error('Timeout lors de la vérification')), 5000)
|
|
205
|
+
)
|
|
206
|
+
]);
|
|
207
|
+
|
|
208
|
+
const versionInfo = await versionInfoPromise;
|
|
209
|
+
|
|
210
|
+
if (!versionInfo) {
|
|
211
|
+
animation.stop(!silent ?
|
|
212
|
+
this.styles.error('❌ Impossible de récupérer les informations de version') : '');
|
|
213
|
+
throw new Error('Impossible de récupérer les informations de version');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const comparison = this.compareVersions(currentVersion, versionInfo.latest);
|
|
217
|
+
|
|
218
|
+
if (comparison < 0) {
|
|
219
|
+
animation.stop(!silent ?
|
|
220
|
+
this.styles.warning(`⚠️ Nouvelle version disponible! ${currentVersion} → ${versionInfo.latest}`) : '');
|
|
221
|
+
|
|
222
|
+
if (!silent) {
|
|
223
|
+
console.log(this.styles.info(` Actuelle: ${this.styles.version(currentVersion)}`));
|
|
224
|
+
console.log(this.styles.info(` Dernière: ${this.styles.version(versionInfo.latest)}`));
|
|
225
|
+
|
|
226
|
+
if (versionInfo.changelog) {
|
|
227
|
+
console.log(this.styles.info('\n📝 Notes de mise à jour:'));
|
|
228
|
+
console.log(this.styles.dim(`${versionInfo.changelog.substring(0, 500)}...`));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
hasUpdate: true,
|
|
234
|
+
currentVersion,
|
|
235
|
+
latestVersion: versionInfo.latest,
|
|
236
|
+
changelog: versionInfo.changelog,
|
|
237
|
+
security: versionInfo.security
|
|
238
|
+
};
|
|
239
|
+
} else {
|
|
240
|
+
animation.stop(!silent ?
|
|
241
|
+
this.styles.success(`✅ Version à jour (${currentVersion})`) : '');
|
|
242
|
+
return { hasUpdate: false, currentVersion };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (!silent) {
|
|
247
|
+
console.log(this.styles.error(`❌ ${error.message}`));
|
|
248
|
+
}
|
|
249
|
+
this.logError(`Erreur lors de la vérification: ${error.message}`);
|
|
250
|
+
return { hasUpdate: false, error: error.message };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 🔐 Vérification de sécurité et intégrité
|
|
255
|
+
static async verifyPackageIntegrity(packagePath, expectedIntegrity) {
|
|
256
|
+
if (!this.config.securityCheck || !expectedIntegrity) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const fileBuffer = fs.readFileSync(packagePath);
|
|
262
|
+
const hash = crypto.createHash('sha512').update(fileBuffer).digest('base64');
|
|
263
|
+
const calculatedIntegrity = `sha512-${hash}`;
|
|
264
|
+
|
|
265
|
+
return calculatedIntegrity === expectedIntegrity;
|
|
266
|
+
} catch (error) {
|
|
267
|
+
this.log('error', `Erreur lors de la vérification d'intégrité: ${error.message}`);
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// 💾 Système de backup amélioré
|
|
273
|
+
static async createBackup() {
|
|
274
|
+
try {
|
|
275
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
276
|
+
const backupPath = path.join(this.backupDir, `backup-${timestamp}`);
|
|
277
|
+
|
|
278
|
+
console.log(this.styles.info('💾 Création du backup...'));
|
|
279
|
+
|
|
280
|
+
// Copie les fichiers essentiels
|
|
281
|
+
const filesToBackup = [
|
|
282
|
+
'package.json',
|
|
283
|
+
'package-lock.json',
|
|
284
|
+
'yarn.lock',
|
|
285
|
+
'node_modules/veko'
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
fs.mkdirSync(backupPath, { recursive: true });
|
|
289
|
+
|
|
290
|
+
for (let i = 0; i < filesToBackup.length; i++) {
|
|
291
|
+
const file = filesToBackup[i];
|
|
292
|
+
const sourcePath = path.join(process.cwd(), file);
|
|
293
|
+
const destPath = path.join(backupPath, file);
|
|
294
|
+
|
|
295
|
+
if (fs.existsSync(sourcePath)) {
|
|
296
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
297
|
+
|
|
298
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
299
|
+
await this.copyDirectory(sourcePath, destPath);
|
|
300
|
+
} else {
|
|
301
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this.showProgress(i + 1, filesToBackup.length, 'Backup en cours...');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Nettoyage des anciens backups
|
|
309
|
+
this.cleanupOldBackups();
|
|
310
|
+
|
|
311
|
+
console.log(this.styles.success(`✅ Backup créé: ${backupPath}`));
|
|
312
|
+
return backupPath;
|
|
313
|
+
|
|
314
|
+
} catch (error) {
|
|
315
|
+
this.log('error', `Erreur lors de la création du backup: ${error.message}`);
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 📁 Copie récursive de répertoires
|
|
321
|
+
static async copyDirectory(source, destination) {
|
|
322
|
+
if (!fs.existsSync(destination)) {
|
|
323
|
+
fs.mkdirSync(destination, { recursive: true });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const items = fs.readdirSync(source);
|
|
327
|
+
|
|
328
|
+
for (const item of items) {
|
|
329
|
+
const sourcePath = path.join(source, item);
|
|
330
|
+
const destPath = path.join(destination, item);
|
|
331
|
+
|
|
332
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
333
|
+
await this.copyDirectory(sourcePath, destPath);
|
|
334
|
+
} else {
|
|
335
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// 🧹 Nettoyage des anciens backups
|
|
341
|
+
static cleanupOldBackups() {
|
|
342
|
+
try {
|
|
343
|
+
const backups = fs.readdirSync(this.backupDir)
|
|
344
|
+
.filter(dir => dir.startsWith('backup-'))
|
|
345
|
+
.map(dir => ({
|
|
346
|
+
name: dir,
|
|
347
|
+
path: path.join(this.backupDir, dir),
|
|
348
|
+
mtime: fs.statSync(path.join(this.backupDir, dir)).mtime
|
|
349
|
+
}))
|
|
350
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
351
|
+
|
|
352
|
+
if (backups.length > this.config.backupCount) {
|
|
353
|
+
const toDelete = backups.slice(this.config.backupCount);
|
|
354
|
+
toDelete.forEach(backup => {
|
|
355
|
+
fs.rmSync(backup.path, { recursive: true, force: true });
|
|
356
|
+
this.log('info', `Backup supprimé: ${backup.name}`);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
this.log('error', `Erreur lors du nettoyage des backups: ${error.message}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// 🔄 Rollback vers un backup spécifié
|
|
365
|
+
static async rollback(backupPath) {
|
|
366
|
+
try {
|
|
367
|
+
// Si le chemin n'est pas spécifié, utiliser le plus récent
|
|
368
|
+
if (!backupPath) {
|
|
369
|
+
const backups = fs.readdirSync(this.backupDir)
|
|
370
|
+
.filter(dir => dir.startsWith('backup-'))
|
|
371
|
+
.map(dir => path.join(this.backupDir, dir))
|
|
372
|
+
.sort((a, b) =>
|
|
373
|
+
fs.statSync(b).mtime.getTime() - fs.statSync(a).mtime.getTime()
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
if (backups.length === 0) {
|
|
377
|
+
throw new Error('Aucun backup disponible');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
backupPath = backups[0];
|
|
381
|
+
console.log(this.styles.info(`Utilisation du backup le plus récent: ${path.basename(backupPath)}`));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!fs.existsSync(backupPath)) {
|
|
385
|
+
throw new Error(`Backup non trouvé: ${backupPath}`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
console.log(this.styles.info('🔄 Restauration en cours...'));
|
|
389
|
+
|
|
390
|
+
const backupFiles = fs.readdirSync(backupPath);
|
|
391
|
+
|
|
392
|
+
for (let i = 0; i < backupFiles.length; i++) {
|
|
393
|
+
const file = backupFiles[i];
|
|
394
|
+
const sourcePath = path.join(backupPath, file);
|
|
395
|
+
const destPath = path.join(process.cwd(), file);
|
|
396
|
+
|
|
397
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
398
|
+
if (fs.existsSync(destPath)) {
|
|
399
|
+
fs.rmSync(destPath, { recursive: true, force: true });
|
|
400
|
+
}
|
|
401
|
+
await this.copyDirectory(sourcePath, destPath);
|
|
402
|
+
} else {
|
|
403
|
+
fs.copyFileSync(sourcePath, destPath);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
this.showProgress(i + 1, backupFiles.length, 'Restauration...');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
this.stats.rollbacks++;
|
|
410
|
+
await this.saveStats();
|
|
411
|
+
|
|
412
|
+
console.log(this.styles.success('✅ Rollback effectué avec succès!'));
|
|
413
|
+
return true;
|
|
414
|
+
|
|
415
|
+
} catch (error) {
|
|
416
|
+
this.log('error', `Erreur lors du rollback: ${error.message}`);
|
|
417
|
+
console.log(this.styles.error(`❌ ${error.message}`));
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 🚀 Mise à jour améliorée avec détection de npm
|
|
423
|
+
static async performUpdate(versionInfo) {
|
|
424
|
+
let backupPath = null;
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
// Création du backup
|
|
428
|
+
backupPath = await this.createBackup();
|
|
429
|
+
|
|
430
|
+
console.log(this.styles.info('🚀 Mise à jour en cours...'));
|
|
431
|
+
|
|
432
|
+
// Trouver le chemin npm correct selon la plateforme
|
|
433
|
+
const isWindows = process.platform === 'win32';
|
|
434
|
+
const npmCommand = isWindows ? 'npm.cmd' : 'npm';
|
|
435
|
+
|
|
436
|
+
// Désinstallation de l'ancienne version
|
|
437
|
+
console.log(this.styles.info('📦 Désinstallation de l\'ancienne version...'));
|
|
438
|
+
try {
|
|
439
|
+
execSync(`${npmCommand} uninstall veko`, { stdio: 'pipe' });
|
|
440
|
+
} catch (error) {
|
|
441
|
+
// Si echec, essayer avec npx
|
|
442
|
+
console.log(this.styles.warning('⚠️ Tentative alternative avec npx...'));
|
|
443
|
+
execSync(`${isWindows ? 'npx.cmd' : 'npx'} -y npm uninstall veko`, { stdio: 'pipe' });
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Installation de la nouvelle version
|
|
447
|
+
console.log(this.styles.info(`📦 Installation de veko@${versionInfo.latestVersion}...`));
|
|
448
|
+
|
|
449
|
+
// Utiliser le chemin complet vers npm si disponible
|
|
450
|
+
const installProcess = spawn(npmCommand, ['install', `veko@${versionInfo.latestVersion}`], {
|
|
451
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
452
|
+
shell: true // Utiliser un shell pour une meilleure compatibilité
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
let installOutput = '';
|
|
456
|
+
installProcess.stdout.on('data', (data) => {
|
|
457
|
+
installOutput += data.toString();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
installProcess.stderr.on('data', (data) => {
|
|
461
|
+
installOutput += data.toString();
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
await new Promise((resolve, reject) => {
|
|
465
|
+
installProcess.on('close', (code) => {
|
|
466
|
+
if (code === 0) {
|
|
467
|
+
resolve();
|
|
468
|
+
} else {
|
|
469
|
+
reject(new Error(`Installation échouée avec le code ${code}: ${installOutput}`));
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
installProcess.on('error', (err) => {
|
|
474
|
+
// Capturer les erreurs de spawn
|
|
475
|
+
reject(new Error(`Erreur lors du lancement du processus npm: ${err.message}`));
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Vérification post-installation
|
|
480
|
+
const newVersion = this.getCurrentVersion();
|
|
481
|
+
if (newVersion !== versionInfo.latestVersion) {
|
|
482
|
+
throw new Error('La version installée ne correspond pas à la version attendue');
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Mise à jour des statistiques
|
|
486
|
+
this.stats.totalUpdates++;
|
|
487
|
+
this.stats.lastUpdate = new Date().toISOString();
|
|
488
|
+
await this.saveStats();
|
|
489
|
+
|
|
490
|
+
console.log(this.styles.success(`✅ Mise à jour réussie vers la version ${versionInfo.latestVersion}!`));
|
|
491
|
+
|
|
492
|
+
if (this.config.notifications) {
|
|
493
|
+
this.showNotification('Veko mis à jour avec succès!', `Version ${versionInfo.latestVersion}`);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return true;
|
|
497
|
+
|
|
498
|
+
} catch (error) {
|
|
499
|
+
this.log('error', `Erreur lors de la mise à jour: ${error.message}`);
|
|
500
|
+
console.log(this.styles.error(`❌ Erreur: ${error.message}`));
|
|
501
|
+
|
|
502
|
+
if (this.config.rollbackOnFailure && backupPath) {
|
|
503
|
+
console.log(this.styles.warning('🔄 Rollback automatique...'));
|
|
504
|
+
await this.rollback(backupPath);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// 🔔 Notification système
|
|
512
|
+
static showNotification(title, message) {
|
|
513
|
+
try {
|
|
514
|
+
const platform = os.platform();
|
|
515
|
+
|
|
516
|
+
if (platform === 'darwin') {
|
|
517
|
+
execSync(`osascript -e 'display notification "${message}" with title "${title}"'`);
|
|
518
|
+
} else if (platform === 'win32') {
|
|
519
|
+
// Windows notification (nécessite des outils supplémentaires)
|
|
520
|
+
console.log(this.styles.info(`🔔 ${title}: ${message}`));
|
|
521
|
+
} else if (platform === 'linux') {
|
|
522
|
+
execSync(`notify-send "${title}" "${message}"`);
|
|
523
|
+
}
|
|
524
|
+
} catch (error) {
|
|
525
|
+
// Ignore les erreurs de notification
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// 📊 Affichage des statistiques
|
|
530
|
+
static displayStats() {
|
|
531
|
+
console.log(this.styles.title('\n📊 Statistiques de l\'auto-updater'));
|
|
532
|
+
console.log(this.styles.separator);
|
|
533
|
+
console.log(this.styles.info(`Mises à jour totales: ${this.stats.totalUpdates}`));
|
|
534
|
+
console.log(this.styles.info(`Rollbacks effectués: ${this.stats.rollbacks}`));
|
|
535
|
+
console.log(this.styles.info(`Dernière vérification: ${this.stats.lastCheck || 'Jamais'}`));
|
|
536
|
+
console.log(this.styles.info(`Dernière mise à jour: ${this.stats.lastUpdate || 'Jamais'}`));
|
|
537
|
+
console.log(this.styles.info(`Version actuelle: ${this.getCurrentVersion() || 'Non installé'}`));
|
|
538
|
+
console.log(this.styles.info(`Canal de mise à jour: ${this.config.updateChannel}`));
|
|
539
|
+
console.log(this.styles.separator);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// ⚙️ Configuration de base
|
|
543
|
+
static async configureSettings(options = {}) {
|
|
544
|
+
try {
|
|
545
|
+
// Mise à jour des options de configuration avec les paramètres passés
|
|
546
|
+
if (options && typeof options === 'object') {
|
|
547
|
+
this.config = { ...this.config, ...options };
|
|
548
|
+
await this.saveConfig();
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
console.log(this.styles.title('\n⚙️ Configuration actuelle:'));
|
|
553
|
+
console.log(this.styles.separator);
|
|
554
|
+
console.log(this.styles.info(`Vérification auto: ${this.config.autoCheck ? '✅' : '❌'}`));
|
|
555
|
+
console.log(this.styles.info(`Mise à jour auto: ${this.config.autoUpdate ? '✅' : '❌'}`));
|
|
556
|
+
console.log(this.styles.info(`Canal: ${this.config.updateChannel}`));
|
|
557
|
+
console.log(this.styles.info(`Backups: ${this.config.backupCount}`));
|
|
558
|
+
console.log(this.styles.info(`Vérification sécurité: ${this.config.securityCheck ? '✅' : '❌'}`));
|
|
559
|
+
console.log(this.styles.info(`Notifications: ${this.config.notifications ? '✅' : '❌'}`));
|
|
560
|
+
console.log(this.styles.info(`Rollback auto: ${this.config.rollbackOnFailure ? '✅' : '❌'}`));
|
|
561
|
+
console.log(this.styles.separator);
|
|
562
|
+
|
|
563
|
+
return true;
|
|
564
|
+
} catch (error) {
|
|
565
|
+
console.log(this.styles.error(`❌ Erreur: ${error.message}`));
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// 🔌 Gestion des WebSocket avec sécurité améliorée
|
|
571
|
+
static async getVersionInfo() {
|
|
572
|
+
return new Promise((resolve, reject) => {
|
|
573
|
+
try {
|
|
574
|
+
const registry = this.config.customRegistry || 'registry.npmjs.org';
|
|
575
|
+
const options = {
|
|
576
|
+
hostname: registry,
|
|
577
|
+
path: '/veko',
|
|
578
|
+
method: 'GET',
|
|
579
|
+
headers: {
|
|
580
|
+
'User-Agent': `veko-auto-updater/2.0.0 (${os.platform()} ${os.arch()})`,
|
|
581
|
+
'Accept': 'application/json'
|
|
582
|
+
},
|
|
583
|
+
timeout: 5000 // Timeout explicite
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const req = https.request(options, (res) => {
|
|
587
|
+
let data = '';
|
|
588
|
+
|
|
589
|
+
res.on('data', (chunk) => {
|
|
590
|
+
// Limite la taille des données pour éviter les attaques DoS
|
|
591
|
+
if (data.length > 1000000) { // Limite à ~1MB
|
|
592
|
+
req.destroy();
|
|
593
|
+
reject(new Error('Réponse trop volumineuse'));
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
data += chunk;
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
res.on('end', () => {
|
|
600
|
+
if (res.statusCode !== 200) {
|
|
601
|
+
reject(new Error(`Erreur HTTP ${res.statusCode}`));
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
const packageInfo = JSON.parse(data);
|
|
607
|
+
const channel = this.config.updateChannel;
|
|
608
|
+
|
|
609
|
+
if (!packageInfo['dist-tags']) {
|
|
610
|
+
reject(new Error('Format de réponse invalide'));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
let version;
|
|
615
|
+
switch (channel) {
|
|
616
|
+
case 'beta':
|
|
617
|
+
version = packageInfo['dist-tags'].beta || packageInfo['dist-tags'].latest;
|
|
618
|
+
break;
|
|
619
|
+
case 'alpha':
|
|
620
|
+
version = packageInfo['dist-tags'].alpha || packageInfo['dist-tags'].beta || packageInfo['dist-tags'].latest;
|
|
621
|
+
break;
|
|
622
|
+
case 'stable':
|
|
623
|
+
default:
|
|
624
|
+
version = packageInfo['dist-tags'].latest;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (!version || !packageInfo.versions || !packageInfo.versions[version]) {
|
|
628
|
+
reject(new Error(`Version invalide: ${version}`));
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const versionInfo = packageInfo.versions[version];
|
|
633
|
+
|
|
634
|
+
resolve({
|
|
635
|
+
latest: version,
|
|
636
|
+
changelog: versionInfo?.changelog || (packageInfo.readme?.slice(0, 500) || 'Pas de notes de mise à jour disponibles'),
|
|
637
|
+
security: versionInfo?.security || false,
|
|
638
|
+
size: versionInfo?.dist?.unpackedSize,
|
|
639
|
+
integrity: versionInfo?.dist?.integrity,
|
|
640
|
+
publishDate: versionInfo?.time
|
|
641
|
+
});
|
|
642
|
+
} catch (error) {
|
|
643
|
+
reject(new Error(`Erreur lors du parsing: ${error.message}`));
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
// Gestion explicite des erreurs
|
|
649
|
+
req.on('error', (error) => {
|
|
650
|
+
reject(new Error(`Erreur de connexion: ${error.message}`));
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// Timeout manuels pour plus de contrôle
|
|
654
|
+
req.setTimeout(10000, () => {
|
|
655
|
+
req.destroy();
|
|
656
|
+
reject(new Error('Timeout de connexion'));
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
req.end();
|
|
660
|
+
} catch (error) {
|
|
661
|
+
reject(new Error(`Erreur lors de la requête: ${error.message}`));
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// ❓ Aide simplifiée
|
|
667
|
+
static showHelp() {
|
|
668
|
+
console.log(this.styles.title('\n❓ Aide - Veko Auto-Updater'));
|
|
669
|
+
console.log(this.styles.separator);
|
|
670
|
+
console.log('Commandes disponibles:');
|
|
671
|
+
console.log(' veko update check - Vérifier les mises à jour');
|
|
672
|
+
console.log(' veko update update - Mettre à jour maintenant');
|
|
673
|
+
console.log(' veko update config - Afficher la configuration');
|
|
674
|
+
console.log(' veko update rollback - Effectuer un rollback');
|
|
675
|
+
console.log(' veko update stats - Afficher les statistiques');
|
|
676
|
+
console.log(' veko update fix - Réparer l\'auto-updater');
|
|
677
|
+
console.log(' veko update help - Afficher l\'aide');
|
|
678
|
+
console.log(' veko update version - Afficher la version');
|
|
679
|
+
console.log(this.styles.separator);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// 🎯 Fonction principale améliorée
|
|
683
|
+
static async checkAndUpdate() {
|
|
684
|
+
try {
|
|
685
|
+
await this.init();
|
|
686
|
+
|
|
687
|
+
// Vérifier npm en avance
|
|
688
|
+
try {
|
|
689
|
+
await this.ensureNpm();
|
|
690
|
+
} catch (error) {
|
|
691
|
+
console.log(this.styles.error(`❌ ${error.message} - L'auto-updater a besoin de npm pour fonctionner.`));
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Animation de chargement
|
|
696
|
+
const animation = this.loadingAnimation('Vérification des mises à jour...');
|
|
697
|
+
|
|
698
|
+
// Vérification si package.json existe
|
|
699
|
+
if (!fs.existsSync(this.packageJsonPath)) {
|
|
700
|
+
animation.stop(this.styles.error('❌ Le fichier package.json est manquant.'));
|
|
701
|
+
console.log(this.styles.error('Un fichier package.json est nécessaire.'));
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Vérification des mises à jour avec timeout
|
|
706
|
+
const updateInfo = await Promise.race([
|
|
707
|
+
this.checkForUpdates(true),
|
|
708
|
+
new Promise((_, reject) =>
|
|
709
|
+
setTimeout(() => reject(new Error('Timeout lors de la vérification')), 5000)
|
|
710
|
+
)
|
|
711
|
+
]);
|
|
712
|
+
|
|
713
|
+
animation.stop();
|
|
714
|
+
|
|
715
|
+
if (updateInfo.needsInstall) {
|
|
716
|
+
console.log(this.styles.warning('⚠️ Veko n\'est pas installé. Installation en cours...'));
|
|
717
|
+
try {
|
|
718
|
+
execSync('npm install veko@latest', { stdio: 'inherit' });
|
|
719
|
+
console.log(this.styles.success('✅ Veko installé avec succès!'));
|
|
720
|
+
return true;
|
|
721
|
+
} catch (error) {
|
|
722
|
+
console.log(this.styles.error(`❌ Erreur lors de l'installation: ${error.message}`));
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (updateInfo.hasUpdate) {
|
|
728
|
+
console.log(this.styles.warning(`⚠️ Nouvelle version disponible! ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
|
|
729
|
+
if (this.config.autoUpdate) {
|
|
730
|
+
return await this.performUpdate(updateInfo);
|
|
731
|
+
} else {
|
|
732
|
+
console.log(this.styles.info('Pour mettre à jour: veko update update'));
|
|
733
|
+
}
|
|
734
|
+
} else if (updateInfo.error) {
|
|
735
|
+
console.log(this.styles.error(`❌ Erreur: ${updateInfo.error}`));
|
|
736
|
+
return false;
|
|
737
|
+
} else {
|
|
738
|
+
console.log(this.styles.success('✅ Veko est à jour!'));
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return true;
|
|
742
|
+
|
|
743
|
+
} catch (error) {
|
|
744
|
+
this.log('error', `Erreur inattendue: ${error.message}`);
|
|
745
|
+
console.log(this.styles.error(`❌ Erreur inattendue: ${error.message}`));
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// 🧪 Installation sécurisée de npm avec plusieurs méthodes
|
|
751
|
+
static async ensureNpm() {
|
|
752
|
+
const isWindows = process.platform === 'win32';
|
|
753
|
+
const npmCommands = [
|
|
754
|
+
isWindows ? 'npm.cmd' : 'npm',
|
|
755
|
+
isWindows ? 'npx.cmd' : 'npx',
|
|
756
|
+
'npm', // Essayer sans extension sur Windows aussi
|
|
757
|
+
path.join(process.execPath, '..', isWindows ? 'npm.cmd' : 'npm')
|
|
758
|
+
];
|
|
759
|
+
|
|
760
|
+
for (const cmd of npmCommands) {
|
|
761
|
+
try {
|
|
762
|
+
execSync(`${cmd} --version`, { stdio: 'pipe' });
|
|
763
|
+
return cmd; // Retourner la première commande qui fonctionne
|
|
764
|
+
} catch (e) {
|
|
765
|
+
// Continuer avec la commande suivante
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
throw new Error('npm introuvable sur le système');
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// 🚀 Commande de mise à jour spécifique améliorée
|
|
773
|
+
static async performUpdateCommand() {
|
|
774
|
+
try {
|
|
775
|
+
// Vérifier npm en avance
|
|
776
|
+
try {
|
|
777
|
+
await this.ensureNpm();
|
|
778
|
+
} catch (error) {
|
|
779
|
+
console.log(this.styles.error(`❌ ${error.message} - L'auto-updater a besoin de npm pour fonctionner.`));
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Vérifier les mises à jour
|
|
784
|
+
const updateInfo = await this.checkForUpdates(true);
|
|
785
|
+
|
|
786
|
+
if (updateInfo.hasUpdate) {
|
|
787
|
+
console.log(this.styles.warning(`⚠️ Mise à jour disponible: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`));
|
|
788
|
+
console.log(this.styles.info('🚀 Démarrage de la mise à jour...'));
|
|
789
|
+
|
|
790
|
+
return await this.performUpdate(updateInfo);
|
|
791
|
+
} else if (updateInfo.needsInstall) {
|
|
792
|
+
console.log(this.styles.warning('⚠️ Veko n\'est pas installé. Installation en cours...'));
|
|
793
|
+
|
|
794
|
+
try {
|
|
795
|
+
execSync('npm install veko@latest', { stdio: 'inherit' });
|
|
796
|
+
console.log(this.styles.success('✅ Veko installé avec succès!'));
|
|
797
|
+
return true;
|
|
798
|
+
} catch (error) {
|
|
799
|
+
console.log(this.styles.error(`❌ Erreur lors de l'installation: ${error.message}`));
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
console.log(this.styles.success('✅ Veko est déjà à jour!'));
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
} catch (error) {
|
|
807
|
+
console.log(this.styles.error(`❌ Erreur lors de la mise à jour: ${error.message}`));
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// 📋 Afficher version
|
|
813
|
+
static showVersion() {
|
|
814
|
+
const version = this.getCurrentVersion() || 'non installé';
|
|
815
|
+
console.log(`Veko v${version}`);
|
|
816
|
+
console.log(`Auto-updater v1.1.5`);
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// 🔧 Réparer l'installation
|
|
821
|
+
static async fixInstallation() {
|
|
822
|
+
console.log(this.styles.title('\n🔧 Réparation de l\'installation'));
|
|
823
|
+
console.log(this.styles.separator);
|
|
824
|
+
|
|
825
|
+
try {
|
|
826
|
+
// 1. Créer les répertoires nécessaires
|
|
827
|
+
console.log('1. Vérification des répertoires');
|
|
828
|
+
this.createDirectories();
|
|
829
|
+
console.log(this.styles.success('✅ Répertoires vérifiés'));
|
|
830
|
+
|
|
831
|
+
// 2. Réinitialiser la configuration
|
|
832
|
+
console.log('2. Réinitialisation de la configuration');
|
|
833
|
+
this.config = { ...this.defaultConfig };
|
|
834
|
+
await this.saveConfig();
|
|
835
|
+
console.log(this.styles.success('✅ Configuration réinitialisée'));
|
|
836
|
+
|
|
837
|
+
// 3. Vérifier package.json
|
|
838
|
+
console.log('3. Vérification de package.json');
|
|
839
|
+
if (!fs.existsSync(this.packageJsonPath)) {
|
|
840
|
+
console.log(this.styles.warning('⚠️ package.json manquant'));
|
|
841
|
+
console.log(this.styles.error('❌ Impossible de continuer sans package.json'));
|
|
842
|
+
return false;
|
|
843
|
+
} else {
|
|
844
|
+
console.log(this.styles.success('✅ package.json trouvé'));
|
|
845
|
+
|
|
846
|
+
// Vérifier l'installation de veko
|
|
847
|
+
const vekoInstalled = this.getCurrentVersion();
|
|
848
|
+
if (!vekoInstalled) {
|
|
849
|
+
console.log(this.styles.warning('⚠️ Veko non installé, tentative d\'installation'));
|
|
850
|
+
try {
|
|
851
|
+
execSync('npm install veko@latest', { stdio: 'inherit' });
|
|
852
|
+
console.log(this.styles.success('✅ Veko installé'));
|
|
853
|
+
} catch (error) {
|
|
854
|
+
console.log(this.styles.error(`❌ Erreur d'installation: ${error.message}`));
|
|
855
|
+
}
|
|
856
|
+
} else {
|
|
857
|
+
console.log(this.styles.success(`✅ Veko v${vekoInstalled} installé`));
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// 4. Reset du log
|
|
862
|
+
console.log('4. Nettoyage des logs');
|
|
863
|
+
if (fs.existsSync(this.logPath)) {
|
|
864
|
+
fs.writeFileSync(this.logPath, '');
|
|
865
|
+
console.log(this.styles.success('✅ Logs nettoyés'));
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
console.log(this.styles.separator);
|
|
869
|
+
console.log(this.styles.success('🎉 Réparation terminée!'));
|
|
870
|
+
console.log(this.styles.info('💡 Utilisez "veko update check" pour vérifier les mises à jour'));
|
|
871
|
+
|
|
872
|
+
return true;
|
|
873
|
+
} catch (error) {
|
|
874
|
+
console.log(this.styles.error(`❌ Erreur lors de la réparation: ${error.message}`));
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// 📄 Récupération de la version actuelle plus robuste
|
|
880
|
+
static getCurrentVersion() {
|
|
881
|
+
try {
|
|
882
|
+
if (!fs.existsSync(this.packageJsonPath)) {
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const packageJson = JSON.parse(fs.readFileSync(this.packageJsonPath, 'utf8'));
|
|
887
|
+
|
|
888
|
+
const vekoVersion = packageJson.dependencies?.veko ||
|
|
889
|
+
packageJson.devDependencies?.veko ||
|
|
890
|
+
packageJson.peerDependencies?.veko;
|
|
891
|
+
|
|
892
|
+
if (!vekoVersion) {
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
this.currentVersion = vekoVersion.replace(/[\^~>=<]/g, '');
|
|
897
|
+
return this.currentVersion;
|
|
898
|
+
} catch (error) {
|
|
899
|
+
console.warn(`[Auto-updater] Erreur lors de la lecture de package.json: ${error.message}`);
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// 📝 Système de logs amélioré avec gestion d'erreurs renforcée
|
|
905
|
+
static log(level, message) {
|
|
906
|
+
try {
|
|
907
|
+
const timestamp = new Date().toISOString();
|
|
908
|
+
const logEntry = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
|
|
909
|
+
|
|
910
|
+
// Affichage console avec couleurs
|
|
911
|
+
const colorMap = {
|
|
912
|
+
error: this.styles.error,
|
|
913
|
+
warn: this.styles.warning,
|
|
914
|
+
info: this.styles.info,
|
|
915
|
+
success: this.styles.success,
|
|
916
|
+
debug: this.styles.dim
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
const colorFunc = colorMap[level] || chalk.white;
|
|
920
|
+
console.log(colorFunc(`[${level.toUpperCase()}] ${message}`));
|
|
921
|
+
|
|
922
|
+
// Écriture dans le fichier de log, mais seulement si accessible
|
|
923
|
+
try {
|
|
924
|
+
if (!fs.existsSync(path.dirname(this.logPath))) {
|
|
925
|
+
fs.mkdirSync(path.dirname(this.logPath), { recursive: true });
|
|
926
|
+
}
|
|
927
|
+
fs.appendFileSync(this.logPath, logEntry);
|
|
928
|
+
this.rotateLogFile();
|
|
929
|
+
} catch (error) {
|
|
930
|
+
// Ignore les erreurs d'écriture dans le fichier
|
|
931
|
+
}
|
|
932
|
+
} catch (error) {
|
|
933
|
+
// Éviter les boucles infinies avec console.error
|
|
934
|
+
console.error(`Erreur dans le système de log: ${error.message}`);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// 📝 Méthode de fallback pour le logging d'erreurs
|
|
939
|
+
static logError(message) {
|
|
940
|
+
try {
|
|
941
|
+
console.error(`[ERROR] ${message}`);
|
|
942
|
+
// Tentative d'écriture dans le fichier de log
|
|
943
|
+
if (fs.existsSync(path.dirname(this.logPath))) {
|
|
944
|
+
const timestamp = new Date().toISOString();
|
|
945
|
+
const logEntry = `[${timestamp}] [ERROR] ${message}\n`;
|
|
946
|
+
fs.appendFileSync(this.logPath, logEntry);
|
|
947
|
+
}
|
|
948
|
+
} catch (e) {
|
|
949
|
+
// Dernier recours
|
|
950
|
+
console.error(`[Auto-updater] Erreur critique: ${message}`);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// 🔧 CLI Handler pour les commandes avec meilleure gestion d'erreurs
|
|
955
|
+
static async handleCLI(args = []) {
|
|
956
|
+
const command = args[0];
|
|
957
|
+
|
|
958
|
+
try {
|
|
959
|
+
// Initialiser d'abord si pas déjà fait
|
|
960
|
+
if (!this.config || Object.keys(this.config).length === 0) {
|
|
961
|
+
await this.init();
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Vérifier les fonctions essentielles et les créer si manquantes
|
|
965
|
+
if (typeof this.getCurrentVersion !== 'function') {
|
|
966
|
+
throw new Error("getCurrentVersion is not a function - Auto-updater corrompu");
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
if (typeof this.log !== 'function') {
|
|
970
|
+
// Recréer log à la volée si manquante
|
|
971
|
+
this.log = this.logError;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
switch (command) {
|
|
975
|
+
case 'check':
|
|
976
|
+
return await this.checkForUpdates();
|
|
977
|
+
|
|
978
|
+
case 'update':
|
|
979
|
+
return await this.performUpdateCommand();
|
|
980
|
+
|
|
981
|
+
case 'config':
|
|
982
|
+
if (args[1] && args[2]) {
|
|
983
|
+
// Mise à jour d'une option spécifique
|
|
984
|
+
return await this.updateSetting(args[1], args[2]);
|
|
985
|
+
}
|
|
986
|
+
return await this.configureSettings();
|
|
987
|
+
|
|
988
|
+
case 'rollback':
|
|
989
|
+
return await this.rollback(args[1]);
|
|
990
|
+
|
|
991
|
+
case 'stats':
|
|
992
|
+
case 'status':
|
|
993
|
+
return this.displayStats();
|
|
994
|
+
|
|
995
|
+
case 'fix':
|
|
996
|
+
return await this.fixInstallation();
|
|
997
|
+
|
|
998
|
+
case 'help':
|
|
999
|
+
case '--help':
|
|
1000
|
+
case '-h':
|
|
1001
|
+
return this.showHelp();
|
|
1002
|
+
|
|
1003
|
+
case 'version':
|
|
1004
|
+
case '--version':
|
|
1005
|
+
case '-v':
|
|
1006
|
+
return this.showVersion();
|
|
1007
|
+
|
|
1008
|
+
case undefined:
|
|
1009
|
+
default:
|
|
1010
|
+
// Par défaut, check seulement
|
|
1011
|
+
return await this.checkForUpdates();
|
|
1012
|
+
}
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
console.error(`[Auto-updater] Erreur de commande: ${error.message}`);
|
|
1015
|
+
if (process.env.DEBUG) {
|
|
1016
|
+
console.error(error.stack);
|
|
1017
|
+
}
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// 🔧 Mise à jour d'un paramètre
|
|
1023
|
+
static async updateSetting(key, value) {
|
|
1024
|
+
try {
|
|
1025
|
+
// Convertir la valeur en fonction du type attendu
|
|
1026
|
+
let parsedValue = value;
|
|
1027
|
+
if (value === 'true') parsedValue = true;
|
|
1028
|
+
if (value === 'false') parsedValue = false;
|
|
1029
|
+
if (!isNaN(parseInt(value))) parsedValue = parseInt(value);
|
|
1030
|
+
|
|
1031
|
+
// Vérifier que la clé existe dans la configuration
|
|
1032
|
+
if (!(key in this.defaultConfig)) {
|
|
1033
|
+
console.log(this.styles.error(`❌ Paramètre inconnu: ${key}`));
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// Mettre à jour la configuration
|
|
1038
|
+
this.config[key] = parsedValue;
|
|
1039
|
+
await this.saveConfig();
|
|
1040
|
+
|
|
1041
|
+
console.log(this.styles.success(`✅ Paramètre mis à jour: ${key} = ${parsedValue}`));
|
|
1042
|
+
return true;
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
console.log(this.styles.error(`❌ Erreur: ${error.message}`));
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// 🔍 Comparaison de versions améliorée avec support des pre-release
|
|
1050
|
+
static compareVersions(version1, version2) {
|
|
1051
|
+
const parseVersion = (version) => {
|
|
1052
|
+
const [main, prerelease] = version.split('-');
|
|
1053
|
+
const [major, minor, patch] = main.split('.').map(n => parseInt(n));
|
|
1054
|
+
return { major, minor, patch, prerelease: prerelease || null };
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
const v1 = parseVersion(version1);
|
|
1058
|
+
const v2 = parseVersion(version2);
|
|
1059
|
+
|
|
1060
|
+
// Compare major.minor.patch
|
|
1061
|
+
if (v1.major !== v2.major) return v1.major - v2.major;
|
|
1062
|
+
if (v1.minor !== v2.minor) return v1.minor - v2.minor;
|
|
1063
|
+
if (v1.patch !== v2.patch) return v1.patch - v2.patch;
|
|
1064
|
+
|
|
1065
|
+
// Compare prerelease
|
|
1066
|
+
if (v1.prerelease && !v2.prerelease) return -1;
|
|
1067
|
+
if (!v1.prerelease && v2.prerelease) return 1;
|
|
1068
|
+
if (v1.prerelease && v2.prerelease) {
|
|
1069
|
+
return v1.prerelease.localeCompare(v2.prerelease);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
return 0;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// 🔄 Rotation des logs
|
|
1076
|
+
static rotateLogFile() {
|
|
1077
|
+
try {
|
|
1078
|
+
if (!fs.existsSync(this.logPath)) return;
|
|
1079
|
+
|
|
1080
|
+
const stats = fs.statSync(this.logPath);
|
|
1081
|
+
if (stats.size > 1024 * 1024) { // 1MB
|
|
1082
|
+
const rotatedPath = this.logPath + '.' + Date.now();
|
|
1083
|
+
fs.renameSync(this.logPath, rotatedPath);
|
|
1084
|
+
fs.writeFileSync(this.logPath, '');
|
|
1085
|
+
|
|
1086
|
+
// Nettoyer les anciens logs
|
|
1087
|
+
const logDir = path.dirname(this.logPath);
|
|
1088
|
+
const files = fs.readdirSync(logDir)
|
|
1089
|
+
.filter(file => file.startsWith(path.basename(this.logPath) + '.'))
|
|
1090
|
+
.sort();
|
|
1091
|
+
|
|
1092
|
+
// Garder seulement les 5 derniers logs
|
|
1093
|
+
if (files.length > 5) {
|
|
1094
|
+
files.slice(0, files.length - 5).forEach(file => {
|
|
1095
|
+
fs.unlinkSync(path.join(logDir, file));
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
// Ignorer les erreurs
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// 💾 Sauvegarde des statistiques
|
|
1105
|
+
static async saveStats() {
|
|
1106
|
+
try {
|
|
1107
|
+
if (fs.existsSync(this.packageJsonPath)) {
|
|
1108
|
+
const packageJson = JSON.parse(fs.readFileSync(this.packageJsonPath, 'utf8'));
|
|
1109
|
+
packageJson.vekoUpdaterStats = this.stats;
|
|
1110
|
+
fs.writeFileSync(this.packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
1111
|
+
}
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
this.logError(`Impossible de sauvegarder les statistiques: ${error.message}`);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
module.exports = AutoUpdater;
|