Fix jwt-token
This commit is contained in:
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
const cron = require('node-cron');
|
const cron = require('node-cron');
|
||||||
const { Op } = require('sequelize');
|
const { Op } = require('sequelize');
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
// Initialize database connection
|
// Initialize database connection
|
||||||
const { initializeDatabase, getModels } = require('./database');
|
const { initializeDatabase, getModels } = require('./database');
|
||||||
|
|||||||
@@ -135,10 +135,6 @@ app.use(errorHandler);
|
|||||||
// Socket.IO initialization
|
// Socket.IO initialization
|
||||||
initializeSocketHandlers(io);
|
initializeSocketHandlers(io);
|
||||||
|
|
||||||
// Initialize services
|
|
||||||
const dataRetentionService = require('./services/data-retention');
|
|
||||||
console.log('✅ Data retention service initialized');
|
|
||||||
|
|
||||||
const PORT = process.env.PORT || 5000;
|
const PORT = process.env.PORT || 5000;
|
||||||
|
|
||||||
// Migration runner
|
// Migration runner
|
||||||
|
|||||||
@@ -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;
|
|
||||||
Reference in New Issue
Block a user