mirror of
https://github.com/aljazceru/enclava.git
synced 2025-12-17 23:44:24 +01:00
clean commit
This commit is contained in:
499
backend/app/models/module.py
Normal file
499
backend/app/models/module.py
Normal file
@@ -0,0 +1,499 @@
|
||||
"""
|
||||
Module model for tracking installed modules and their configurations
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, Any, List
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Boolean, JSON, Text
|
||||
from app.db.database import Base
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ModuleStatus(str, Enum):
|
||||
"""Module status types"""
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
ERROR = "error"
|
||||
LOADING = "loading"
|
||||
DISABLED = "disabled"
|
||||
|
||||
|
||||
class ModuleType(str, Enum):
|
||||
"""Module type categories"""
|
||||
CORE = "core"
|
||||
INTERCEPTOR = "interceptor"
|
||||
ANALYTICS = "analytics"
|
||||
SECURITY = "security"
|
||||
STORAGE = "storage"
|
||||
INTEGRATION = "integration"
|
||||
CUSTOM = "custom"
|
||||
|
||||
|
||||
class Module(Base):
|
||||
"""Module model for tracking installed modules and their configurations"""
|
||||
|
||||
__tablename__ = "modules"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String, unique=True, index=True, nullable=False)
|
||||
display_name = Column(String, nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
|
||||
# Module classification
|
||||
module_type = Column(String, default=ModuleType.CUSTOM)
|
||||
category = Column(String, nullable=True) # cache, rag, analytics, etc.
|
||||
|
||||
# Module details
|
||||
version = Column(String, nullable=False)
|
||||
author = Column(String, nullable=True)
|
||||
license = Column(String, nullable=True)
|
||||
|
||||
# Module status
|
||||
status = Column(String, default=ModuleStatus.INACTIVE)
|
||||
is_enabled = Column(Boolean, default=False)
|
||||
is_core = Column(Boolean, default=False) # Core modules cannot be disabled
|
||||
|
||||
# Configuration
|
||||
config_schema = Column(JSON, default=dict) # JSON schema for configuration
|
||||
config_values = Column(JSON, default=dict) # Current configuration values
|
||||
default_config = Column(JSON, default=dict) # Default configuration
|
||||
|
||||
# Dependencies
|
||||
dependencies = Column(JSON, default=list) # List of module dependencies
|
||||
conflicts = Column(JSON, default=list) # List of conflicting modules
|
||||
|
||||
# Installation details
|
||||
install_path = Column(String, nullable=True)
|
||||
entry_point = Column(String, nullable=True) # Main module entry point
|
||||
|
||||
# Interceptor configuration
|
||||
interceptor_chains = Column(JSON, default=list) # Which chains this module hooks into
|
||||
execution_order = Column(Integer, default=100) # Order in interceptor chain
|
||||
|
||||
# API endpoints
|
||||
api_endpoints = Column(JSON, default=list) # List of API endpoints this module provides
|
||||
|
||||
# Permissions and security
|
||||
required_permissions = Column(JSON, default=list) # Permissions required to use this module
|
||||
security_level = Column(String, default="low") # low, medium, high, critical
|
||||
|
||||
# Metadata
|
||||
tags = Column(JSON, default=list)
|
||||
module_metadata = Column(JSON, default=dict)
|
||||
|
||||
# Runtime information
|
||||
last_error = Column(Text, nullable=True)
|
||||
error_count = Column(Integer, default=0)
|
||||
last_started = Column(DateTime, nullable=True)
|
||||
last_stopped = Column(DateTime, nullable=True)
|
||||
|
||||
# Statistics
|
||||
request_count = Column(Integer, default=0)
|
||||
success_count = Column(Integer, default=0)
|
||||
error_count_runtime = Column(Integer, default=0)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
installed_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Module(id={self.id}, name='{self.name}', status='{self.status}')>"
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert module to dictionary for API responses"""
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"display_name": self.display_name,
|
||||
"description": self.description,
|
||||
"module_type": self.module_type,
|
||||
"category": self.category,
|
||||
"version": self.version,
|
||||
"author": self.author,
|
||||
"license": self.license,
|
||||
"status": self.status,
|
||||
"is_enabled": self.is_enabled,
|
||||
"is_core": self.is_core,
|
||||
"config_schema": self.config_schema,
|
||||
"config_values": self.config_values,
|
||||
"default_config": self.default_config,
|
||||
"dependencies": self.dependencies,
|
||||
"conflicts": self.conflicts,
|
||||
"install_path": self.install_path,
|
||||
"entry_point": self.entry_point,
|
||||
"interceptor_chains": self.interceptor_chains,
|
||||
"execution_order": self.execution_order,
|
||||
"api_endpoints": self.api_endpoints,
|
||||
"required_permissions": self.required_permissions,
|
||||
"security_level": self.security_level,
|
||||
"tags": self.tags,
|
||||
"metadata": self.module_metadata,
|
||||
"last_error": self.last_error,
|
||||
"error_count": self.error_count,
|
||||
"last_started": self.last_started.isoformat() if self.last_started else None,
|
||||
"last_stopped": self.last_stopped.isoformat() if self.last_stopped else None,
|
||||
"request_count": self.request_count,
|
||||
"success_count": self.success_count,
|
||||
"error_count_runtime": self.error_count_runtime,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
"installed_at": self.installed_at.isoformat() if self.installed_at else None,
|
||||
"success_rate": self.get_success_rate(),
|
||||
"uptime": self.get_uptime_seconds() if self.is_running() else 0
|
||||
}
|
||||
|
||||
def is_running(self) -> bool:
|
||||
"""Check if module is currently running"""
|
||||
return self.status == ModuleStatus.ACTIVE
|
||||
|
||||
def is_healthy(self) -> bool:
|
||||
"""Check if module is healthy (running without recent errors)"""
|
||||
return self.is_running() and self.error_count_runtime == 0
|
||||
|
||||
def get_success_rate(self) -> float:
|
||||
"""Get success rate as percentage"""
|
||||
if self.request_count == 0:
|
||||
return 100.0
|
||||
return (self.success_count / self.request_count) * 100
|
||||
|
||||
def get_uptime_seconds(self) -> int:
|
||||
"""Get uptime in seconds"""
|
||||
if not self.last_started:
|
||||
return 0
|
||||
return int((datetime.utcnow() - self.last_started).total_seconds())
|
||||
|
||||
def can_be_disabled(self) -> bool:
|
||||
"""Check if module can be disabled"""
|
||||
return not self.is_core
|
||||
|
||||
def has_dependency(self, module_name: str) -> bool:
|
||||
"""Check if module has a specific dependency"""
|
||||
return module_name in self.dependencies
|
||||
|
||||
def conflicts_with(self, module_name: str) -> bool:
|
||||
"""Check if module conflicts with another module"""
|
||||
return module_name in self.conflicts
|
||||
|
||||
def requires_permission(self, permission: str) -> bool:
|
||||
"""Check if module requires a specific permission"""
|
||||
return permission in self.required_permissions
|
||||
|
||||
def hooks_into_chain(self, chain_name: str) -> bool:
|
||||
"""Check if module hooks into a specific interceptor chain"""
|
||||
return chain_name in self.interceptor_chains
|
||||
|
||||
def provides_endpoint(self, endpoint: str) -> bool:
|
||||
"""Check if module provides a specific API endpoint"""
|
||||
return endpoint in self.api_endpoints
|
||||
|
||||
def update_config(self, config_updates: Dict[str, Any]):
|
||||
"""Update module configuration"""
|
||||
if self.config_values is None:
|
||||
self.config_values = {}
|
||||
self.config_values.update(config_updates)
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def reset_config(self):
|
||||
"""Reset configuration to default values"""
|
||||
self.config_values = self.default_config.copy() if self.default_config else {}
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def enable(self):
|
||||
"""Enable the module"""
|
||||
if self.status != ModuleStatus.ERROR:
|
||||
self.is_enabled = True
|
||||
self.status = ModuleStatus.LOADING
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def disable(self):
|
||||
"""Disable the module"""
|
||||
if self.can_be_disabled():
|
||||
self.is_enabled = False
|
||||
self.status = ModuleStatus.DISABLED
|
||||
self.last_stopped = datetime.utcnow()
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def start(self):
|
||||
"""Start the module"""
|
||||
self.status = ModuleStatus.ACTIVE
|
||||
self.last_started = datetime.utcnow()
|
||||
self.last_error = None
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def stop(self):
|
||||
"""Stop the module"""
|
||||
self.status = ModuleStatus.INACTIVE
|
||||
self.last_stopped = datetime.utcnow()
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def set_error(self, error_message: str):
|
||||
"""Set module error status"""
|
||||
self.status = ModuleStatus.ERROR
|
||||
self.last_error = error_message
|
||||
self.error_count += 1
|
||||
self.error_count_runtime += 1
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def clear_error(self):
|
||||
"""Clear error status"""
|
||||
self.last_error = None
|
||||
self.error_count_runtime = 0
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
def record_request(self, success: bool = True):
|
||||
"""Record a request to this module"""
|
||||
self.request_count += 1
|
||||
if success:
|
||||
self.success_count += 1
|
||||
else:
|
||||
self.error_count_runtime += 1
|
||||
|
||||
def add_tag(self, tag: str):
|
||||
"""Add a tag to the module"""
|
||||
if tag not in self.tags:
|
||||
self.tags.append(tag)
|
||||
|
||||
def remove_tag(self, tag: str):
|
||||
"""Remove a tag from the module"""
|
||||
if tag in self.tags:
|
||||
self.tags.remove(tag)
|
||||
|
||||
def update_metadata(self, key: str, value: Any):
|
||||
"""Update metadata"""
|
||||
if self.module_metadata is None:
|
||||
self.module_metadata = {}
|
||||
self.module_metadata[key] = value
|
||||
|
||||
def add_dependency(self, module_name: str):
|
||||
"""Add a dependency"""
|
||||
if module_name not in self.dependencies:
|
||||
self.dependencies.append(module_name)
|
||||
|
||||
def remove_dependency(self, module_name: str):
|
||||
"""Remove a dependency"""
|
||||
if module_name in self.dependencies:
|
||||
self.dependencies.remove(module_name)
|
||||
|
||||
def add_conflict(self, module_name: str):
|
||||
"""Add a conflict"""
|
||||
if module_name not in self.conflicts:
|
||||
self.conflicts.append(module_name)
|
||||
|
||||
def remove_conflict(self, module_name: str):
|
||||
"""Remove a conflict"""
|
||||
if module_name in self.conflicts:
|
||||
self.conflicts.remove(module_name)
|
||||
|
||||
def add_interceptor_chain(self, chain_name: str):
|
||||
"""Add an interceptor chain"""
|
||||
if chain_name not in self.interceptor_chains:
|
||||
self.interceptor_chains.append(chain_name)
|
||||
|
||||
def remove_interceptor_chain(self, chain_name: str):
|
||||
"""Remove an interceptor chain"""
|
||||
if chain_name in self.interceptor_chains:
|
||||
self.interceptor_chains.remove(chain_name)
|
||||
|
||||
def add_api_endpoint(self, endpoint: str):
|
||||
"""Add an API endpoint"""
|
||||
if endpoint not in self.api_endpoints:
|
||||
self.api_endpoints.append(endpoint)
|
||||
|
||||
def remove_api_endpoint(self, endpoint: str):
|
||||
"""Remove an API endpoint"""
|
||||
if endpoint in self.api_endpoints:
|
||||
self.api_endpoints.remove(endpoint)
|
||||
|
||||
def add_required_permission(self, permission: str):
|
||||
"""Add a required permission"""
|
||||
if permission not in self.required_permissions:
|
||||
self.required_permissions.append(permission)
|
||||
|
||||
def remove_required_permission(self, permission: str):
|
||||
"""Remove a required permission"""
|
||||
if permission in self.required_permissions:
|
||||
self.required_permissions.remove(permission)
|
||||
|
||||
@classmethod
|
||||
def create_core_module(cls, name: str, display_name: str, description: str,
|
||||
version: str, entry_point: str) -> "Module":
|
||||
"""Create a core module"""
|
||||
return cls(
|
||||
name=name,
|
||||
display_name=display_name,
|
||||
description=description,
|
||||
module_type=ModuleType.CORE,
|
||||
version=version,
|
||||
author="Confidential Empire",
|
||||
license="Proprietary",
|
||||
status=ModuleStatus.ACTIVE,
|
||||
is_enabled=True,
|
||||
is_core=True,
|
||||
entry_point=entry_point,
|
||||
config_schema={},
|
||||
config_values={},
|
||||
default_config={},
|
||||
dependencies=[],
|
||||
conflicts=[],
|
||||
interceptor_chains=[],
|
||||
execution_order=10, # Core modules run first
|
||||
api_endpoints=[],
|
||||
required_permissions=[],
|
||||
security_level="high",
|
||||
tags=["core"],
|
||||
module_metadata={}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create_cache_module(cls) -> "Module":
|
||||
"""Create the cache module"""
|
||||
return cls(
|
||||
name="cache",
|
||||
display_name="Cache Module",
|
||||
description="Redis-based caching for improved performance",
|
||||
module_type=ModuleType.INTERCEPTOR,
|
||||
category="cache",
|
||||
version="1.0.0",
|
||||
author="Confidential Empire",
|
||||
license="Proprietary",
|
||||
status=ModuleStatus.INACTIVE,
|
||||
is_enabled=True,
|
||||
is_core=False,
|
||||
entry_point="app.modules.cache.main",
|
||||
config_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": {"type": "string", "enum": ["redis"]},
|
||||
"ttl": {"type": "integer", "minimum": 60},
|
||||
"max_size": {"type": "integer", "minimum": 1000}
|
||||
},
|
||||
"required": ["provider", "ttl"]
|
||||
},
|
||||
config_values={
|
||||
"provider": "redis",
|
||||
"ttl": 3600,
|
||||
"max_size": 10000
|
||||
},
|
||||
default_config={
|
||||
"provider": "redis",
|
||||
"ttl": 3600,
|
||||
"max_size": 10000
|
||||
},
|
||||
dependencies=[],
|
||||
conflicts=[],
|
||||
interceptor_chains=["pre_request", "post_response"],
|
||||
execution_order=20,
|
||||
api_endpoints=["/api/v1/cache/stats", "/api/v1/cache/clear"],
|
||||
required_permissions=["cache.read", "cache.write"],
|
||||
security_level="low",
|
||||
tags=["cache", "performance"],
|
||||
module_metadata={}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create_rag_module(cls) -> "Module":
|
||||
"""Create the RAG module"""
|
||||
return cls(
|
||||
name="rag",
|
||||
display_name="RAG Module",
|
||||
description="Retrieval Augmented Generation with vector database",
|
||||
module_type=ModuleType.INTERCEPTOR,
|
||||
category="rag",
|
||||
version="1.0.0",
|
||||
author="Confidential Empire",
|
||||
license="Proprietary",
|
||||
status=ModuleStatus.INACTIVE,
|
||||
is_enabled=True,
|
||||
is_core=False,
|
||||
entry_point="app.modules.rag.main",
|
||||
config_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vector_db": {"type": "string", "enum": ["qdrant"]},
|
||||
"embedding_model": {"type": "string"},
|
||||
"chunk_size": {"type": "integer", "minimum": 100},
|
||||
"max_results": {"type": "integer", "minimum": 1}
|
||||
},
|
||||
"required": ["vector_db", "embedding_model"]
|
||||
},
|
||||
config_values={
|
||||
"vector_db": "qdrant",
|
||||
"embedding_model": "sentence-transformers/all-MiniLM-L6-v2",
|
||||
"chunk_size": 512,
|
||||
"max_results": 10
|
||||
},
|
||||
default_config={
|
||||
"vector_db": "qdrant",
|
||||
"embedding_model": "sentence-transformers/all-MiniLM-L6-v2",
|
||||
"chunk_size": 512,
|
||||
"max_results": 10
|
||||
},
|
||||
dependencies=[],
|
||||
conflicts=[],
|
||||
interceptor_chains=["pre_request"],
|
||||
execution_order=30,
|
||||
api_endpoints=["/api/v1/rag/documents", "/api/v1/rag/search"],
|
||||
required_permissions=["rag.read", "rag.write"],
|
||||
security_level="medium",
|
||||
tags=["rag", "ai", "search"],
|
||||
module_metadata={}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create_analytics_module(cls) -> "Module":
|
||||
"""Create the analytics module"""
|
||||
return cls(
|
||||
name="analytics",
|
||||
display_name="Analytics Module",
|
||||
description="Request and response analytics and monitoring",
|
||||
module_type=ModuleType.ANALYTICS,
|
||||
category="analytics",
|
||||
version="1.0.0",
|
||||
author="Confidential Empire",
|
||||
license="Proprietary",
|
||||
status=ModuleStatus.INACTIVE,
|
||||
is_enabled=True,
|
||||
is_core=False,
|
||||
entry_point="app.modules.analytics.main",
|
||||
config_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"track_requests": {"type": "boolean"},
|
||||
"track_responses": {"type": "boolean"},
|
||||
"retention_days": {"type": "integer", "minimum": 1}
|
||||
},
|
||||
"required": ["track_requests", "track_responses"]
|
||||
},
|
||||
config_values={
|
||||
"track_requests": True,
|
||||
"track_responses": True,
|
||||
"retention_days": 30
|
||||
},
|
||||
default_config={
|
||||
"track_requests": True,
|
||||
"track_responses": True,
|
||||
"retention_days": 30
|
||||
},
|
||||
dependencies=[],
|
||||
conflicts=[],
|
||||
interceptor_chains=["pre_request", "post_response"],
|
||||
execution_order=90, # Analytics runs last
|
||||
api_endpoints=["/api/v1/analytics/stats", "/api/v1/analytics/reports"],
|
||||
required_permissions=["analytics.read"],
|
||||
security_level="low",
|
||||
tags=["analytics", "monitoring"],
|
||||
module_metadata={}
|
||||
)
|
||||
|
||||
def get_health_status(self) -> Dict[str, Any]:
|
||||
"""Get health status of the module"""
|
||||
return {
|
||||
"name": self.name,
|
||||
"status": self.status,
|
||||
"is_healthy": self.is_healthy(),
|
||||
"success_rate": self.get_success_rate(),
|
||||
"uptime_seconds": self.get_uptime_seconds() if self.is_running() else 0,
|
||||
"last_error": self.last_error,
|
||||
"error_count": self.error_count_runtime,
|
||||
"last_started": self.last_started.isoformat() if self.last_started else None
|
||||
}
|
||||
Reference in New Issue
Block a user