Fix jwt-token

This commit is contained in:
2025-09-23 15:03:31 +02:00
parent ea5583d56a
commit e3816b056e
4 changed files with 333 additions and 1 deletions

View File

@@ -5,6 +5,8 @@
const cron = require('node-cron'); const cron = require('node-cron');
const { Op } = require('sequelize'); const { Op } = require('sequelize');
const http = require('http');
const url = require('url');
// Initialize database connection // Initialize database connection
const { initializeDatabase, getModels } = require('./database'); const { initializeDatabase, getModels } = require('./database');
@@ -46,6 +48,9 @@ class DataRetentionService {
console.log('⏰ Scheduled cleanup: Daily at 2:00 AM UTC'); console.log('⏰ Scheduled cleanup: Daily at 2:00 AM UTC');
// Start metrics HTTP server
this.startMetricsServer();
// Run immediate cleanup in development or if IMMEDIATE_CLEANUP is set // Run immediate cleanup in development or if IMMEDIATE_CLEANUP is set
if (process.env.NODE_ENV === 'development' || process.env.IMMEDIATE_CLEANUP === 'true') { if (process.env.NODE_ENV === 'development' || process.env.IMMEDIATE_CLEANUP === 'true') {
console.log('🧹 Running immediate cleanup...'); console.log('🧹 Running immediate cleanup...');
@@ -233,6 +238,125 @@ class DataRetentionService {
}; };
} }
/**
* Get detailed metrics for dashboard
*/
getMetrics() {
const uptime = Math.floor(process.uptime());
const memoryUsage = process.memoryUsage();
return {
service: {
name: 'data-retention-service',
version: '1.0.0',
status: 'running',
uptime: uptime,
uptimeFormatted: this.formatUptime(uptime)
},
performance: {
memoryUsage: {
heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024),
heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024),
external: Math.round(memoryUsage.external / 1024 / 1024),
rss: Math.round(memoryUsage.rss / 1024 / 1024)
},
cpuUsage: process.cpuUsage()
},
cleanup: {
lastRun: this.lastCleanup,
lastRunFormatted: this.lastCleanup ? new Date(this.lastCleanup).toLocaleString() : null,
isCurrentlyRunning: this.isRunning,
nextScheduledRun: '2:00 AM UTC daily',
stats: this.cleanupStats
},
schedule: {
cronExpression: '0 2 * * *',
timezone: 'UTC',
description: 'Daily cleanup at 2:00 AM UTC'
}
};
}
/**
* Format uptime in human readable format
*/
formatUptime(seconds) {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (days > 0) {
return `${days}d ${hours}h ${minutes}m ${secs}s`;
} else if (hours > 0) {
return `${hours}h ${minutes}m ${secs}s`;
} else if (minutes > 0) {
return `${minutes}m ${secs}s`;
} else {
return `${secs}s`;
}
}
/**
* Start HTTP server for metrics endpoint
*/
startMetricsServer() {
const port = process.env.METRICS_PORT || 3001;
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Content-Type', 'application/json');
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}
if (req.method === 'GET') {
if (parsedUrl.pathname === '/metrics') {
// Detailed metrics for dashboard
res.writeHead(200);
res.end(JSON.stringify(this.getMetrics(), null, 2));
} else if (parsedUrl.pathname === '/health') {
// Simple health check
res.writeHead(200);
res.end(JSON.stringify({
status: 'healthy',
uptime: Math.floor(process.uptime()),
lastCleanup: this.lastCleanup,
isRunning: this.isRunning
}, null, 2));
} else if (parsedUrl.pathname === '/stats') {
// Basic stats
res.writeHead(200);
res.end(JSON.stringify(this.getStats(), null, 2));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: 'Not found' }));
}
} else {
res.writeHead(405);
res.end(JSON.stringify({ error: 'Method not allowed' }));
}
});
server.listen(port, () => {
console.log(`📊 Metrics server listening on port ${port}`);
console.log(`📊 Endpoints: /health, /metrics, /stats`);
});
return server;
}
/** /**
* Graceful shutdown * Graceful shutdown
*/ */

View File

@@ -72,6 +72,8 @@ services:
RATE_LIMIT_WINDOW_MS: ${RATE_LIMIT_WINDOW_MS:-900000} RATE_LIMIT_WINDOW_MS: ${RATE_LIMIT_WINDOW_MS:-900000}
RATE_LIMIT_MAX_REQUESTS: ${RATE_LIMIT_MAX_REQUESTS:-1000} RATE_LIMIT_MAX_REQUESTS: ${RATE_LIMIT_MAX_REQUESTS:-1000}
SECURITY_LOG_DIR: /app/logs SECURITY_LOG_DIR: /app/logs
DATA_RETENTION_HOST: data-retention
DATA_RETENTION_PORT: 3001
ports: ports:
- "3002:3001" - "3002:3001"
volumes: volumes:
@@ -188,6 +190,9 @@ services:
DB_PASSWORD: ${DB_PASSWORD:-your_secure_password} DB_PASSWORD: ${DB_PASSWORD:-your_secure_password}
NODE_ENV: ${NODE_ENV:-production} NODE_ENV: ${NODE_ENV:-production}
IMMEDIATE_CLEANUP: ${IMMEDIATE_CLEANUP:-false} IMMEDIATE_CLEANUP: ${IMMEDIATE_CLEANUP:-false}
METRICS_PORT: 3001
ports:
- "3004:3001" # Expose metrics port
networks: networks:
- drone-network - drone-network
depends_on: depends_on:

View File

