# Configuration Management The PDF-TEI-Editor uses JSON configuration files to manage application settings. Configuration can be managed via the command-line interface. For programmatic use, see [Configuration Processing Architecture](#configuration-processing-architecture). ## Configuration Files - **`data/db/config.json`** - Runtime configuration (user-specific, gitignored) - **`config/config.json`** - Default configuration (version-controlled) ## Configuration Processing Architecture ### Backend Processing Configuration values are processed through a high-level API that abstracts storage details. #### `fastapi_app/lib/utils/config_utils.py` Provides a `Config` class and module-level configuration instance: **High-Level API (recommended)**: ```python from fastapi_app.lib.utils.config_utils import get_config # Get config instance (lazy initialization) config = get_config() # Get configuration values value = config.get('session.timeout', default=3600) # Set configuration values (with validation) success, message = config.set('session.timeout', 7200) # Delete configuration keys success, message = config.delete('old.key') # Load complete configuration config_data = config.load() ``` The `get_config()` function returns a module-level config instance preconfigured with settings.db_dir, so you don't need to pass directory paths. **Alternative - Custom Config Instance**: For testing or custom db_dir: ```python from fastapi_app.lib.utils.config_utils import Config from pathlib import Path custom_config = Config(Path('/custom/db/dir')) value = custom_config.get('key') ``` **Configuration Features**: - **Type validation**: If `.type` exists, validates value matches required JSON type - **Values constraints**: If `.values` exists, validates value is in allowed values - **Auto-typing**: New keys automatically get a `.type` constraint based on value's JSON type - **Thread-safe**: Uses cross-platform file locking (fcntl on Unix, msvcrt on Windows) - **Dot notation**: Supports keys like `session.timeout` and `session.cookie.name` #### Using Configuration in Backend Routes Configuration is accessed via `get_config()`: ```python from fastapi import APIRouter from fastapi_app.lib.utils.config_utils import get_config router = APIRouter() @router.get("/my-endpoint") async def my_endpoint(): # Get config instance config = get_config() # Get configuration values timeout = config.get('session.timeout', default=3600) mode = config.get('application.mode', default='production') return { "timeout": timeout, "mode": mode } ``` **For Settings Properties**: Access environment variables and static settings via the Settings object: ```python from fastapi_app.config import get_settings settings = get_settings() host = settings.HOST port = settings.PORT data_root = settings.data_root # Path property db_dir = settings.db_dir # Path property ``` **Settings Properties**: - Environment variables: `HOST`, `PORT`, `DATA_ROOT`, `WEBDAV_ENABLED`, `LOG_LEVEL`, etc. - Path properties: `data_root`, `db_dir`, `upload_dir`, `config_dir` - Dynamic properties: `session_timeout` (checks environment, then config.json, then default) - The Settings class is cached via `@lru_cache`, so `get_settings()` returns the same instance **Setting Config Values**: ```python from fastapi_app.lib.utils.config_utils import get_config from fastapi import HTTPException @router.post("/custom-config") async def set_custom(key: str, value: str): # Get config instance config = get_config() # Write custom config value (validates constraints) success, message = config.set(key, value) if not success: raise HTTPException(status_code=400, detail=message) return {"result": message} ``` #### `fastapi_app/api/config.py` REST API endpoints for configuration access: - **`GET /api/v1/config/list`**: Returns complete configuration object - **`GET /api/v1/config/get/{key}`**: Returns specific configuration value by key - **`POST /api/v1/config/set`**: Sets a configuration value (requires authentication) - Request body: `{"key": "config.key", "value": }` - Validates user authentication via session - Calls `set_config_value()` which performs validation - Logs the configuration change with username ### Frontend Integration #### `app/src/plugins/config.js` The config plugin provides client-side access to configuration: - **API Methods**: - `get(key, defaultValue, updateFirst=false)`: Retrieves a configuration value. If `updateFirst` is true, fetches fresh data from server before returning. - `set(key, value)`: Sets a configuration value on the server. Validates the key exists before attempting to set. - `load()`: Fetches configuration data from server and updates local cache. - `toMap()`: Returns configuration as a Map object. - **Usage Pattern**: ```javascript import { api as config } from './plugins/config.js'; // Get a value (uses cached data) const mode = await config.get('application.mode'); // Get with fresh data from server const interval = await config.get('heartbeat.interval', 30, true); // Set a value (updates server) await config.set('application.mode', 'production'); // Reload configuration cache await config.load(); ``` - **Internal State**: - Maintains a local `configMap` object cached from server - Automatically loads config on first `get()` call if not already loaded - Uses `client.getConfigData()` to fetch from `/api/v1/config/list` - Uses `client.setConfigValue(key, value)` to update via `/api/v1/config/set` ### Configuration Flow 1. **Initial Load**: - Frontend calls `config.get()` for the first time - Plugin fetches complete config via `GET /api/v1/config/list` - Backend calls `config.load()` to read `data/db/config.json` - Data is cached in `configMap` object 2. **Reading Values**: - Frontend calls `config.get(key)` using cached data - Or `config.get(key, default, true)` to force server refresh - Backend uses `config.get(key, default)` for server-side reads 3. **Writing Values**: - Frontend calls `config.set(key, value)` - Validates key exists in cached config (throws error if not) - Sends `POST /api/v1/config/set` with JSON body - Backend validates authentication and session - Backend calls `config.set(key, value)` which: - Acquires file lock on `config.json` - Loads current config - Validates value constraints and type - Writes updated config - Releases file lock 4. **CLI Usage**: - CLI calls Python functions from `bin/manage.py` - Uses low-level `config_utils.py` functions directly - No authentication required for CLI access