waengine 1.7.3 → 1.7.4

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.
@@ -1,4 +1,4 @@
1
- // Session Manager für WAEngine v1.0.6
1
+ // Session Manager für WAEngine - Windows-kompatibel
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
 
@@ -7,6 +7,11 @@ export class SessionManager {
7
7
  this.authDir = authDir;
8
8
  }
9
9
 
10
+ // Einfache Prüfung ob Auth-Ordner existiert
11
+ hasAuthFolder() {
12
+ return fs.existsSync(this.authDir);
13
+ }
14
+
10
15
  // Prüft ob Session existiert und gültig ist
11
16
  async validateSession() {
12
17
  try {
@@ -19,9 +24,49 @@ export class SessionManager {
19
24
  return { valid: false, reason: 'no_creds' };
20
25
  }
21
26
 
22
- const creds = JSON.parse(fs.readFileSync(credsPath, 'utf8'));
27
+ // Robuste JSON-Validierung mit mehreren Fallbacks
28
+ let creds;
29
+ let fileContent;
30
+
31
+ try {
32
+ fileContent = fs.readFileSync(credsPath, 'utf8');
33
+
34
+ // Prüfe ob Datei leer oder zu kurz ist
35
+ if (!fileContent || fileContent.trim().length < 10) {
36
+ console.error('❌ creds.json ist leer oder zu kurz');
37
+ return { valid: false, reason: 'empty_creds' };
38
+ }
39
+
40
+ // Prüfe auf unvollständige JSON (häufiger Fehler)
41
+ if (!fileContent.trim().endsWith('}') && !fileContent.trim().endsWith(']')) {
42
+ console.error('❌ creds.json ist unvollständig (fehlendes Ende)');
43
+ return { valid: false, reason: 'incomplete_creds' };
44
+ }
45
+
46
+ // Versuche JSON zu parsen
47
+ creds = JSON.parse(fileContent);
48
+
49
+ } catch (jsonError) {
50
+ console.error('❌ Fehler beim Lesen der creds.json:', jsonError.message);
51
+
52
+ // Versuche Backup-Recovery
53
+ const backupRecovered = await this.tryRecoverFromBackup();
54
+ if (backupRecovered) {
55
+ console.log('✅ Session aus Backup wiederhergestellt');
56
+ return await this.validateSession(); // Rekursiv nach Recovery
57
+ }
58
+
59
+ // Erstelle Backup der korrupten Datei für Debugging
60
+ await this.backupCorruptedFile(credsPath, fileContent);
61
+
62
+ return { valid: false, reason: 'corrupted_creds', error: jsonError.message };
63
+ }
23
64
 
24
65
  // Prüfe ob User-ID vorhanden (eingeloggt)
66
+ if (!creds || typeof creds !== 'object') {
67
+ return { valid: false, reason: 'invalid_creds_format' };
68
+ }
69
+
25
70
  if (!creds.me?.id) {
26
71
  return { valid: false, reason: 'not_logged_in' };
27
72
  }
@@ -42,35 +87,226 @@ export class SessionManager {
42
87
  };
43
88
 
44
89
  } catch (error) {
45
- return { valid: false, reason: 'corrupted', error: error.message };
90
+ console.error('❌ Session-Validierung Fehler:', error);
91
+ return { valid: false, reason: 'validation_error', error: error.message };
46
92
  }
47
93
  }
48
94
 
