vantuz 3.5.18 → 4.0.3

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.
Files changed (88) hide show
  1. package/cli.js +9 -9
  2. package/config.js +6 -6
  3. package/core/agent-loop.js +6 -6
  4. package/core/agent.js +6 -6
  5. package/core/ai-copilot.js +461 -0
  6. package/core/ai-provider.js +60 -10
  7. package/core/automation.js +8 -8
  8. package/core/cache-manager.js +232 -0
  9. package/core/channels.js +8 -8
  10. package/core/dashboard.js +5 -5
  11. package/core/database-manager.js +331 -0
  12. package/core/database.js +124 -83
  13. package/core/eia-brain.js +2 -2
  14. package/core/eia-monitor.js +7 -7
  15. package/core/engine.js +24 -24
  16. package/core/error-handler.js +203 -0
  17. package/core/gateway.js +9 -9
  18. package/core/learning.js +7 -7
  19. package/core/license-manager.js +1 -1
  20. package/core/license.js +6 -6
  21. package/core/logger.js +228 -0
  22. package/core/marketplace-adapter.js +5 -5
  23. package/core/memory.js +6 -6
  24. package/core/multi-agent.js +180 -0
  25. package/core/openclaw-bridge.js +6 -6
  26. package/core/queue.js +3 -3
  27. package/core/scheduler.js +6 -6
  28. package/core/scrapers/Scraper.js +1 -1
  29. package/core/scrapers/TrendyolScraper.js +1 -1
  30. package/core/self-healer.js +8 -6
  31. package/core/unified-product.js +8 -8
  32. package/core/vector-db.js +5 -5
  33. package/core/vision-service.js +5 -5
  34. package/desktop/index.html +2804 -0
  35. package/desktop/main.js +97 -0
  36. package/desktop/preload.js +30 -0
  37. package/dev.sh +5 -0
  38. package/index.js +484 -116
  39. package/modules/crm/sentiment-crm.js +4 -4
  40. package/modules/healer/listing-healer.js +2 -2
  41. package/modules/oracle/predictor.js +5 -5
  42. package/modules/researcher/agent.js +4 -4
  43. package/modules/war-room/competitor-tracker.js +5 -5
  44. package/modules/war-room/pricing-engine.js +5 -5
  45. package/nodes/warehouse.js +5 -5
  46. package/onboard.js +1 -1
  47. package/package.json +11 -3
  48. package/pkg.json +26 -0
  49. package/plugins/vantuz/index.js +16 -17
  50. package/plugins/vantuz/memory/hippocampus.js +3 -3
  51. package/plugins/vantuz/platforms/_request.js +1 -1
  52. package/plugins/vantuz/platforms/_template.js +2 -2
  53. package/plugins/vantuz/platforms/amazon.js +3 -3
  54. package/plugins/vantuz/platforms/ciceksepeti.js +2 -2
  55. package/plugins/vantuz/platforms/hepsiburada.js +2 -2
  56. package/plugins/vantuz/platforms/index.js +9 -24
  57. package/plugins/vantuz/platforms/n11.js +3 -3
  58. package/plugins/vantuz/platforms/pazarama.js +2 -2
  59. package/plugins/vantuz/platforms/pttavm.js +2 -2
  60. package/plugins/vantuz/platforms/trendyol.js +3 -3
  61. package/plugins/vantuz/services/alerts.js +1 -1
  62. package/plugins/vantuz/services/scheduler.js +1 -1
  63. package/plugins/vantuz/tools/nl-parser.js +1 -1
  64. package/plugins/vantuz/tools/quick-report.js +2 -2
  65. package/plugins/vantuz/tools/repricer.js +1 -1
  66. package/plugins/vantuz/tools/vision.js +3 -3
  67. package/server/app.js +8 -8
  68. package/DOCS_TR.md +0 -80
  69. package/modules/team/agents/base.js +0 -92
  70. package/modules/team/agents/dev.js +0 -33
  71. package/modules/team/agents/josh.js +0 -40
  72. package/modules/team/agents/marketing.js +0 -33
  73. package/modules/team/agents/milo.js +0 -36
  74. package/modules/team/index.js +0 -78
  75. package/modules/team/shared-memory.js +0 -87
  76. package/n11docs.md +0 -1680
  77. package/openclawdocs.md +0 -3
  78. package/vantuz.sqlite +0 -0
  79. package/workspace/AGENTS.md +0 -73
  80. package/workspace/BRAND.md +0 -29
  81. package/workspace/SOUL.md +0 -72
  82. package/workspace/team/DECISIONS.md +0 -3
  83. package/workspace/team/GOALS.md +0 -3
  84. package/workspace/team/PROJECT_STATUS.md +0 -3
  85. package/workspace/team/agents/dev/SOUL.md +0 -12
  86. package/workspace/team/agents/josh/SOUL.md +0 -12
  87. package/workspace/team/agents/marketing/SOUL.md +0 -12
  88. package/workspace/team/agents/milo/SOUL.md +0 -12
