237 lines
8.0 KiB
Python
237 lines
8.0 KiB
Python
"""
|
|
Dashboard API for Calejo Control Adapter
|
|
Provides REST endpoints for configuration management and system monitoring
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from typing import Dict, Any, List, Optional
|
|
from fastapi import APIRouter, HTTPException, BackgroundTasks
|
|
from fastapi.responses import HTMLResponse
|
|
from pydantic import BaseModel, ValidationError
|
|
|
|
from config.settings import Settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# API Router
|
|
dashboard_router = APIRouter(prefix="/api/v1/dashboard", tags=["dashboard"])
|
|
|
|
# Pydantic models for configuration
|
|
class DatabaseConfig(BaseModel):
|
|
db_host: str = "localhost"
|
|
db_port: int = 5432
|
|
db_name: str = "calejo"
|
|
db_user: str = "calejo"
|
|
db_password: str = ""
|
|
|
|
class OPCUAConfig(BaseModel):
|
|
enabled: bool = True
|
|
host: str = "localhost"
|
|
port: int = 4840
|
|
|
|
class ModbusConfig(BaseModel):
|
|
enabled: bool = True
|
|
host: str = "localhost"
|
|
port: int = 502
|
|
unit_id: int = 1
|
|
|
|
class RESTAPIConfig(BaseModel):
|
|
enabled: bool = True
|
|
host: str = "0.0.0.0"
|
|
port: int = 8080
|
|
cors_enabled: bool = True
|
|
|
|
class MonitoringConfig(BaseModel):
|
|
health_monitor_port: int = 9090
|
|
metrics_enabled: bool = True
|
|
|
|
class SecurityConfig(BaseModel):
|
|
jwt_secret_key: str = ""
|
|
api_key: str = ""
|
|
|
|
class SystemConfig(BaseModel):
|
|
database: DatabaseConfig
|
|
opcua: OPCUAConfig
|
|
modbus: ModbusConfig
|
|
rest_api: RESTAPIConfig
|
|
monitoring: MonitoringConfig
|
|
security: SecurityConfig
|
|
|
|
class ValidationResult(BaseModel):
|
|
valid: bool
|
|
errors: List[str] = []
|
|
warnings: List[str] = []
|
|
|
|
class DashboardStatus(BaseModel):
|
|
application_status: str
|
|
database_status: str
|
|
opcua_status: str
|
|
modbus_status: str
|
|
rest_api_status: str
|
|
monitoring_status: str
|
|
|
|
# Global settings instance
|
|
settings = Settings()
|
|
|
|
@dashboard_router.get("/config", response_model=SystemConfig)
|
|
async def get_configuration():
|
|
"""Get current system configuration"""
|
|
try:
|
|
config = SystemConfig(
|
|
database=DatabaseConfig(
|
|
db_host=settings.db_host,
|
|
db_port=settings.db_port,
|
|
db_name=settings.db_name,
|
|
db_user=settings.db_user,
|
|
db_password="********" # Don't expose actual password
|
|
),
|
|
opcua=OPCUAConfig(
|
|
enabled=settings.opcua_enabled,
|
|
host=settings.opcua_host,
|
|
port=settings.opcua_port
|
|
),
|
|
modbus=ModbusConfig(
|
|
enabled=settings.modbus_enabled,
|
|
host=settings.modbus_host,
|
|
port=settings.modbus_port,
|
|
unit_id=settings.modbus_unit_id
|
|
),
|
|
rest_api=RESTAPIConfig(
|
|
enabled=settings.rest_api_enabled,
|
|
host=settings.rest_api_host,
|
|
port=settings.rest_api_port,
|
|
cors_enabled=settings.rest_api_cors_enabled
|
|
),
|
|
monitoring=MonitoringConfig(
|
|
health_monitor_port=settings.health_monitor_port,
|
|
metrics_enabled=True
|
|
),
|
|
security=SecurityConfig(
|
|
jwt_secret_key="********",
|
|
api_key="********"
|
|
)
|
|
)
|
|
return config
|
|
except Exception as e:
|
|
logger.error(f"Error getting configuration: {str(e)}")
|
|
raise HTTPException(status_code=500, detail="Failed to retrieve configuration")
|
|
|
|
@dashboard_router.post("/config", response_model=ValidationResult)
|
|
async def update_configuration(config: SystemConfig, background_tasks: BackgroundTasks):
|
|
"""Update system configuration"""
|
|
try:
|
|
# Validate configuration
|
|
validation_result = validate_configuration(config)
|
|
|
|
if validation_result.valid:
|
|
# Save configuration in background
|
|
background_tasks.add_task(save_configuration, config)
|
|
|
|
return validation_result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error updating configuration: {str(e)}")
|
|
raise HTTPException(status_code=500, detail="Failed to update configuration")
|
|
|
|
@dashboard_router.get("/status", response_model=DashboardStatus)
|
|
async def get_system_status():
|
|
"""Get current system status"""
|
|
try:
|
|
# This would integrate with the health monitor
|
|
# For now, return mock status
|
|
status = DashboardStatus(
|
|
application_status="running",
|
|
database_status="connected",
|
|
opcua_status="listening",
|
|
modbus_status="listening",
|
|
rest_api_status="running",
|
|
monitoring_status="active"
|
|
)
|
|
return status
|
|
except Exception as e:
|
|
logger.error(f"Error getting system status: {str(e)}")
|
|
raise HTTPException(status_code=500, detail="Failed to retrieve system status")
|
|
|
|
@dashboard_router.post("/restart")
|
|
async def restart_system():
|
|
"""Restart the system (admin only)"""
|
|
# This would trigger a system restart
|
|
# For now, just log the request
|
|
logger.info("System restart requested via dashboard")
|
|
return {"message": "Restart request received", "status": "pending"}
|
|
|
|
@dashboard_router.get("/backup")
|
|
async def create_backup():
|
|
"""Create a system backup"""
|
|
# This would trigger the backup script
|
|
logger.info("Backup requested via dashboard")
|
|
return {"message": "Backup initiated", "status": "in_progress"}
|
|
|
|
@dashboard_router.get("/logs")
|
|
async def get_system_logs(limit: int = 100):
|
|
"""Get system logs"""
|
|
try:
|
|
# This would read from the application logs
|
|
# For now, return mock logs
|
|
logs = [
|
|
{"timestamp": "2024-01-01T10:00:00", "level": "INFO", "message": "System started"},
|
|
{"timestamp": "2024-01-01T10:01:00", "level": "INFO", "message": "Database connected"},
|
|
{"timestamp": "2024-01-01T10:02:00", "level": "INFO", "message": "OPC UA server started"}
|
|
]
|
|
return {"logs": logs[:limit]}
|
|
except Exception as e:
|
|
logger.error(f"Error getting logs: {str(e)}")
|
|
raise HTTPException(status_code=500, detail="Failed to retrieve logs")
|
|
|
|
def validate_configuration(config: SystemConfig) -> ValidationResult:
|
|
"""Validate configuration before applying"""
|
|
errors = []
|
|
warnings = []
|
|
|
|
# Database validation
|
|
if not config.database.db_host:
|
|
errors.append("Database host is required")
|
|
if not config.database.db_name:
|
|
errors.append("Database name is required")
|
|
if not config.database.db_user:
|
|
errors.append("Database user is required")
|
|
|
|
# Port validation
|
|
if not (1 <= config.database.db_port <= 65535):
|
|
errors.append("Database port must be between 1 and 65535")
|
|
if not (1 <= config.opcua.port <= 65535):
|
|
errors.append("OPC UA port must be between 1 and 65535")
|
|
if not (1 <= config.modbus.port <= 65535):
|
|
errors.append("Modbus port must be between 1 and 65535")
|
|
if not (1 <= config.rest_api.port <= 65535):
|
|
errors.append("REST API port must be between 1 and 65535")
|
|
if not (1 <= config.monitoring.health_monitor_port <= 65535):
|
|
errors.append("Health monitor port must be between 1 and 65535")
|
|
|
|
# Security warnings
|
|
if config.security.jwt_secret_key == "your-secret-key-change-in-production":
|
|
warnings.append("Default JWT secret key detected - please change for production")
|
|
if config.security.api_key == "your-api-key-here":
|
|
warnings.append("Default API key detected - please change for production")
|
|
|
|
return ValidationResult(
|
|
valid=len(errors) == 0,
|
|
errors=errors,
|
|
warnings=warnings
|
|
)
|
|
|
|
def save_configuration(config: SystemConfig):
|
|
"""Save configuration to settings file"""
|
|
try:
|
|
# This would update the settings file
|
|
# For now, just log the configuration
|
|
logger.info(f"Configuration update received: {config.json(indent=2)}")
|
|
|
|
# In a real implementation, this would:
|
|
# 1. Update the settings file
|
|
# 2. Restart affected services
|
|
# 3. Verify the new configuration
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error saving configuration: {str(e)}") |