feat: add SQLCipher database encryption support and consolidate agent documentation
Some checks failed
Blockchain Synchronization Verification / sync-verification (push) Failing after 3s
CLI Tests / test-cli (push) Failing after 3s
Cross-Chain Functionality Tests / test-cross-chain-sync (push) Successful in 2s
Cross-Chain Functionality Tests / test-cross-chain-transactions (push) Successful in 3s
Cross-Chain Functionality Tests / test-cross-chain-bridge (push) Has been skipped
Cross-Chain Functionality Tests / test-multi-chain-consensus (push) Successful in 2s
Cross-Chain Functionality Tests / aggregate-results (push) Has been skipped
Deploy to Testnet / deploy-testnet (push) Successful in 1m12s
Documentation Validation / validate-docs (push) Failing after 8s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Integration Tests / test-service-integration (push) Successful in 2m6s
Multi-Chain Island Architecture Tests / test-multi-chain-island (push) Successful in 2s
Multi-Node Blockchain Health Monitoring / health-check (push) Failing after 4s
P2P Network Verification / p2p-verification (push) Successful in 4s
Package Tests / Python package - aitbc-agent-sdk (push) Successful in 32s
Package Tests / Python package - aitbc-core (push) Successful in 14s
Package Tests / Python package - aitbc-crypto (push) Successful in 12s
Package Tests / Python package - aitbc-sdk (push) Successful in 9s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 8s
Package Tests / JavaScript package - aitbc-token (push) Successful in 17s
Python Tests / test-python (push) Successful in 15s
Security Scanning / security-scan (push) Successful in 27s
Node Failover Simulation / failover-test (push) Successful in 7s
Multi-Node Stress Testing / stress-test (push) Successful in 6s
Cross-Node Transaction Testing / transaction-test (push) Successful in 4s
Some checks failed
Blockchain Synchronization Verification / sync-verification (push) Failing after 3s
CLI Tests / test-cli (push) Failing after 3s
Cross-Chain Functionality Tests / test-cross-chain-sync (push) Successful in 2s
Cross-Chain Functionality Tests / test-cross-chain-transactions (push) Successful in 3s
Cross-Chain Functionality Tests / test-cross-chain-bridge (push) Has been skipped
Cross-Chain Functionality Tests / test-multi-chain-consensus (push) Successful in 2s
Cross-Chain Functionality Tests / aggregate-results (push) Has been skipped
Deploy to Testnet / deploy-testnet (push) Successful in 1m12s
Documentation Validation / validate-docs (push) Failing after 8s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Integration Tests / test-service-integration (push) Successful in 2m6s
Multi-Chain Island Architecture Tests / test-multi-chain-island (push) Successful in 2s
Multi-Node Blockchain Health Monitoring / health-check (push) Failing after 4s
P2P Network Verification / p2p-verification (push) Successful in 4s
Package Tests / Python package - aitbc-agent-sdk (push) Successful in 32s
Package Tests / Python package - aitbc-core (push) Successful in 14s
Package Tests / Python package - aitbc-crypto (push) Successful in 12s
Package Tests / Python package - aitbc-sdk (push) Successful in 9s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 8s
Package Tests / JavaScript package - aitbc-token (push) Successful in 17s
Python Tests / test-python (push) Successful in 15s
Security Scanning / security-scan (push) Successful in 27s
Node Failover Simulation / failover-test (push) Successful in 7s
Multi-Node Stress Testing / stress-test (push) Successful in 6s
Cross-Node Transaction Testing / transaction-test (push) Successful in 4s
- Add SQLCipher encryption for ait-mainnet database with configurable flag - Add db_encryption_enabled and db_encryption_key_path config settings - Implement encryption key loading and PRAGMA key setup via connection events - Add shutdown_db function for proper database cleanup - Export middleware classes in aitbc/__init__.py - Fix import path in sync.py for settings - Remove duplicate agent documentation from docs
This commit is contained in:
@@ -46,6 +46,12 @@ from .exceptions import (
|
||||
RetryError,
|
||||
ValidationError,
|
||||
)
|
||||
from .middleware import (
|
||||
RequestIDMiddleware,
|
||||
PerformanceLoggingMiddleware,
|
||||
RequestValidationMiddleware,
|
||||
ErrorHandlerMiddleware,
|
||||
)
|
||||
from .paths import (
|
||||
ensure_dir,
|
||||
ensure_file_dir,
|
||||
|
||||
38
aitbc/logging.py
Normal file
38
aitbc/logging.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
Logging module (alias for aitbc_logging)
|
||||
This module provides a compatibility layer for imports from aitbc.logging
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
def setup_logger(
|
||||
name: str,
|
||||
level: str = "INFO",
|
||||
format_string: Optional[str] = None
|
||||
) -> logging.Logger:
|
||||
"""Setup a logger with consistent formatting"""
|
||||
if format_string is None:
|
||||
format_string = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(getattr(logging, level.upper()))
|
||||
|
||||
if not logger.handlers:
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
formatter = logging.Formatter(format_string)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
|
||||
return logger
|
||||
|
||||
def get_logger(name: str) -> logging.Logger:
|
||||
"""Get a logger instance"""
|
||||
return logging.getLogger(name)
|
||||
|
||||
def configure_logging(level: str = "INFO", format_string: str = None):
|
||||
"""Configure logging with default settings"""
|
||||
return setup_logger("aitbc", level=level, format_string=format_string)
|
||||
|
||||
__all__ = ["get_logger", "setup_logger", "configure_logging"]
|
||||
15
aitbc/middleware/__init__.py
Normal file
15
aitbc/middleware/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Shared middleware for AITBC services
|
||||
"""
|
||||
|
||||
from .request_id import RequestIDMiddleware
|
||||
from .performance import PerformanceLoggingMiddleware
|
||||
from .validation import RequestValidationMiddleware
|
||||
from .error_handler import ErrorHandlerMiddleware
|
||||
|
||||
__all__ = [
|
||||
"RequestIDMiddleware",
|
||||
"PerformanceLoggingMiddleware",
|
||||
"RequestValidationMiddleware",
|
||||
"ErrorHandlerMiddleware",
|
||||
]
|
||||
61
aitbc/middleware/error_handler.py
Normal file
61
aitbc/middleware/error_handler.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Standardized error response middleware for FastAPI
|
||||
"""
|
||||
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.types import ASGIApp
|
||||
|
||||
from aitbc.aitbc_logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class ErrorHandlerMiddleware(BaseHTTPMiddleware):
|
||||
"""Middleware to standardize error responses"""
|
||||
|
||||
async def dispatch(self, request: Request, call_next: Callable) -> JSONResponse:
|
||||
try:
|
||||
response = await call_next(request)
|
||||
return response
|
||||
except HTTPException as e:
|
||||
logger.warning(
|
||||
"HTTP exception",
|
||||
status_code=e.status_code,
|
||||
detail=e.detail,
|
||||
path=request.url.path,
|
||||
method=request.method,
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=e.status_code,
|
||||
content={
|
||||
"error": {
|
||||
"type": "http_error",
|
||||
"message": e.detail,
|
||||
"status_code": e.status_code,
|
||||
"path": request.url.path,
|
||||
}
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Unhandled exception",
|
||||
error=str(e),
|
||||
path=request.url.path,
|
||||
method=request.method,
|
||||
exc_info=True,
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={
|
||||
"error": {
|
||||
"type": "internal_error",
|
||||
"message": "An internal server error occurred",
|
||||
"status_code": 500,
|
||||
"path": request.url.path,
|
||||
}
|
||||
},
|
||||
)
|
||||
41
aitbc/middleware/performance.py
Normal file
41
aitbc/middleware/performance.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
Performance logging middleware for tracking request timing
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import Request, Response
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.types import ASGIApp
|
||||
|
||||
from aitbc.aitbc_logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class PerformanceLoggingMiddleware(BaseHTTPMiddleware):
|
||||
"""Middleware to log request performance metrics"""
|
||||
|
||||
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
||||
start_time = time.perf_counter()
|
||||
|
||||
# Process request
|
||||
response = await call_next(request)
|
||||
|
||||
# Calculate duration
|
||||
duration = time.perf_counter() - start_time
|
||||
|
||||
# Log performance metrics
|
||||
logger.info(
|
||||
"Request performance",
|
||||
method=request.method,
|
||||
path=request.url.path,
|
||||
status_code=response.status_code,
|
||||
duration_ms=round(duration * 1000, 2),
|
||||
)
|
||||
|
||||
# Add performance header
|
||||
response.headers["X-Process-Time"] = f"{duration:.3f}"
|
||||
|
||||
return response
|
||||
54
aitbc/middleware/request_id.py
Normal file
54
aitbc/middleware/request_id.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Request ID correlation middleware for structured logging
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import Request, Response
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.types import ASGIApp
|
||||
|
||||
from aitbc.aitbc_logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class RequestIDMiddleware(BaseHTTPMiddleware):
|
||||
"""Middleware to add request ID to all requests for correlation"""
|
||||
|
||||
def __init__(self, app: ASGIApp) -> None:
|
||||
super().__init__(app)
|
||||
self.header_name = "X-Request-ID"
|
||||
|
||||
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
||||
# Generate or retrieve request ID
|
||||
request_id = request.headers.get(self.header_name) or str(uuid.uuid4())
|
||||
|
||||
# Add request ID to request state for use in endpoints
|
||||
request.state.request_id = request_id
|
||||
|
||||
# Bind request ID to logger context
|
||||
logger = get_logger(__name__).bind(request_id=request_id)
|
||||
|
||||
# Log request start
|
||||
logger.info(
|
||||
"Incoming request",
|
||||
method=request.method,
|
||||
path=request.url.path,
|
||||
client=request.client.host if request.client else "unknown",
|
||||
)
|
||||
|
||||
# Process request
|
||||
response = await call_next(request)
|
||||
|
||||
# Add request ID to response headers
|
||||
response.headers[self.header_name] = request_id
|
||||
|
||||
# Log request completion
|
||||
logger.info(
|
||||
"Request completed",
|
||||
status_code=response.status_code,
|
||||
)
|
||||
|
||||
return response
|
||||
67
aitbc/middleware/validation.py
Normal file
67
aitbc/middleware/validation.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Request validation middleware for FastAPI
|
||||
"""
|
||||
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import Request, HTTPException, Response
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.types import ASGIApp
|
||||
|
||||
from aitbc.aitbc_logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class RequestValidationMiddleware(BaseHTTPMiddleware):
|
||||
"""Middleware to validate incoming requests"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
app: ASGIApp,
|
||||
max_request_size: int = 10 * 1024 * 1024, # 10MB default
|
||||
max_response_size: int = 10 * 1024 * 1024, # 10MB default
|
||||
) -> None:
|
||||
super().__init__(app)
|
||||
self.max_request_size = max_request_size
|
||||
self.max_response_size = max_response_size
|
||||
|
||||
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
||||
# Validate request size
|
||||
content_length = request.headers.get("content-length")
|
||||
if content_length:
|
||||
try:
|
||||
size = int(content_length)
|
||||
if size > self.max_request_size:
|
||||
logger.warning(
|
||||
"Request too large",
|
||||
content_length=size,
|
||||
max_size=self.max_request_size,
|
||||
client=request.client.host if request.client else "unknown",
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=f"Request too large. Maximum size is {self.max_request_size} bytes",
|
||||
)
|
||||
except ValueError:
|
||||
logger.warning("Invalid content-length header", content_length=content_length)
|
||||
|
||||
# Process request
|
||||
response = await call_next(request)
|
||||
|
||||
# Validate response size (skip for streaming responses)
|
||||
if hasattr(response, "body"):
|
||||
response_size = len(response.body)
|
||||
if response_size > self.max_response_size:
|
||||
logger.warning(
|
||||
"Response too large",
|
||||
response_size=response_size,
|
||||
max_size=self.max_response_size,
|
||||
path=request.url.path,
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Response too large",
|
||||
)
|
||||
|
||||
return response
|
||||
Reference in New Issue
Block a user