Fix jwt-token

This commit is contained in:
2025-09-23 13:12:17 +02:00
parent 44047f9c98
commit ee4d3503e5
5 changed files with 694 additions and 3 deletions

View File

@@ -0,0 +1,282 @@
/**
* 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;