@@ -0,0 +1,192 @@
const express = require('express');
const router = express.Router();
const http = require('http');
/**
* Data Retention Metrics Proxy
* Proxies requests to the data retention microservice
*/
const DATA_RETENTION_HOST = process.env.DATA_RETENTION_HOST || 'data-retention';
const DATA_RETENTION_PORT = process.env.DATA_RETENTION_PORT || 3001;
/**
* Make HTTP request to data retention service
*/
function makeRequest(path) {
return new Promise((resolve, reject) => {
const options = {
hostname: DATA_RETENTION_HOST,
port: DATA_RETENTION_PORT,
path: path,
method: 'GET',
timeout: 5000
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
const result = JSON.parse(data);
resolve({ status: res.statusCode, data: result });
} catch (error) {
reject(new Error(`Failed to parse response: ${error.message}`));
}
});
});
req.on('error', (error) => {
reject(new Error(`Request failed: ${error.message}`));
});
req.on('timeout', () => {
req.destroy();
reject(new Error('Request timeout'));
});
req.end();
});
}
/**
* GET /api/data-retention/metrics
* Get detailed metrics from data retention service
*/
router.get('/metrics', async (req, res) => {
try {
const response = await makeRequest('/metrics');
if (response.status === 200) {
res.json({
success: true,
data: response.data,
timestamp: new Date().toISOString()
});
} else {
res.status(response.status).json({
success: false,
error: 'Failed to fetch metrics from data retention service'
});
}
} catch (error) {
console.error('Data retention metrics error:', error);
res.status(503).json({
success: false,
error: 'Data retention service unavailable',
details: error.message
});
}
});
/**
* GET /api/data-retention/health
* Get health status from data retention service
*/
router.get('/health', async (req, res) => {
try {
const response = await makeRequest('/health');
if (response.status === 200) {
res.json({
success: true,
data: response.data,
timestamp: new Date().toISOString()
});
} else {
res.status(response.status).json({
success: false,
error: 'Failed to fetch health from data retention service'
});
}
} catch (error) {
console.error('Data retention health error:', error);
res.status(503).json({
success: false,
error: 'Data retention service unavailable',
details: error.message
});
}
});
/**
* GET /api/data-retention/stats
* Get basic statistics from data retention service
*/
router.get('/stats', async (req, res) => {
try {
const response = await makeRequest('/stats');
if (response.status === 200) {
res.json({
success: true,
data: response.data,
timestamp: new Date().toISOString()
});
} else {
res.status(response.status).json({
success: false,
error: 'Failed to fetch stats from data retention service'
});
}
} catch (error) {
console.error('Data retention stats error:', error);
res.status(503).json({
success: false,
error: 'Data retention service unavailable',
details: error.message
});
}
});
/**
* GET /api/data-retention/status
* Get combined status including service connectivity
*/
router.get('/status', async (req, res) => {
try {
const [healthResponse, metricsResponse] = await Promise.allSettled([
makeRequest('/health'),
makeRequest('/metrics')
]);
const result = {
success: true,
service: {
name: 'data-retention-service',
connected: false,
error: null
},
health: null,
metrics: null,
timestamp: new Date().toISOString()
};
if (healthResponse.status === 'fulfilled' && healthResponse.value.status === 200) {
result.service.connected = true;
result.health = healthResponse.value.data;
} else {
result.service.error = healthResponse.reason?.message || 'Health check failed';
}
if (metricsResponse.status === 'fulfilled' && metricsResponse.value.status === 200) {
result.metrics = metricsResponse.value.data;
}
res.json(result);
} catch (error) {
console.error('Data retention status error:', error);
res.status(503).json({
success: false,
error: 'Failed to get data retention service status',
details: error.message
});
}
});
module.exports = router;

View File

@@ -16,6 +16,7 @@ const detectorsRoutes = require('./detectors');
const detectionsRoutes = require('./detections'); const detectionsRoutes = require('./detections');
const droneTypesRoutes = require('./droneTypes'); const droneTypesRoutes = require('./droneTypes');
const tenantDebugRoutes = require('./tenant-debug'); const tenantDebugRoutes = require('./tenant-debug');
const dataRetentionRoutes = require('./dataRetention');
// Management portal routes (before API versioning) // Management portal routes (before API versioning)
router.use('/management', managementRoutes); router.use('/management', managementRoutes);
@@ -49,6 +50,7 @@ router.use('/detectors', detectorsRoutes);
router.use('/detections', detectionsRoutes); router.use('/detections', detectionsRoutes);
router.use('/drone-types', droneTypesRoutes); router.use('/drone-types', droneTypesRoutes);
router.use('/tenant-debug', tenantDebugRoutes); router.use('/tenant-debug', tenantDebugRoutes);
router.use('/data-retention', dataRetentionRoutes);
// API documentation endpoint // API documentation endpoint
router.get('/', (req, res) => { router.get('/', (req, res) => {
@@ -64,7 +66,16 @@ router.get('/', (req, res) => {
dashboard: '/api/dashboard', dashboard: '/api/dashboard',
health: '/api/health', health: '/api/health',
'device-health': '/api/device-health', 'device-health': '/api/device-health',
'drone-types': '/api/drone-types' 'drone-types': '/api/drone-types',
'data-retention': '/api/data-retention'
},
microservices: {
'data-retention': {
status: '/api/data-retention/status',
health: '/api/data-retention/health',
metrics: '/api/data-retention/metrics',
stats: '/api/data-retention/stats'
}
}, },
documentation: '/api/docs' documentation: '/api/docs'
}); });