49
- // Bereinigt Session komplett
95
+ // Windows-kompatible Session-Bereinigung
50
96
  async cleanupSession() {
51
97
  try {
52
- if (fs.existsSync(this.authDir)) {
53
- console.log("🧹 Bereinige Session...");
54
-
55
- // Alle Auth-Dateien löschen
56
- const files = fs.readdirSync(this.authDir);
57
- for (const file of files) {
58
- const filePath = path.join(this.authDir, file);
59
- fs.unlinkSync(filePath);
60
- }
61
-
62
- // Auth-Ordner löschen
63
- fs.rmdirSync(this.authDir);
64
- console.log("✅ Session bereinigt");
98
+ if (!fs.existsSync(this.authDir)) {
65
99
  return true;
66
100
  }
101
+
102
+ console.log("🧹 Bereinige Session (Windows-kompatibel)...");
103
+
104
+ // Spezielle Behandlung für Windows EPERM Fehler
105
+ await this.forceDeleteDirectory(this.authDir);
106
+
107
+ console.log("✅ Session bereinigt");
67
108
  return true;
109
+
68
110
  } catch (error) {
69
111
  console.error("❌ Fehler beim Session-Cleanup:", error);
112
+
113
+ // Erweiterte Fallback-Strategien
114
+ return await this.fallbackCleanup();
115
+ }
116
+ }
117
+
118
+ // Erweiterte Fallback-Bereinigung
119
+ async fallbackCleanup() {
120
+ try {
121
+ console.log("🔄 Versuche Fallback-Bereinigung...");
122
+
123
+ // Strategie 1: Nur wichtige Dateien löschen
124
+ const criticalFiles = ['creds.json', 'app-state-sync-version.json'];
125
+ let deletedFiles = 0;
126
+
127
+ for (const file of criticalFiles) {
128
+ const filePath = path.join(this.authDir, file);
129
+ if (fs.existsSync(filePath)) {
130
+ try {
131
+ fs.unlinkSync(filePath);
132
+ deletedFiles++;
133
+ console.log(`✅ ${file} gelöscht`);
134
+ } catch (fileError) {
135
+ console.log(`⚠️ Konnte ${file} nicht löschen`);
136
+ }
137
+ }
138
+ }
139
+
140
+ // Strategie 2: Ordner umbenennen (Windows-Trick)
141
+ if (deletedFiles === 0) {
142
+ const tempName = `${this.authDir}_deleted_${Date.now()}`;
143
+ try {
144
+ fs.renameSync(this.authDir, tempName);
145
+ console.log(`📁 Auth-Ordner umbenannt zu: ${path.basename(tempName)}`);
146
+ console.log("💡 Alter Ordner wird beim nächsten Neustart automatisch bereinigt");
147
+ return true;
148
+ } catch (renameError) {
149
+ console.log("⚠️ Auch Umbenennung fehlgeschlagen");
150
+ }
151
+ }
152
+
153
+ // Strategie 3: Leeren Ordner erstellen (Override)
154
+ try {
155
+ const newAuthDir = `${this.authDir}_new_${Date.now()}`;
156
+ fs.mkdirSync(newAuthDir, { recursive: true });
157
+
158
+ // Symbolischen Link erstellen (falls möglich)
159
+ try {
160
+ if (fs.existsSync(this.authDir)) {
161
+ fs.rmSync(this.authDir, { recursive: true, force: true });
162
+ }
163
+ fs.renameSync(newAuthDir, this.authDir);
164
+ console.log("✅ Neuer Auth-Ordner erstellt");
165
+ return true;
166
+ } catch (linkError) {
167
+ console.log("⚠️ Symbolischer Link fehlgeschlagen");
168
+ }
169
+ } catch (createError) {
170
+ console.log("⚠️ Neuer Ordner konnte nicht erstellt werden");
171
+ }
172
+
173
+ console.log("⚠️ Fallback-Bereinigung teilweise erfolgreich");
174
+ return deletedFiles > 0;
175
+
176
+ } catch (fallbackError) {
177
+ console.error("❌ Alle Bereinigungsstrategien fehlgeschlagen:", fallbackError);
70
178
  return false;
71
179
  }
72
180
  }
73
181
 
182
+ // Windows-kompatible Ordner-Löschung mit erweiterten Fallback-Strategien
183
+ async forceDeleteDirectory(dirPath) {
184
+ if (!fs.existsSync(dirPath)) return;
185
+
186
+ try {
187
+ // Moderne Node.js Lösung (Node.js 14.14.0+)
188
+ if (fs.rmSync) {
189
+ fs.rmSync(dirPath, {
190
+ recursive: true,
191
+ force: true,
192
+ maxRetries: 3,
193
+ retryDelay: 100
194
+ });
195
+ return;
196
+ }
197
+
198
+ // Fallback für ältere Node.js Versionen
199
+ await this.legacyDeleteDirectory(dirPath);
200
+
201
+ } catch (error) {
202
+ if (error.code === 'EPERM' || error.code === 'EBUSY' || error.code === 'ENOTEMPTY') {
203
+ console.log("⚠️ Windows-Berechtigungsfehler - verwende erweiterte Fallback-Strategien");
204
+ await this.windowsEpermFallback(dirPath);
205
+ } else {
206
+ throw error;
207
+ }
208
+ }
209
+ }
210
+
211
+ // Legacy Verzeichnis-Löschung für ältere Node.js Versionen
212
+ async legacyDeleteDirectory(dirPath) {
213
+ const files = fs.readdirSync(dirPath);
214
+
215
+ for (const file of files) {
216
+ const filePath = path.join(dirPath, file);
217
+ const stat = fs.statSync(filePath);
218
+
219
+ if (stat.isDirectory()) {
220
+ await this.legacyDeleteDirectory(filePath);
221
+ } else {
222
+ await this.forceDeleteFile(filePath);
223
+ }
224
+ }
225
+
226
+ fs.rmdirSync(dirPath);
227
+ }
228
+
229
+ // Robuste Datei-Löschung mit Windows-Kompatibilität
230
+ async forceDeleteFile(filePath) {
231
+ try {
232
+ fs.unlinkSync(filePath);
233
+ } catch (error) {
234
+ if (error.code === 'EPERM') {
235
+ // Windows EPERM Fallback-Strategien
236
+ try {
237
+ // Strategie 1: Datei-Attribute zurücksetzen (Windows)
238
+ if (process.platform === 'win32') {
239
+ const { execSync } = await import('child_process');
240
+ execSync(`attrib -R -H -S "${filePath}"`, { stdio: 'ignore' });
241
+ }
242
+ fs.unlinkSync(filePath);
243
+ } catch (attribError) {
244
+ try {
245
+ // Strategie 2: Datei überschreiben und dann löschen
246
+ fs.writeFileSync(filePath, '');
247
+ fs.unlinkSync(filePath);
248
+ } catch (overwriteError) {
249
+ // Strategie 3: Datei umbenennen (für spätere Löschung)
250
+ const tempName = `${filePath}_deleted_${Date.now()}`;
251
+ try {
252
+ fs.renameSync(filePath, tempName);
253
+ console.log(`📁 Datei umbenannt: ${path.basename(tempName)}`);
254
+ } catch (renameError) {
255
+ console.log(`⚠️ Konnte Datei nicht löschen: ${path.basename(filePath)}`);
256
+ }
257
+ }
258
+ }
259
+ } else {
260
+ throw error;
261
+ }
262
+ }
263
+ }
264
+
265
+ // Windows EPERM Fallback-Strategien
266
+ async windowsEpermFallback(dirPath) {
267
+ try {
268
+ // Strategie 1: Verzeichnis-Attribute zurücksetzen (Windows)
269
+ if (process.platform === 'win32') {
270
+ const { execSync } = await import('child_process');
271
+ try {
272
+ execSync(`attrib -R -H -S "${dirPath}" /S /D`, { stdio: 'ignore' });
273
+ if (fs.rmSync) {
274
+ fs.rmSync(dirPath, { recursive: true, force: true });
275
+ } else {
276
+ await this.legacyDeleteDirectory(dirPath);
277
+ }
278
+ return;
279
+ } catch (attribError) {
280
+ // Weiter zu nächster Strategie
281
+ }
282
+ }
283
+
284
+ // Strategie 2: Verzeichnis umbenennen
285
+ const tempName = `${dirPath}_deleted_${Date.now()}`;
286
+ try {
287
+ fs.renameSync(dirPath, tempName);
288
+ console.log(`📁 Verzeichnis umbenannt zu: ${path.basename(tempName)}`);
289
+
290
+ // Versuche verzögerte Löschung
291
+ setTimeout(() => {
292
+ try {
293
+ if (fs.rmSync) {
294
+ fs.rmSync(tempName, { recursive: true, force: true });
295
+ }
296
+ } catch (delayedError) {
297
+ // Stille Behandlung - Verzeichnis bleibt umbenannt
298
+ }
299
+ }, 1000);
300
+
301
+ } catch (renameError) {
302
+ console.log("⚠️ Verzeichnis konnte nicht umbenannt werden - bleibt bestehen");
303
+ }
304
+
305
+ } catch (fallbackError) {
306
+ console.log(`⚠️ Alle Fallback-Strategien fehlgeschlagen für: ${path.basename(dirPath)}`);
307
+ }
308
+ }
309
+
74
310
  // Erstellt Auth-Ordner falls nicht vorhanden
75
311
  async ensureAuthDir() {
76
312
  if (!fs.existsSync(this.authDir)) {
@@ -91,7 +327,14 @@ export class SessionManager {
91
327
  for (const file of files) {
92
328
  const srcPath = path.join(this.authDir, file);
93
329
  const destPath = path.join(backupDir, file);
94
- fs.copyFileSync(srcPath, destPath);
330
+
331
+ try {
332
+ if (fs.statSync(srcPath).isFile()) {
333
+ fs.copyFileSync(srcPath, destPath);
334
+ }
335
+ } catch (copyError) {
336
+ console.log(`⚠️ Konnte ${file} nicht kopieren:`, copyError.message);
337
+ }
95
338
  }
96
339
 
97
340
  console.log(`💾 Session-Backup erstellt: ${backupDir}`);
@@ -112,12 +355,22 @@ export class SessionManager {
112
355
  const files = fs.readdirSync(this.authDir);
113
356
  const fileDetails = files.map(file => {
114
357
  const filePath = path.join(this.authDir, file);
115
- const stats = fs.statSync(filePath);
116
- return {
117
- name: file,
118
- size: stats.size,
119
- modified: stats.mtime
120
- };
358
+ try {
359
+ const stats = fs.statSync(filePath);
360
+ return {
361
+ name: file,
362
+ size: stats.size,
363
+ modified: stats.mtime,
364
+ isFile: stats.isFile()
365
+ };
366
+ } catch (statError) {
367
+ return {
368
+ name: file,
369
+ size: 0,
370
+ modified: null,
371
+ error: statError.message
372
+ };
373
+ }
121
374
  });
122
375
 
123
376
  return {
@@ -151,4 +404,156 @@ export class SessionManager {
151
404
 
152
405
  return { repaired: false, reason: validation.reason };
153
406
  }
407
+
408
+ // Versucht Session aus Backup wiederherzustellen
409
+ async tryRecoverFromBackup() {
410
+ try {
411
+ const backupPattern = `${this.authDir}_backup_`;
412
+ const parentDir = path.dirname(this.authDir);
413
+
414
+ if (!fs.existsSync(parentDir)) return false;
415
+
416
+ const entries = fs.readdirSync(parentDir);
417
+ const backupDirs = entries
418
+ .filter(entry => entry.startsWith(path.basename(backupPattern)))
419
+ .map(entry => ({
420
+ path: path.join(parentDir, entry),
421
+ timestamp: parseInt(entry.split('_').pop()) || 0
422
+ }))
423
+ .sort((a, b) => b.timestamp - a.timestamp); // Neueste zuerst
424
+
425
+ for (const backup of backupDirs) {
426
+ const backupCredsPath = path.join(backup.path, 'creds.json');
427
+
428
+ if (fs.existsSync(backupCredsPath)) {
429
+ try {
430
+ // Teste ob Backup-JSON gültig ist
431
+ const backupContent = fs.readFileSync(backupCredsPath, 'utf8');
432
+ const testCreds = JSON.parse(backupContent);
433
+
434
+ if (testCreds.me?.id) {
435
+ // Backup ist gültig - stelle wieder her
436
+ console.log(`🔄 Stelle Session aus Backup wieder her: ${backup.path}`);
437
+
438
+ // Kopiere alle Backup-Dateien zurück
439
+ const backupFiles = fs.readdirSync(backup.path);
440
+ for (const file of backupFiles) {
441
+ const srcPath = path.join(backup.path, file);
442
+ const destPath = path.join(this.authDir, file);
443
+
444
+ try {
445
+ fs.copyFileSync(srcPath, destPath);
446
+ } catch (copyError) {
447
+ console.log(`⚠️ Konnte ${file} nicht wiederherstellen`);
448
+ }
449
+ }
450
+
451
+ return true;
452
+ }
453
+ } catch (backupError) {
454
+ console.log(`⚠️ Backup ${backup.path} ist auch korrupt`);
455
+ continue;
456
+ }
457
+ }
458
+ }
459
+
460
+ return false;
461
+ } catch (error) {
462
+ console.error('❌ Backup-Recovery Fehler:', error);
463
+ return false;
464
+ }
465
+ }
466
+
467
+ // Erstellt Backup der korrupten Datei für Debugging
468
+ async backupCorruptedFile(credsPath, content) {
469
+ try {
470
+ const timestamp = Date.now();
471
+ const corruptedDir = `${this.authDir}_corrupted_${timestamp}`;
472
+
473
+ fs.mkdirSync(corruptedDir, { recursive: true });
474
+
475
+ // Korrupte Datei sichern
476
+ fs.writeFileSync(path.join(corruptedDir, 'creds.json'), content);
477
+
478
+ // Debug-Info hinzufügen
479
+ const debugInfo = {
480
+ timestamp: new Date().toISOString(),
481
+ fileSize: content.length,
482
+ firstChars: content.substring(0, 100),
483
+ lastChars: content.substring(Math.max(0, content.length - 100)),
484
+ error: 'JSON parse failed',
485
+ platform: process.platform,
486
+ nodeVersion: process.version
487
+ };
488
+
489
+ fs.writeFileSync(
490
+ path.join(corruptedDir, 'debug-info.json'),
491
+ JSON.stringify(debugInfo, null, 2)
492
+ );
493
+
494
+ console.log(`🗂️ Korrupte Datei gesichert: ${corruptedDir}`);
495
+ } catch (error) {
496
+ console.error('❌ Fehler beim Sichern der korrupten Datei:', error);
497
+ }
498
+ }
499
+
500
+ // Automatische Session-Reparatur mit erweiterten Optionen
501
+ async autoRepairSession() {
502
+ console.log("🔧 Starte automatische Session-Reparatur...");
503
+
504
+ const validation = await this.validateSession();
505
+
506
+ switch (validation.reason) {
507
+ case 'corrupted_creds':
508
+ case 'empty_creds':
509
+ case 'incomplete_creds':
510
+ console.log("🗑️ Korrupte Session erkannt - bereinige...");
511
+ const cleanupResult = await this.cleanupSession();
512
+ if (cleanupResult) {
513
+ return { repaired: true, action: 'cleanup_corrupted' };
514
+ } else {
515
+ return { repaired: false, action: 'cleanup_failed' };
516
+ }
517
+
518
+ case 'session_too_old':
519
+ console.log("⏰ Session zu alt - bereinige...");
520
+ await this.cleanupSession();
521
+ return { repaired: true, action: 'cleanup_old' };
522
+
523
+ case 'not_logged_in':
524
+ console.log("👤 Nicht eingeloggt - Session bereinigen...");
525
+ await this.cleanupSession();
526
+ return { repaired: true, action: 'cleanup_not_logged_in' };
527
+
528
+ case 'no_creds':
529
+ console.log("📄 Keine Credentials - Auth-Ordner vorbereiten...");
530
+ await this.ensureAuthDir();
531
+ return { repaired: true, action: 'prepare_auth_dir' };
532
+
533
+ default:
534
+ return { repaired: false, reason: validation.reason };
535
+ }
536
+ }
537
+
538
+ // Intelligente Session-Bereinigung (nur wenn nötig)
539
+ async smartCleanup() {
540
+ const validation = await this.validateSession();
541
+
542
+ if (validation.valid) {
543
+ console.log("✅ Session ist gültig - keine Bereinigung nötig");
544
+ return { cleaned: false, reason: 'session_valid' };
545
+ }
546
+
547
+ // Backup vor Bereinigung
548
+ const backupPath = await this.backupSession();
549
+
550
+ // Bereinigung durchführen
551
+ const cleanupResult = await this.cleanupSession();
552
+
553
+ return {
554
+ cleaned: cleanupResult,
555
+ reason: validation.reason,
556
+ backup: backupPath
557
+ };
558
+ }
154
559
  }