@@ -1,10 +1,10 @@
1
1
  // core/automation.js
2
- import fs from 'fs';
3
- import path from 'path';
4
- import os from 'os';
5
- import crypto from 'crypto';
6
- import { log, chat as aiChat } from './ai-provider.js';
7
- import { getScheduler } from './scheduler.js';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const crypto = require('crypto');
6
+ const { log, chat as aiChat } = require('./ai-provider.js');
7
+ const { getScheduler } = require('./scheduler.js');
8
8
 
9
9
  const VANTUZ_HOME = path.join(os.homedir(), '.vantuz');
10
10
  const CONFIG_JSON = path.join(VANTUZ_HOME, 'config.json');
@@ -212,7 +212,7 @@ function fallbackPlan(message) {
212
212
  };
213
213
  }
214
214
 
215
- export class AutomationManager {
215
+ module.exports = AutomationManager {
216
216
  constructor(engine) {
217
217
  this.engine = engine;
218
218
  this.scheduler = getScheduler();
@@ -520,4 +520,4 @@ export class AutomationManager {
520
520
  }
521
521
  }
522
522
 
523
- export default AutomationManager;
523
+ module.exports = AutomationManager;
@@ -0,0 +1,232 @@
1
+ /**
2
+ * 🐙 VANTUZ Cache Manager - Database Query Caching
3
+ * Sık kullanılan veriler için in-memory cache
4
+ */
5
+
6
+ const logger = require('./logger');
7
+
8
+ class CacheManager {
9
+ constructor(options = {}) {
10
+ this.ttl = options.ttl || 300000; // 5 dakika varsayılan
11
+ this.maxSize = options.maxSize || 100; // Maksimum entry sayısı
12
+ this.cache = new Map();
13
+ this.stats = { hits: 0, misses: 0 };
14
+ }
15
+
16
+ /**
17
+ * Cache'e veri ekle
18
+ */
19
+ set(key, value, ttl = null) {
20
+ const effectiveTtl = ttl || this.ttl;
21
+
22
+ // Boyut kontrolü
23
+ if (this.cache.size >= this.maxSize) {
24
+ // En eski entry'yi sil
25
+ const firstKey = this.cache.keys().next().value;
26
+ this.cache.delete(firstKey);
27
+ }
28
+
29
+ this.cache.set(key, {
30
+ value,
31
+ expires: Date.now() + effectiveTtl
32
+ });
33
+
34
+ logger.debug('Cache set', { key, ttl: effectiveTtl });
35
+ }
36
+
37
+ /**
38
+ * Cache'den veri getir
39
+ */
40
+ get(key) {
41
+ const entry = this.cache.get(key);
42
+
43
+ if (!entry) {
44
+ this.stats.misses++;
45
+ logger.debug('Cache miss', { key });
46
+ return null;
47
+ }
48
+
49
+ // Süre dolmuş mu?
50
+ if (Date.now() > entry.expires) {
51
+ this.cache.delete(key);
52
+ this.stats.misses++;
53
+ logger.debug('Cache expired', { key });
54
+ return null;
55
+ }
56
+
57
+ this.stats.hits++;
58
+ logger.debug('Cache hit', { key });
59
+ return entry.value;
60
+ }
61
+
62
+ /**
63
+ * Cache'den veri sil
64
+ */
65
+ delete(key) {
66
+ return this.cache.delete(key);
67
+ }
68
+
69
+ /**
70
+ * Tüm cache'i temizle
71
+ */
72
+ clear() {
73
+ this.cache.clear();
74
+ logger.info('Cache temizlendi');
75
+ }
76
+
77
+ /**
78
+ * Süresi dolmuş entry'leri temizle
79
+ */
80
+ cleanup() {
81
+ const now = Date.now();
82
+ let cleaned = 0;
83
+
84
+ for (const [key, entry] of this.cache) {
85
+ if (now > entry.expires) {
86
+ this.cache.delete(key);
87
+ cleaned++;
88
+ }
89
+ }
90
+
91
+ logger.info('Cache cleanup', { cleaned });
92
+ return cleaned;
93
+ }
94
+
95
+ /**
96
+ * Cache istatistikleri
97
+ */
98
+ getStats() {
99
+ const total = this.stats.hits + this.stats.misses;
100
+ const hitRate = total > 0 ? (this.stats.hits / total * 100).toFixed(2) : 0;
101
+
102
+ return {
103
+ size: this.cache.size,
104
+ maxSize: this.maxSize,
105
+ hits: this.stats.hits,
106
+ misses: this.stats.misses,
107
+ hitRate: `${hitRate}%`
108
+ };
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Query cache decorator
114
+ */
115
+ function cachedQuery(cache, keyPrefix = 'query') {
116
+ return async function(queryFn, key, ttl) {
117
+ const cacheKey = `${keyPrefix}:${key}`;
118
+ const cached = cache.get(cacheKey);
119
+
120
+ if (cached !== null) {
121
+ return cached;
122
+ }
123
+
124
+ const result = await queryFn();
125
+ cache.set(cacheKey, result, ttl);
126
+ return result;
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Debounce fonksiyonu - çoklu istekleri birleştir
132
+ */
133
+ function debounce(fn, delay = 300) {
134
+ let timeoutId = null;
135
+ let pendingArgs = null;
136
+
137
+ return function(...args) {
138
+ pendingArgs = args;
139
+
140
+ if (timeoutId) {
141
+ clearTimeout(timeoutId);
142
+ }
143
+
144
+ timeoutId = setTimeout(() => {
145
+ if (pendingArgs !== null) {
146
+ fn.apply(this, pendingArgs);
147
+ pendingArgs = null;
148
+ }
149
+ timeoutId = null;
150
+ }, delay);
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Throttle fonksiyonu - istekleri sınırla
156
+ */
157
+ function throttle(fn, limit = 10) {
158
+ let inThrottle = false;
159
+ let lastResult = null;
160
+
161
+ return function(...args) {
162
+ if (!inThrottle) {
163
+ inThrottle = true;
164
+ lastResult = fn.apply(this, args);
165
+ setTimeout(() => inThrottle = false, limit);
166
+ }
167
+ return lastResult;
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Lazy loader - sayfa açılınca veri çek
173
+ */
174
+ function createLazyLoader(loadFn) {
175
+ let isLoaded = false;
176
+ let data = null;
177
+ let loading = false;
178
+
179
+ return {
180
+ async get() {
181
+ if (isLoaded && data) {
182
+ return data;
183
+ }
184
+
185
+ if (loading) {
186
+ // Zaten yükleniyorsa bekle
187
+ while (loading) {
188
+ await new Promise(r => setTimeout(r, 50));
189
+ }
190
+ return data;
191
+ }
192
+
193
+ loading = true;
194
+ try {
195
+ data = await loadFn();
196
+ isLoaded = true;
197
+ logger.debug('Lazy load tamamlandı');
198
+ return data;
199
+ } finally {
200
+ loading = false;
201
+ }
202
+ },
203
+
204
+ refresh() {
205
+ isLoaded = false;
206
+ data = null;
207
+ }
208
+ };
209
+ }
210
+
211
+ // Global cache instance
212
+ const globalCache = new CacheManager({
213
+ ttl: 300000, // 5 dakika
214
+ maxSize: 200
215
+ });
216
+
217
+ // Periyodik cleanup
218
+ setInterval(() => {
219
+ const cleaned = globalCache.cleanup();
220
+ if (cleaned > 0) {
221
+ logger.info('Periyodik cache cleanup', { cleaned });
222
+ }
223
+ }, 60000); // Her dakika
224
+
225
+ module.exports = {
226
+ CacheManager,
227
+ cachedQuery,
228
+ debounce,
229
+ throttle,
230
+ createLazyLoader,
231
+ globalCache
232
+ };
package/core/channels.js CHANGED
@@ -7,16 +7,16 @@
7
7
  * - Telegram (Bot API / Gateway)
8
8
  */
9
9
 
10
- import fs from 'fs';
11
- import path from 'path';
12
- import os from 'os';
13
- import { log } from './ai-provider.js';
14
- import { getGateway } from './gateway.js';
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const os = require('os');
13
+ const { log } = require('./ai-provider.js');
14
+ const { getGateway } = require('./gateway.js');
15
15
 
16
16
  const VANTUZ_HOME = path.join(os.homedir(), '.vantuz');
17
17
  const CONFIG_PATH = path.join(VANTUZ_HOME, '.env');
18
18
 
19
- export class ChannelManager {
19
+ module.exports = ChannelManager {
20
20
  constructor() {
21
21
  this.gateway = null;
22
22
  this.status = {
@@ -158,7 +158,7 @@ export class ChannelManager {
158
158
  // Singleton
159
159
  let managerInstance = null;
160
160
 
161
- export async function getChannelManager() {
161
+ module.exports.getChannelManager = async function() {
162
162
  if (!managerInstance) {
163
163
  managerInstance = new ChannelManager();
164
164
  await managerInstance.initAll();
@@ -166,4 +166,4 @@ export async function getChannelManager() {
166
166
  return managerInstance;
167
167
  }
168
168
 
169
- export default ChannelManager;
169
+ module.exports = ChannelManager;
package/core/dashboard.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // System Health Dashboard — Vantuz OS V2 Control Tower
3
3
  // Aggregates status from all modules into a single real-time overview.
4
4
 
5
- import os from 'os';
6
- import { log } from './ai-provider.js';
5
+ const os = require('os');
6
+ const { log } = require('./ai-provider.js');
7
7
 
8
8
  // ═══════════════════════════════════════════════════════════════════════════
9
9
  // UPTIME TRACKER
@@ -195,7 +195,7 @@ class Dashboard {
195
195
 
196
196
  let dashboardInstance = null;
197
197
 
198
- export function getDashboard() {
198
+ module.exports.getDashboard = function() {
199
199
  if (!dashboardInstance) {
200
200
  dashboardInstance = new Dashboard();
201
201
  }
@@ -206,7 +206,7 @@ export function getDashboard() {
206
206
  * Helper: Wire up all V2 modules to the dashboard.
207
207
  * Call this after all modules are initialized.
208
208
  */
209
- export function wireModulesToDashboard(refs = {}) {
209
+ module.exports.w = functionireModulesToDashboard(refs = {}) {
210
210
  const dash = getDashboard();
211
211
 
212
212
  if (refs.agentLoop) dash.registerModule('AgentLoop', () => refs.agentLoop.getStatus());
@@ -227,4 +227,4 @@ export function wireModulesToDashboard(refs = {}) {
227
227
  return dash;
228
228
  }
229
229
 
230
- export default Dashboard;
230
+ module.exports = Dashboard;
@@ -0,0 +1,331 @@
1
+ /**
2
+ * 🐙 VANTUZ Database Manager - Auto-migration, Backup & Integrity
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const crypto = require('crypto');
8
+ const logger = require('./logger');
9
+ const { withErrorHandler } = require('./error-handler');
10
+
11
+ class DatabaseManager {
12
+ constructor(db, dbPath) {
13
+ this.db = db;
14
+ this.dbPath = dbPath;
15
+ this.backupDir = path.join(path.dirname(dbPath), 'backups');
16
+ this.migrationsDir = path.join(__dirname, 'migrations');
17
+ this.schemaVersion = 1;
18
+
19
+ // Backup klasörü yoksa oluştur
20
+ if (!fs.existsSync(this.backupDir)) {
21
+ fs.mkdirSync(this.backupDir, { recursive: true });
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Auto-migration sistemi
27
+ */
28
+ async runMigrations() {
29
+ logger.info('Migration kontrol ediliyor', { currentVersion: this.schemaVersion });
30
+
31
+ const migrations = this.getAvailableMigrations();
32
+
33
+ for (const migration of migrations) {
34
+ if (migration.version > this.schemaVersion) {
35
+ logger.info(`Migration çalıştırılıyor: v${migration.version}`, { name: migration.name });
36
+
37
+ try {
38
+ await migration.up(this.db);
39
+ this.schemaVersion = migration.version;
40
+ logger.info(`Migration tamamlandı: v${migration.version}`);
41
+ } catch (error) {
42
+ logger.error('Migration hatası', {
43
+ version: migration.version,
44
+ error: error.message
45
+ });
46
+ throw error;
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Mevcut migration'ları listele
54
+ */
55
+ getAvailableMigrations() {
56
+ const migrations = [];
57
+
58
+ if (fs.existsSync(this.migrationsDir)) {
59
+ const files = fs.readdirSync(this.migrationsDir)
60
+ .filter(f => f.endsWith('.js') && f.startsWith('migration-'))
61
+ .sort();
62
+
63
+ for (const file of files) {
64
+ const match = file.match(/migration-v(\d+)-(.+)\.js/);
65
+ if (match) {
66
+ const migration = require(path.join(this.migrationsDir, file));
67
+ migrations.push({
68
+ version: parseInt(match[1]),
69
+ name: match[2],
70
+ up: migration.up,
71
+ down: migration.down
72
+ });
73
+ }
74
+ }
75
+ }
76
+
77
+ return migrations;
78
+ }
79
+
80
+ /**
81
+ * Manuel migration kaydet (tabloya)
82
+ */
83
+ async recordMigration(version, name) {
84
+ const migrationsTable = `
85
+ CREATE TABLE IF NOT EXISTS SchemaMigrations (
86
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
87
+ version INTEGER UNIQUE,
88
+ name TEXT,
89
+ appliedAt TEXT
90
+ )
91
+ `;
92
+ this.db.run(migrationsTable);
93
+ this.db.run(
94
+ 'INSERT OR IGNORE INTO SchemaMigrations (version, name, appliedAt) VALUES (?, ?, ?)',
95
+ [version, name, new Date().toISOString()]
96
+ );
97
+ }
98
+
99
+ /**
100
+ * Günlük backup
101
+ */
102
+ async createBackup(reason = 'scheduled') {
103
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
104
+ const backupFile = path.join(this.backupDir, `vantuz-${timestamp}.sqlite`);
105
+
106
+ logger.info('Backup oluşturuluyor', { reason, file: backupFile });
107
+
108
+ try {
109
+ // DB export
110
+ const data = this.db.export();
111
+ const buffer = Buffer.from(data);
112
+
113
+ // Hash hesapla
114
+ const hash = crypto.createHash('sha256').update(buffer).digest('hex');
115
+
116
+ // Backup dosyasına yaz
117
+ fs.writeFileSync(backupFile, buffer);
118
+
119
+ // Metadata
120
+ const meta = {
121
+ date: timestamp,
122
+ reason,
123
+ size: buffer.length,
124
+ hash,
125
+ version: this.schemaVersion
126
+ };
127
+
128
+ fs.writeFileSync(backupFile + '.meta.json', JSON.stringify(meta, null, 2));
129
+
130
+ logger.info('Backup tamamlandı', { file: backupFile, size: buffer.length });
131
+ return { success: true, file: backupFile, hash };
132
+ } catch (error) {
133
+ logger.error('Backup hatası', { error: error.message });
134
+ return { success: false, error: error.message };
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Veri bütünlüğü kontrolü
140
+ */
141
+ async checkIntegrity() {
142
+ logger.info('Veri bütünlüğü kontrol ediliyor...');
143
+
144
+ const issues = [];
145
+
146
+ try {
147
+ // 1. DB dosyası mevcut mu?
148
+ if (!fs.existsSync(this.dbPath)) {
149
+ issues.push({ type: 'missing', message: 'Veritabanı dosyası bulunamadı' });
150
+ return { valid: false, issues };
151
+ }
152
+
153
+ // 2. Hash kontrolü (son backup ile)
154
+ const latestBackup = this.getLatestBackup();
155
+ if (latestBackup) {
156
+ const currentHash = this.calculateHash();
157
+ if (currentHash !== latestBackup.hash) {
158
+ // Değişiklik var, bu normal olabilir
159
+ logger.info('Veritabanı değişikliği tespit edildi', {
160
+ oldHash: latestBackup.hash.substring(0, 8),
161
+ newHash: currentHash.substring(0, 8)
162
+ });
163
+ }
164
+ }
165
+
166
+ // 3. Temel tabloları kontrol et
167
+ const tables = ['Stores', 'Products', 'Orders', 'Insights'];
168
+ for (const table of tables) {
169
+ const result = this.db.get(`SELECT COUNT(*) as count FROM ${table}`);
170
+ if (result === undefined) {
171
+ issues.push({ type: 'missing_table', table });
172
+ }
173
+ }
174
+
175
+ // 4. Orphaned records kontrolü
176
+ const orphanedOrders = this.db.all(`
177
+ SELECT o.* FROM Orders o
178
+ LEFT JOIN Stores s ON o.platform = s.platform
179
+ WHERE s.id IS NULL
180
+ `);
181
+
182
+ if (orphanedOrders.length > 0) {
183
+ issues.push({
184
+ type: 'orphaned_records',
185
+ table: 'Orders',
186
+ count: orphanedOrders.length
187
+ });
188
+ }
189
+
190
+ // 5. Index kontrolü
191
+ const requiredIndexes = [
192
+ { table: 'Products', column: 'barcode' },
193
+ { table: 'Orders', column: 'orderNumber' }
194
+ ];
195
+
196
+ for (const idx of requiredIndexes) {
197
+ const indexName = `idx_${idx.table}_${idx.column}`;
198
+ const hasIndex = this.db.get(
199
+ "SELECT name FROM sqlite_master WHERE type='index' AND name=?",
200
+ [indexName]
201
+ );
202
+ if (!hasIndex) {
203
+ this.db.run(`CREATE INDEX ${indexName} ON ${idx.table} (${idx.column})`);
204
+ logger.info('Eksik index oluşturuldu', { index: indexName });
205
+ }
206
+ }
207
+
208
+ const valid = issues.filter(i => i.type !== 'orphaned_records').length === 0;
209
+
210
+ logger.info('Bütünlük kontrolü tamamlandı', { valid, issues: issues.length });
211
+ return { valid, issues };
212
+
213
+ } catch (error) {
214
+ logger.error('Bütünlük kontrolü hatası', { error: error.message });
215
+ return { valid: false, issues: [{ type: 'error', message: error.message }] };
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Hash hesapla
221
+ */
222
+ calculateHash() {
223
+ if (!fs.existsSync(this.dbPath)) return null;
224
+ const buffer = fs.readFileSync(this.dbPath);
225
+ return crypto.createHash('sha256').update(buffer).digest('hex');
226
+ }
227
+
228
+ /**
229
+ * Son backup'ı getir
230
+ */
231
+ getLatestBackup() {
232
+ if (!fs.existsSync(this.backupDir)) return null;
233
+
234
+ const files = fs.readdirSync(this.backupDir)
235
+ .filter(f => f.endsWith('.sqlite.meta.json'))
236
+ .sort()
237
+ .reverse();
238
+
239
+ if (files.length === 0) return null;
240
+
241
+ try {
242
+ const meta = JSON.parse(
243
+ fs.readFileSync(path.join(this.backupDir, files[0]), 'utf-8')
244
+ );
245
+ return meta;
246
+ } catch {
247
+ return null;
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Backup'tan geri yükle
253
+ */
254
+ async restoreFromBackup(backupFile) {
255
+ if (!fs.existsSync(backupFile)) {
256
+ throw new Error('Backup dosyası bulunamadı');
257
+ }
258
+
259
+ logger.info('Backup geri yükleniyor', { file: backupFile });
260
+
261
+ // Mevcut DB'yi yedekle
262
+ if (fs.existsSync(this.dbPath)) {
263
+ const emergencyBackup = this.dbPath + '.emergency-' + Date.now();
264
+ fs.copyFileSync(this.dbPath, emergencyBackup);
265
+ logger.warn('Mevcut DB yedeklendi', { file: emergencyBackup });
266
+ }
267
+
268
+ // Backup'ı geri yükle
269
+ const buffer = fs.readFileSync(backupFile);
270
+ const newDb = new (require('sql.js'))();
271
+ newDb.run(buffer);
272
+
273
+ // Mevcut db'yi değiştir
274
+ Object.assign(this.db, newDb);
275
+
276
+ logger.info('Backup geri yüklendi');
277
+ return { success: true };
278
+ }
279
+
280
+ /**
281
+ * Eski backup'ları temizle (son 30 gün)
282
+ */
283
+ cleanupOldBackups() {
284
+ const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
285
+
286
+ if (!fs.existsSync(this.backupDir)) return 0;
287
+
288
+ const files = fs.readdirSync(this.backupDir);
289
+ let cleaned = 0;
290
+
291
+ for (const file of files) {
292
+ const filePath = path.join(this.backupDir, file);
293
+ const stats = fs.statSync(filePath);
294
+
295
+ if (stats.mtimeMs < thirtyDaysAgo) {
296
+ fs.unlinkSync(filePath);
297
+ cleaned++;
298
+ }
299
+ }
300
+
301
+ logger.info('Eski backup\'lar temizlendi', { cleaned });
302
+ return cleaned;
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Günlük backup scheduler
308
+ */
309
+ function scheduleDailyBackup(dbManager, hour = 3, minute = 0) {
310
+ const now = new Date();
311
+ const target = new Date();
312
+ target.setHours(hour, minute, 0, 0);
313
+
314
+ if (target <= now) {
315
+ target.setDate(target.getDate() + 1);
316
+ }
317
+
318
+ const msUntilTarget = target - now;
319
+
320
+ setTimeout(() => {
321
+ dbManager.createBackup('scheduled');
322
+ scheduleDailyBackup(dbManager, hour, minute);
323
+ }, msUntilTarget);
324
+
325
+ logger.info('Günlük backup planlandı', { time: `${hour}:${minute}` });
326
+ }
327
+
328
+ module.exports = {
329
+ DatabaseManager,
330
+ scheduleDailyBackup
331
+ };