diff --git a/data-retention-service/index.js b/data-retention-service/index.js index 013b1a2..e397b35 100644 --- a/data-retention-service/index.js +++ b/data-retention-service/index.js @@ -5,7 +5,6 @@ const cron = require('node-cron'); const { Op } = require('sequelize'); -require('dotenv').config(); // Initialize database connection const { initializeDatabase, getModels } = require('./database'); diff --git a/server/index.js b/server/index.js index 53f2287..d1490c1 100644 --- a/server/index.js +++ b/server/index.js @@ -135,10 +135,6 @@ app.use(errorHandler); // Socket.IO initialization initializeSocketHandlers(io); -// Initialize services -const dataRetentionService = require('./services/data-retention'); -console.log('โœ… Data retention service initialized'); - const PORT = process.env.PORT || 5000; // Migration runner diff --git a/server/services/data-retention.js b/server/services/data-retention.js deleted file mode 100644 index 5f7f9fa..0000000 --- a/server/services/data-retention.js +++ /dev/null @@ -1,282 +0,0 @@ -/** - * Data Retention Cleanup Service - * Automatically removes old data based on tenant retention policies - */ - -const cron = require('node-cron'); -const { Op } = require('sequelize'); - -class DataRetentionService { - constructor() { - this.isRunning = false; - this.lastCleanup = null; - - // Run cleanup daily at 2 AM - this.scheduleCleanup(); - } - - /** - * Schedule automatic cleanup - */ - scheduleCleanup() { - // Run at 2:00 AM every day - cron.schedule('0 2 * * *', async () => { - console.log('๐Ÿ—‘๏ธ Starting scheduled data retention cleanup...'); - await this.runCleanup(); - }); - - console.log('๐Ÿ“… Data retention cleanup scheduled for 2:00 AM daily'); - } - - /** - * Run cleanup for all tenants - */ - async runCleanup() { - if (this.isRunning) { - console.log('โš ๏ธ Data retention cleanup already running, skipping...'); - return; - } - - try { - this.isRunning = true; - const startTime = Date.now(); - console.log('๐Ÿ—‘๏ธ Starting data retention cleanup...'); - - const { Tenant } = require('../models'); - const tenants = await Tenant.findAll({ - where: { - is_active: true - } - }); - - let totalCleaned = { - detections: 0, - heartbeats: 0, - logs: 0, - sessions: 0 - }; - - for (const tenant of tenants) { - const retentionDays = tenant.features?.data_retention_days; - - // Skip tenants with unlimited retention (-1) - if (retentionDays === -1) { - console.log(`โญ๏ธ Skipping tenant ${tenant.slug} - unlimited retention`); - continue; - } - - console.log(`๐Ÿงน Cleaning data for tenant ${tenant.slug} (${retentionDays} days retention)`); - - const cleanupResult = await this.cleanupTenantData(tenant.id, retentionDays); - - totalCleaned.detections += cleanupResult.detections; - totalCleaned.heartbeats += cleanupResult.heartbeats; - totalCleaned.logs += cleanupResult.logs; - totalCleaned.sessions += cleanupResult.sessions; - } - - const duration = Date.now() - startTime; - this.lastCleanup = new Date(); - - console.log(`โœ… Data retention cleanup completed in ${duration}ms`); - console.log(`๐Ÿ“Š Cleaned up:`, totalCleaned); - - } catch (error) { - console.error('โŒ Error during data retention cleanup:', error); - } finally { - this.isRunning = false; - } - } - - /** - * Clean up data for a specific tenant - */ - async cleanupTenantData(tenantId, retentionDays) { - const cutoffDate = new Date(); - cutoffDate.setDate(cutoffDate.getDate() - retentionDays); - - console.log(`๐Ÿ—‘๏ธ Cleaning data older than ${cutoffDate.toISOString()} for tenant ${tenantId}`); - - const { DroneDetection, Heartbeat, ApiLog, Session } = require('../models'); - - const cleanupResults = { - detections: 0, - heartbeats: 0, - logs: 0, - sessions: 0 - }; - - try { - // Clean up drone detections - const deletedDetections = await DroneDetection.destroy({ - where: { - tenant_id: tenantId, - timestamp: { - [Op.lt]: cutoffDate - } - } - }); - cleanupResults.detections = deletedDetections; - - // Clean up heartbeats - const deletedHeartbeats = await Heartbeat.destroy({ - where: { - tenant_id: tenantId, - timestamp: { - [Op.lt]: cutoffDate - } - } - }); - cleanupResults.heartbeats = deletedHeartbeats; - - // Clean up API logs (if exists) - try { - const deletedLogs = await ApiLog.destroy({ - where: { - tenant_id: tenantId, - created_at: { - [Op.lt]: cutoffDate - } - } - }); - cleanupResults.logs = deletedLogs; - } catch (error) { - // ApiLog table might not exist, skip silently - console.log(`โญ๏ธ Skipping API logs cleanup for tenant ${tenantId} (table might not exist)`); - } - - // Clean up old sessions - try { - const deletedSessions = await Session.destroy({ - where: { - tenant_id: tenantId, - updated_at: { - [Op.lt]: cutoffDate - } - } - }); - cleanupResults.sessions = deletedSessions; - } catch (error) { - // Session table might not exist, skip silently - console.log(`โญ๏ธ Skipping sessions cleanup for tenant ${tenantId} (table might not exist)`); - } - - console.log(`โœ… Tenant ${tenantId} cleanup:`, cleanupResults); - - } catch (error) { - console.error(`โŒ Error cleaning data for tenant ${tenantId}:`, error); - } - - return cleanupResults; - } - - /** - * Manual cleanup for a specific tenant - */ - async manualCleanup(tenantId, retentionDays = null) { - const { Tenant } = require('../models'); - const tenant = await Tenant.findByPk(tenantId); - - if (!tenant) { - throw new Error('Tenant not found'); - } - - const days = retentionDays || tenant.features?.data_retention_days; - - if (days === -1) { - throw new Error('Tenant has unlimited retention, manual cleanup requires explicit retention days'); - } - - console.log(`๐Ÿงน Manual cleanup for tenant ${tenant.slug} (${days} days retention)`); - - return await this.cleanupTenantData(tenantId, days); - } - - /** - * Get cleanup statistics - */ - async getCleanupStats() { - const { Tenant, DroneDetection, Heartbeat } = require('../models'); - - const tenants = await Tenant.findAll({ - where: { is_active: true } - }); - - const stats = []; - - for (const tenant of tenants) { - const retentionDays = tenant.features?.data_retention_days; - - if (retentionDays === -1) { - stats.push({ - tenant_id: tenant.id, - tenant_slug: tenant.slug, - retention_days: 'unlimited', - old_detections: 0, - old_heartbeats: 0, - next_cleanup: 'never' - }); - continue; - } - - const cutoffDate = new Date(); - cutoffDate.setDate(cutoffDate.getDate() - retentionDays); - - const [oldDetections, oldHeartbeats] = await Promise.all([ - DroneDetection.count({ - where: { - tenant_id: tenant.id, - timestamp: { [Op.lt]: cutoffDate } - } - }), - Heartbeat.count({ - where: { - tenant_id: tenant.id, - timestamp: { [Op.lt]: cutoffDate } - } - }) - ]); - - stats.push({ - tenant_id: tenant.id, - tenant_slug: tenant.slug, - retention_days: retentionDays, - old_detections: oldDetections, - old_heartbeats: oldHeartbeats, - next_cleanup: this.getNextCleanupTime() - }); - } - - return { - last_cleanup: this.lastCleanup, - next_cleanup: this.getNextCleanupTime(), - is_running: this.isRunning, - tenant_stats: stats - }; - } - - /** - * Get next scheduled cleanup time - */ - getNextCleanupTime() { - const now = new Date(); - const tomorrow = new Date(now); - tomorrow.setDate(tomorrow.getDate() + 1); - tomorrow.setHours(2, 0, 0, 0); - - return tomorrow; - } - - /** - * Force immediate cleanup (for testing/admin use) - */ - async forceCleanup() { - console.log('๐Ÿšจ Force cleanup initiated'); - await this.runCleanup(); - } -} - -// Create singleton instance -const dataRetentionService = new DataRetentionService(); - -module.exports = dataRetentionService; \ No newline at end of file