cmboulanger's picture
disaster-recovery deploy of pdf-tei-editor
6a49f21 verified
Raw
History Blame Contribute Delete
5.55 kB
"""
Configuration API endpoints for FastAPI.
Provides configuration management, instructions, and state information.
"""
from typing import Any, Optional, List
from pydantic import BaseModel
from fastapi import APIRouter, Request, HTTPException, Depends
from pathlib import Path
import json
import os
from ..config import get_settings
from ..lib.utils.auth import AuthManager
from ..lib.core.sessions import SessionManager
from ..lib.utils.server_utils import get_session_id_from_request
from ..lib.utils.config_utils import get_config
from ..lib.utils.logging_utils import get_logger
logger = get_logger(__name__)
router = APIRouter(prefix="/config", tags=["configuration"])
# Get config instance
config = get_config()
# Pydantic Models
class ConfigSetRequest(BaseModel):
"""Request model for setting config values"""
key: str
value: Any
class InstructionItem(BaseModel):
"""Model for extraction instructions"""
label: str
extractor: List[str]
text: List[str]
class StateResponse(BaseModel):
"""Response model for application state"""
hasInternet: Optional[bool] = None
class ConfigSetResponse(BaseModel):
"""Response for config set operation"""
result: str
# Authentication dependency
async def require_auth(request: Request) -> dict:
"""
Dependency that requires valid authentication.
Returns authenticated user data.
"""
settings = get_settings()
session_id = get_session_id_from_request(request)
if not session_id:
raise HTTPException(status_code=401, detail="Authentication required")
auth_manager = AuthManager(settings.db_dir, logger=logger)
session_manager = SessionManager(settings.db_dir, logger=logger)
# Validate session
if not session_manager.is_session_valid(session_id, settings.session_timeout):
raise HTTPException(status_code=401, detail="Session expired")
# Get user
user = auth_manager.get_user_by_session_id(session_id, session_manager)
if not user:
raise HTTPException(status_code=401, detail="Invalid session")
return user
# Helper functions
def has_internet() -> bool:
"""Check if internet connection is available"""
# Simple check - can be enhanced later
import socket
try:
socket.create_connection(("8.8.8.8", 53), timeout=3)
return True
except OSError:
return False
# API Endpoints
@router.get("/list", response_model=dict)
async def list_config() -> dict:
"""
List all configuration values.
Returns complete configuration object.
"""
config_data = config.load()
return config_data
@router.get("/get/{key}")
async def get_config(key: str) -> Any:
"""
Get a specific configuration value by key.
Returns the value associated with the key.
"""
if not key:
raise HTTPException(status_code=400, detail="Invalid or empty key")
config_data = config.load()
if key not in config_data:
raise HTTPException(status_code=404, detail=f"Key '{key}' not found")
return config_data[key]
@router.post("/set", response_model=ConfigSetResponse)
async def set_config(
request_data: ConfigSetRequest,
user: dict = Depends(require_auth)
):
"""
Set a configuration value.
Requires authentication.
"""
if not request_data.key:
raise HTTPException(status_code=400, detail="Missing 'key' in request")
success, message = config.set(request_data.key, request_data.value)
if not success:
raise HTTPException(status_code=400, detail=message)
logger.info(f"User {user['username']} set config {request_data.key}")
return ConfigSetResponse(result="OK")
@router.get("/instructions", response_model=List[InstructionItem])
async def get_instructions(user: dict = Depends(require_auth)) -> List[InstructionItem]:
"""
Get extraction instructions.
Requires authentication.
Returns list of instruction items.
"""
settings = get_settings()
instruction_file = settings.db_dir / "prompt.json"
if instruction_file.exists():
with open(instruction_file, 'r', encoding='utf-8') as f:
instructions = json.load(f)
else:
instructions = [{
"label": "Default instructions",
"extractor": ["llamore-gemini"],
"text": []
}]
return instructions
class SaveInstructionsResponse(BaseModel):
"""Response for saving instructions"""
result: str
@router.post("/instructions", response_model=SaveInstructionsResponse)
async def save_instructions(
instructions: List[InstructionItem],
user: dict = Depends(require_auth)
) -> SaveInstructionsResponse:
"""
Save extraction instructions.
Requires authentication.
"""
settings = get_settings()
instruction_file = settings.db_dir / "prompt.json"
# Ensure directory exists
instruction_file.parent.mkdir(parents=True, exist_ok=True)
# Convert Pydantic models to dicts for JSON serialization
instructions_data = [item.model_dump() for item in instructions]
with open(instruction_file, 'w', encoding='utf-8') as f:
json.dump(instructions_data, f, indent=4)
logger.info(f"User {user['username']} saved instructions")
return SaveInstructionsResponse(result="ok")
@router.get("/state", response_model=StateResponse)
async def get_state():
"""
Get application state information.
Returns state including internet connectivity.
"""
return StateResponse(hasInternet=has_internet())