cloudloyaldb
Version:
Centralized NoSQL Database - MongoDB benzeri API ile cloud server'a veri saklama, real-time sync
1,089 lines (908 loc) • 35.2 kB
JavaScript
/**
* CloudLoyalDB Server
* Centralized database server with flydb.json storage + 24h backup + 2-day cleanup
*/
const express = require('express');
const WebSocket = require('ws');
const fs = require('fs');
const path = require('path');
const cors = require('cors');
class CloudLoyalDBServer {
constructor(options = {}) {
this.app = express();
this.wss = null;
this.clients = new Map();
this.config = {
port: options.port || 3000,
wsPort: options.wsPort || 3001,
dataDir: path.join(process.cwd(), 'data'),
backupDir: path.join(process.cwd(), 'flydb-backups'),
corsOrigin: options.corsOrigin || '*'
};
// Data storage - Multi-database cache
this.dataCache = new Map();
// Ensure data directory exists
if (!fs.existsSync(this.config.dataDir)) {
fs.mkdirSync(this.config.dataDir, { recursive: true });
console.log('CloudLoyalDB: data/ klasörü oluşturuldu');
}
// Backup interval (24 hours = 86400000ms)
this.backupInterval = null;
this._setupMiddleware();
this._setupRoutes();
this._setupWebSocket();
this._setupAutoBackup();
}
/**
* Database dosya yolunu al
*/
getDataFile(database = 'main') {
return path.join(this.config.dataDir, `${database}.json`);
}
/**
* Belirtilen database dosyasından verileri yükle
*/
_loadData(database = 'main') {
try {
const dataFile = this.getDataFile(database);
if (fs.existsSync(dataFile)) {
const rawData = fs.readFileSync(dataFile, 'utf8');
console.log(`CloudLoyalDB: ${database}.json yüklendi`);
return JSON.parse(rawData || '{}');
}
console.log(`CloudLoyalDB: Yeni ${database}.json oluşturuluyor`);
return {};
} catch (error) {
console.error(`CloudLoyalDB: ${database}.json yükleme hatası:`, error.message);
return {};
}
}
/**
* Verileri belirtilen database dosyasına kaydet
*/
_saveData(database = 'main', data) {
try {
const dataFile = this.getDataFile(database);
const jsonData = JSON.stringify(data, null, 2);
fs.writeFileSync(dataFile, jsonData, 'utf8');
console.log(`CloudLoyalDB: ${database}.json kaydedildi (${Object.keys(data).length} koleksiyon)`);
} catch (error) {
console.error(`CloudLoyalDB: ${database}.json kaydetme hatası:`, error.message);
throw error;
}
}
/**
* Database cache'den veri al veya dosyadan yükle
*/
_getDatabase(database = 'main') {
if (!this.dataCache.has(database)) {
const data = this._loadData(database);
this.dataCache.set(database, data);
}
return this.dataCache.get(database);
}
/**
* Database'i cache'e kaydet ve dosyaya yaz + Direkt Backup
*/
_setDatabase(database = 'main', data) {
this.dataCache.set(database, data);
this._saveData(database, data);
// Direkt backup oluştur
this._createDirectBackup(database, data);
}
/**
* Her veri değişikliğinde direkt backup oluştur
*/
_createDirectBackup(database, data) {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupFileName = `${database}-data-${timestamp}.json`;
const backupPath = path.join(this.config.backupDir, backupFileName);
const backupData = {
metadata: {
type: 'auto-save',
database,
createdAt: new Date().toISOString(),
originalFile: this.getDataFile(database),
totalCollections: Object.keys(data || {}).length,
serverVersion: '1.0.0'
},
data: data || {}
};
fs.writeFileSync(backupPath, JSON.stringify(backupData, null, 2), 'utf8');
console.log(`CloudLoyalDB: ${database} direkt backup: ${backupFileName}`);
// WebSocket ile bildir
this._broadcast({
type: 'data_backup_created',
database,
backupId: backupFileName,
timestamp: Date.now(),
backupType: 'auto-save'
});
} catch (error) {
console.error('Direkt backup hatası:', error.message);
}
}
/**
* Middleware kurulumu
*/
_setupMiddleware() {
this.app.use(cors({ origin: this.config.corsOrigin }));
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true }));
// Request logging
this.app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
next();
});
}
/**
* API Routes kurulumu
*/
_setupRoutes() {
// Health check
this.app.get('/api/health', (req, res) => {
// Calculate total collections across all databases
let totalCollections = 0;
const databases = [];
if (fs.existsSync(this.config.dataDir)) {
const dataFiles = fs.readdirSync(this.config.dataDir)
.filter(file => file.endsWith('.json'));
for (const file of dataFiles) {
const dbName = file.replace('.json', '');
const data = this._getDatabase(dbName);
const collections = Object.keys(data || {}).length;
totalCollections += collections;
databases.push({ database: dbName, collections });
}
}
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
version: '1.0.0',
totalCollections,
databases,
dataDir: this.config.dataDir
});
});
// Data operations
this.app.post('/api/data/set', this._handleSet.bind(this));
this.app.get('/api/data/get', this._handleGet.bind(this));
this.app.get('/api/data/all', this._handleGetAll.bind(this));
this.app.post('/api/data/push', this._handlePush.bind(this));
this.app.post('/api/data/unpush', this._handleUnpush.bind(this));
this.app.post('/api/data/delByPriority', this._handleDelByPriority.bind(this));
this.app.post('/api/data/setByPriority', this._handleSetByPriority.bind(this));
this.app.delete('/api/data/delete', this._handleDelete.bind(this));
this.app.delete('/api/data/deleteAll', this._handleDeleteAll.bind(this));
this.app.get('/api/data/has', this._handleHas.bind(this));
// Backup operations
this.app.post('/api/backup/manual', this._handleManualBackup.bind(this));
this.app.get('/api/backup/list', this._handleListBackups.bind(this));
this.app.post('/api/backup/restore', this._handleRestoreBackup.bind(this));
// Stats
this.app.get('/api/stats', this._handleStats.bind(this));
// Error handler
this.app.use((err, req, res, next) => {
console.error('Server Error:', err);
res.status(500).json({ error: 'Internal server error' });
});
}
/**
* WebSocket kurulumu
*/
_setupWebSocket() {
this.wss = new WebSocket.Server({ port: this.config.wsPort });
this.wss.on('connection', (ws, req) => {
const clientId = Date.now() + Math.random();
this.clients.set(ws, { id: clientId, connectedAt: new Date() });
console.log(`WebSocket Client bağlandı: ${clientId}`);
ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
this._handleWebSocketMessage(ws, message);
} catch (error) {
console.error('WebSocket mesaj hatası:', error.message);
}
});
ws.on('close', () => {
this.clients.delete(ws);
console.log(`WebSocket Client ayrıldı: ${clientId}`);
});
ws.on('error', (error) => {
console.error('WebSocket hatası:', error.message);
this.clients.delete(ws);
});
// Bağlantı onayı
ws.send(JSON.stringify({
type: 'connected',
clientId,
timestamp: Date.now()
}));
});
console.log(`CloudLoyalDB: WebSocket Server başlatıldı: ws://localhost:${this.config.wsPort}`);
}
/**
* WebSocket mesaj işleyici
*/
_handleWebSocketMessage(ws, message) {
const { type } = message;
switch (type) {
case 'ping':
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
break;
case 'change_database':
// Database değiştirme bildirimi
console.log('Client database changed:', message.database);
break;
}
}
/**
* Tüm client'lara broadcast
*/
_broadcast(message, excludeWs = null) {
this.clients.forEach((clientInfo, ws) => {
if (ws !== excludeWs && ws.readyState === WebSocket.OPEN) {
try {
ws.send(JSON.stringify(message));
} catch (error) {
console.error('Broadcast hatası:', error.message);
this.clients.delete(ws);
}
}
});
}
// ========== API HANDLERS ==========
/**
* SET - Veri ekleme/güncelleme
*/
_handleSet(req, res) {
try {
const { collection, path, value, database = 'main' } = req.body;
if (!collection) {
return res.status(400).json({ error: 'Collection gereklidir' });
}
if (!path) {
return res.status(400).json({ error: 'Path gereklidir' });
}
// Database verilerini al
const data = this._getDatabase(database);
// Collection yoksa oluştur
if (!data[collection]) {
data[collection] = {};
}
// Nested path'e veri set et
this._setNestedValue(data[collection], path, value);
// Database'e kaydet
this._setDatabase(database, data);
// Real-time broadcast
this._broadcast({
type: 'data_changed',
database,
collection,
path,
value,
operation: 'set',
timestamp: Date.now()
});
res.json({ success: true, database, path, value });
} catch (error) {
console.error('Set Error:', error);
res.status(500).json({ error: error.message });
}
}
/**
* GET - Veri alma
*/
_handleGet(req, res) {
try {
const { collection, path, database = 'main' } = req.query;
if (!collection) {
return res.status(400).json({ error: 'Collection gereklidir' });
}
// Database verilerini al
const data = this._getDatabase(database);
if (!data[collection]) {
return res.status(404).json({ error: 'Collection bulunamadı' });
}
const value = path ?
this._getNestedValue(data[collection], path) :
data[collection];
res.json({ value });
} catch (error) {
console.error('Get Error:', error);
res.status(500).json({ error: error.message });
}
}
/**
* ALL - Tüm koleksiyon verisi
*/
_handleGetAll(req, res) {
try {
const { collection, database = 'main' } = req.query;
if (!collection) {
return res.status(400).json({ error: 'Collection gereklidir' });
}
const data = this._getDatabase(database);
const collectionData = data[collection] || {};
res.json({ data: collectionData });
} catch (error) {
console.error('GetAll Error:', error);
res.status(500).json({ error: error.message });
}
}
/**
* PUSH - Array'e eleman ekleme
*/
_handlePush(req, res) {
try {
const { collection, path, value, database = 'main' } = req.body;
if (!collection || !path) {
return res.status(400).json({ error: 'Collection ve path gereklidir' });
}
// Database verilerini al
const data = this._getDatabase(database);
// Collection yoksa oluştur
if (!data[collection]) {
data[collection] = {};
}
// Mevcut değeri al
let arr = this._getNestedValue(data[collection], path);
// Array değilse oluştur
if (!Array.isArray(arr)) {
arr = [];
}
// Değeri ekle
arr.push(value);
// Geri kaydet
this._setNestedValue(data[collection], path, arr);
this._setDatabase(database, data);
// Broadcast
this._broadcast({
type: 'data_changed',
collection,
path,
value,
operation: 'push',
timestamp: Date.now()
});
res.json({ success: true, path, newLength: arr.length });
} catch (error) {
console.error('Push Error:', error);
res.status(500).json({ error: error.message });
}
}
/**
* Stats endpoint
*/
_handleStats(req, res) {
try {
let totalCollections = 0;
const databaseStats = {};
if (fs.existsSync(this.config.dataDir)) {
const dataFiles = fs.readdirSync(this.config.dataDir)
.filter(file => file.endsWith('.json'));
for (const file of dataFiles) {
const dbName = file.replace('.json', '');
const data = this._getDatabase(dbName);
const collections = Object.keys(data || {});
totalCollections += collections.length;
databaseStats[dbName] = {
collections: collections.length,
collectionsNames: collections,
size: JSON.stringify(data || {}).length
};
}
}
const stats = {
totalCollections,
databases: databaseStats,
dataDir: this.config.dataDir,
serverStarted: this.serverStartTime,
connectedClients: this.clients.size
};
collections.forEach(collection => {
const data = this.data[collection];
stats.collections[collection] = {
keys: Object.keys(data).length,
size: JSON.stringify(data).length
};
});
res.json(stats);
} catch (error) {
console.error('Stats Error:', error);
res.status(500).json({ error: error.message });
}
}
// ========== BACKUP SYSTEM ==========
/**
* Otomatik backup sistemi (24 saatte bir)
*/
_setupAutoBackup() {
// Backup dizinini oluştur
if (!fs.existsSync(this.config.backupDir)) {
fs.mkdirSync(this.config.backupDir, { recursive: true });
}
// 24 saatte bir backup al
this.backupInterval = setInterval(() => {
this._createBackup('auto');
this._cleanupOldBackups();
}, 24 * 60 * 60 * 1000); // 24 hours
console.log('CloudLoyalDB: Otomatik backup sistemi aktif (24 saatte bir)');
// İlk startup backup - sadece 10 dakika içinde backup yoksa
this._createStartupBackupIfNeeded();
}
/**
* Startup backup - sadece son 10 dakika içinde backup yoksa oluştur
*/
_createStartupBackupIfNeeded() {
try {
// Backup klasörünü kontrol et
if (!fs.existsSync(this.config.backupDir)) {
fs.mkdirSync(this.config.backupDir, { recursive: true });
this._createBackup('startup');
return;
}
// Son 10 dakika içindeki backup'ları kontrol et
const backupFiles = fs.readdirSync(this.config.backupDir);
const tenMinutesAgo = Date.now() - (10 * 60 * 1000); // 10 dakika
let recentBackupFound = false;
let recentBackupFile = '';
for (const file of backupFiles) {
const filePath = path.join(this.config.backupDir, file);
const stats = fs.statSync(filePath);
if (stats.mtimeMs > tenMinutesAgo) {
recentBackupFound = true;
recentBackupFile = file;
break;
}
}
if (recentBackupFound) {
console.log('CloudLoyalDB: Son 10 dakika içinde backup mevcut, yeni backup oluşturulmuyor:', recentBackupFile);
} else {
console.log('CloudLoyalDB: Son 10 dakika içinde backup bulunamadı, startup backup oluşturuluyor...');
this._createBackup('startup');
}
} catch (error) {
console.error('CloudLoyalDB: Startup backup kontrolü hatası:', error.message);
// Hata durumunda backup oluştur
this._createBackup('startup');
}
}
/**
* Backup oluştur
*/
_createBackup(type = 'manual', database = 'all') {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
if (database === 'all') {
// Tüm database'leri backup et
if (!fs.existsSync(this.config.dataDir)) {
console.log('CloudLoyalDB: data/ klasörü bulunamadı, boş backup oluşturuluyor');
return { success: true, backups: [] };
}
const dataFiles = fs.readdirSync(this.config.dataDir)
.filter(file => file.endsWith('.json'));
if (dataFiles.length === 0) {
console.log('CloudLoyalDB: data/ klasöründe JSON dosyası bulunamadı');
return { success: true, backups: [] };
}
const allBackups = [];
for (const file of dataFiles) {
const dbName = file.replace('.json', '');
const singleBackup = this._createSingleBackup(type, dbName, timestamp);
allBackups.push(singleBackup);
}
console.log(`CloudLoyalDB: ${allBackups.length} database backup'ı oluşturuldu`);
return { success: true, backups: allBackups };
} else {
// Tek database backup'ı
return this._createSingleBackup(type, database, timestamp);
}
} catch (error) {
console.error('Backup oluşturma hatası:', error);
throw error;
}
}
_createSingleBackup(type, database, timestamp) {
const backupFileName = `${database}-backup-${type}-${timestamp}.json`;
const backupPath = path.join(this.config.backupDir, backupFileName);
const data = this._getDatabase(database);
const backupData = {
metadata: {
type,
database,
createdAt: new Date().toISOString(),
originalFile: this.getDataFile(database),
totalCollections: Object.keys(data || {}).length,
serverVersion: '1.0.0'
},
data: data || {}
};
fs.writeFileSync(backupPath, JSON.stringify(backupData, null, 2), 'utf8');
console.log(`CloudLoyalDB: ${database}.json backup'ı oluşturuldu: ${backupFileName}`);
// WebSocket ile bildir
this._broadcast({
type: 'backup_completed',
database,
backupId: backupFileName,
timestamp: Date.now(),
backupType: type
});
return { success: true, backupFile: backupFileName, database };
}
/**
* 2 günden eski backup'ları sil
*/
_cleanupOldBackups() {
try {
const files = fs.readdirSync(this.config.backupDir);
const twoDaysAgo = Date.now() - (2 * 24 * 60 * 60 * 1000); // 2 days in milliseconds
let deletedCount = 0;
files.forEach(file => {
if (file.startsWith('flydb-backup-') && file.endsWith('.json')) {
const filePath = path.join(this.config.backupDir, file);
const stats = fs.statSync(filePath);
if (stats.mtime.getTime() < twoDaysAgo) {
fs.unlinkSync(filePath);
deletedCount++;
console.log(`CloudLoyalDB: Eski backup silindi: ${file}`);
}
}
});
if (deletedCount > 0) {
console.log(`CloudLoyalDB: ${deletedCount} eski backup temizlendi (2+ gün)`);
}
} catch (error) {
console.error('Backup cleanup hatası:', error);
}
}
/**
* Manuel backup endpoint
*/
_handleManualBackup(req, res) {
try {
const backupFileName = this._createBackup('manual');
res.json({
success: true,
backupFile: backupFileName,
path: path.join(this.config.backupDir, backupFileName)
});
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* Backup listesi
*/
_handleListBackups(req, res) {
try {
const files = fs.readdirSync(this.config.backupDir);
const backups = files
.filter(file => file.startsWith('flydb-backup-') && file.endsWith('.json'))
.map(file => {
const filePath = path.join(this.config.backupDir, file);
const stats = fs.statSync(filePath);
return {
filename: file,
size: stats.size,
createdAt: stats.mtime.toISOString(),
age: Date.now() - stats.mtime.getTime()
};
})
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
res.json({ backups });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
// ========== HELPER METHODS ==========
/**
* Nested value setter
*/
_setNestedValue(obj, path, value) {
const keys = path.split('.');
const lastKey = keys.pop();
let current = obj;
for (const key of keys) {
if (!current[key] || typeof current[key] !== 'object' || Array.isArray(current[key])) {
current[key] = {};
}
current = current[key];
}
current[lastKey] = value;
}
/**
* Nested value getter
*/
_getNestedValue(obj, path) {
if (!path) return obj;
const keys = path.split('.');
let current = obj;
for (const key of keys) {
if (current === null || current === undefined || typeof current !== 'object') {
return undefined;
}
current = current[key];
}
return current;
}
/**
* Server'ı başlat
*/
async start() {
try {
this.serverStartTime = new Date().toISOString();
this.app.listen(this.config.port, () => {
console.log(`CloudLoyalDB Server çalışıyor:`);
console.log(` HTTP API: http://localhost:${this.config.port}/api`);
console.log(` WebSocket: ws://localhost:${this.config.wsPort}`);
console.log(` Data Dir: ${this.config.dataDir}`);
console.log(` Backup Dir: ${this.config.backupDir}`);
// Database bilgilerini göster
if (fs.existsSync(this.config.dataDir)) {
const dataFiles = fs.readdirSync(this.config.dataDir)
.filter(file => file.endsWith('.json'));
console.log(` Databases: ${dataFiles.length}`);
dataFiles.forEach(file => {
const dbName = file.replace('.json', '');
const data = this._getDatabase(dbName);
const collections = Object.keys(data || {}).length;
console.log(` - ${dbName}: ${collections} collections`);
});
} else {
console.log(` Databases: 0 (data/ klasörü bulunamadı)`);
}
});
} catch (error) {
console.error('Server başlatma hatası:', error);
process.exit(1);
}
}
/**
* Server'ı durdur
*/
stop() {
if (this.backupInterval) {
clearInterval(this.backupInterval);
}
if (this.wss) {
this.wss.close();
}
console.log('CloudLoyalDB Server durduruldu');
}
/**
* UNPUSH - Array'den değer silme
*/
_handleUnpush(req, res) {
try {
const { collection, path, value } = req.body;
if (!collection || !path) {
return res.status(400).json({ error: 'Collection ve path gereklidir' });
}
if (!this.data[collection]) {
return res.status(404).json({ error: 'Collection bulunamadı' });
}
let arr = this._getNestedValue(this.data[collection], path);
if (Array.isArray(arr)) {
const index = arr.indexOf(value);
if (index !== -1) {
arr.splice(index, 1);
this._setNestedValue(this.data[collection], path, arr);
this._saveData();
}
}
this._broadcast({
type: 'data_changed',
collection, path, value,
operation: 'unpush',
timestamp: Date.now()
});
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* HAS - Veri kontrolü
*/
_handleHas(req, res) {
try {
const { collection, path } = req.query;
if (!collection) {
return res.status(400).json({ error: 'Collection gereklidir' });
}
if (!this.data[collection]) {
return res.json({ exists: false });
}
const value = path ?
this._getNestedValue(this.data[collection], path) :
this.data[collection];
res.json({ exists: value !== undefined });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* DELETE - Veri silme
*/
_handleDelete(req, res) {
try {
const { collection, path } = req.body;
if (!collection || !path) {
return res.status(400).json({ error: 'Collection ve path gereklidir' });
}
if (!this.data[collection]) {
return res.status(404).json({ error: 'Collection bulunamadı' });
}
const deleted = this._deleteNestedValue(this.data[collection], path);
if (deleted) {
this._saveData();
this._broadcast({
type: 'data_changed',
collection, path,
operation: 'delete',
timestamp: Date.now()
});
}
res.json({ success: deleted });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* DELETEALL - Koleksiyon temizleme
*/
_handleDeleteAll(req, res) {
try {
const { collection } = req.body;
if (!collection) {
return res.status(400).json({ error: 'Collection gereklidir' });
}
this.data[collection] = {};
this._saveData();
this._broadcast({
type: 'collection_cleared',
collection,
timestamp: Date.now()
});
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* DELBYPRIORITY - Index ile array elemanı silme
*/
_handleDelByPriority(req, res) {
try {
const { collection, path, index } = req.body;
if (!collection || !path || typeof index !== 'number') {
return res.status(400).json({ error: 'Collection, path ve index gereklidir' });
}
if (!this.data[collection]) {
return res.status(404).json({ error: 'Collection bulunamadı' });
}
let arr = this._getNestedValue(this.data[collection], path);
if (Array.isArray(arr) && index >= 0 && index < arr.length) {
arr.splice(index, 1);
this._setNestedValue(this.data[collection], path, arr);
this._saveData();
}
this._broadcast({
type: 'data_changed',
collection, path, index,
operation: 'delByPriority',
timestamp: Date.now()
});
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* SETBYPRIORITY - Index ile array elemanı güncelleme
*/
_handleSetByPriority(req, res) {
try {
const { collection, path, value, index } = req.body;
if (!collection || !path || typeof index !== 'number') {
return res.status(400).json({ error: 'Collection, path, value ve index gereklidir' });
}
if (!this.data[collection]) {
this.data[collection] = {};
}
let arr = this._getNestedValue(this.data[collection], path);
if (!Array.isArray(arr)) {
arr = [];
}
// Array'i genişlet (gerekirse)
while (arr.length <= index) {
arr.push(null);
}
arr[index] = value;
this._setNestedValue(this.data[collection], path, arr);
this._saveData();
this._broadcast({
type: 'data_changed',
collection, path, value, index,
operation: 'setByPriority',
timestamp: Date.now()
});
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* RESTORE - Backup geri yükleme
*/
_handleRestoreBackup(req, res) {
try {
const { backupFileName } = req.body;
if (!backupFileName) {
return res.status(400).json({ error: 'Backup dosya adı gereklidir' });
}
const backupPath = path.join(this.config.backupDir, backupFileName);
if (!fs.existsSync(backupPath)) {
return res.status(404).json({ error: 'Backup dosyası bulunamadı' });
}
// Güvenlik backup'ı al
this._createBackup('safety-restore');
// Backup'ı yükle
const backupString = fs.readFileSync(backupPath, 'utf8');
const backupData = JSON.parse(backupString);
this.data = backupData.data;
this._saveData();
console.log('CloudLoyalDB: Backup geri yüklendi:', backupFileName);
this._broadcast({
type: 'backup_restored',
backupFileName,
timestamp: Date.now()
});
res.json({
success: true,
restoredFrom: backupData.metadata,
restoredAt: new Date().toISOString()
});
} catch (error) {
res.status(500).json({ error: error.message });
}
}
/**
* Nested value operations helper
*/
_deleteNestedValue(obj, path) {
if (!path || !obj) return false;
const keys = path.split('.');
const lastKey = keys.pop();
let current = obj;
for (const key of keys) {
if (!current[key] || typeof current[key] !== 'object') {
return false;
}
current = current[key];
}
if (current && Object.prototype.hasOwnProperty.call(current, lastKey)) {
delete current[lastKey];
return true;
}
return false;
}
}
// Server'ı başlat
if (require.main === module) {
const server = new CloudLoyalDBServer({
port: process.env.PORT || 3000,
wsPort: process.env.WS_PORT || 3001,
corsOrigin: process.env.CORS_ORIGIN || '*'
});
server.start();
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\nCloudLoyalDB Server kapatılıyor...');
server.stop();
process.exit(0);
});
}
module.exports = CloudLoyalDBServer;