Implement config storage
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
*.venv
|
||||
*__pycache__
|
||||
*__pycache__
|
||||
*.bat
|
||||
*.json
|
||||
@@ -4,6 +4,9 @@ FROM python:3.13-slim
|
||||
# Set the working directory in the container
|
||||
WORKDIR /app
|
||||
|
||||
# Create the data dir (this should be a volume on the local machine to store data)
|
||||
RUN mkdir -p data
|
||||
|
||||
# Copy the requirements file into the container
|
||||
COPY requirements.txt .
|
||||
|
||||
|
||||
1
Makefile
1
Makefile
@@ -14,5 +14,6 @@ run: build
|
||||
-e SERVER_WS_URI=${SERVER_WS_URI} \
|
||||
-e SERVER_API_URL=${SERVER_API_URL} \
|
||||
-e CLIENT_API_URL=${CLIENT_API_URL} \
|
||||
-v ./data:/data \
|
||||
--network=host \
|
||||
$(CLIENT_IMAGE)
|
||||
|
||||
@@ -7,14 +7,19 @@ from drb_cdb_api import DRBCDBAPI
|
||||
from drb_cdb_types import ConfigGenerator
|
||||
from server_api import RadioAPIClient
|
||||
from enum import Enum
|
||||
from config import Config
|
||||
|
||||
app_conf = Config()
|
||||
|
||||
# --- Client Configuration ---
|
||||
SERVER_WS_URI = os.getenv("SERVER_WS_URI", "ws://localhost:8765")
|
||||
SERVER_API_URL = os.getenv("SERVER_API_URL", "http://localhost:5000")
|
||||
CLIENT_API_URL = os.getenv("CLIENT_API_URL", "http://localhost:8001")
|
||||
# Generate or define a unique ID for THIS client instance
|
||||
# In a real app, this might come from config or a login process
|
||||
CLIENT_ID = f"client-{uuid.uuid4().hex[:8]}" # TODO - Implement persistent ID
|
||||
|
||||
# Get/set the ID of this node
|
||||
if not app_conf.get("client_id"):
|
||||
app_conf.set("client_id", f"client-{uuid.uuid4().hex[:8]}")
|
||||
CLIENT_ID = app_conf.client_id
|
||||
# ----------------------------
|
||||
|
||||
# Dictionary mapping command names (strings) to local client functions
|
||||
@@ -38,7 +43,6 @@ def command(func):
|
||||
return func
|
||||
|
||||
# --- Define Client-Side Command Handlers (The "API" functions) ---
|
||||
|
||||
# Join server
|
||||
@command
|
||||
async def join_server(system_id, guild_id, channel_id):
|
||||
@@ -114,13 +118,6 @@ async def leave_server(guild_id):
|
||||
|
||||
print("Leave server completed")
|
||||
|
||||
@command
|
||||
async def set_status(status_text):
|
||||
"""Example command: Sets or displays a status."""
|
||||
print(f"\n--- Server Command: set_status ---")
|
||||
print(f"Status updated to: {status_text}")
|
||||
print("----------------------------------")
|
||||
|
||||
@command
|
||||
async def run_task(task_id, duration_seconds):
|
||||
"""Example command: Simulates running a task."""
|
||||
@@ -130,11 +127,7 @@ async def run_task(task_id, duration_seconds):
|
||||
print(f"Task {task_id} finished.")
|
||||
print("------------------------------")
|
||||
|
||||
# Add more command handlers as needed...
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
async def receive_commands(websocket):
|
||||
"""Listens for and processes command messages from the server."""
|
||||
async for message in websocket:
|
||||
|
||||
139
app/config.py
Normal file
139
app/config.py
Normal file
@@ -0,0 +1,139 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
class Config:
|
||||
"""
|
||||
Manages application configuration stored in a JSON file.
|
||||
|
||||
Provides attribute-style access to configuration values.
|
||||
"""
|
||||
def __init__(self, file_path='/data/config.json'):
|
||||
"""
|
||||
Initializes the Config manager.
|
||||
|
||||
Loads configuration from the JSON file. Creates the file with an
|
||||
empty JSON object if it doesn't exist.
|
||||
|
||||
Args:
|
||||
file_path (str): The path to the JSON configuration file.
|
||||
"""
|
||||
self.file_path = file_path
|
||||
self._config_data = {} # Internal dictionary to hold config data
|
||||
self._load_config()
|
||||
|
||||
def _load_config(self):
|
||||
"""Loads configuration key-value pairs from the JSON file."""
|
||||
# Check if the file exists. If not, create it with an empty JSON object.
|
||||
if not os.path.exists(self.file_path):
|
||||
self._save_config() # Create the file with empty data
|
||||
|
||||
try:
|
||||
with open(self.file_path, 'r') as f:
|
||||
# Load data from the file. Handle empty file case.
|
||||
content = f.read()
|
||||
if content:
|
||||
self._config_data = json.loads(content)
|
||||
else:
|
||||
self._config_data = {} # Initialize as empty if file is empty
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"Error loading config file {self.file_path}: {e}")
|
||||
# If there's an error loading, initialize with empty data
|
||||
self._config_data = {}
|
||||
# Optionally, you might want to back up the problematic file
|
||||
# or log the error more severely in a real application.
|
||||
|
||||
def _save_config(self):
|
||||
"""Saves the current configuration data to the JSON file."""
|
||||
try:
|
||||
with open(self.file_path, 'w') as f:
|
||||
json.dump(self._config_data, f, indent=4) # Use indent for readability
|
||||
except IOError as e:
|
||||
print(f"Error saving config file {self.file_path}: {e}")
|
||||
except TypeError as e:
|
||||
print(f"Error serializing config data to JSON: {e}. Ensure all values are JSON serializable.")
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""
|
||||
Retrieves a configuration value by key.
|
||||
|
||||
Args:
|
||||
key (str): The configuration key.
|
||||
default: The value to return if the key is not found.
|
||||
|
||||
Returns:
|
||||
The configuration value or the default value if not found.
|
||||
"""
|
||||
return self._config_data.get(key, default)
|
||||
|
||||
def set(self, key, value):
|
||||
"""
|
||||
Sets or updates a configuration value in memory and saves to the file.
|
||||
|
||||
Args:
|
||||
key (str): The configuration key.
|
||||
value: The configuration value. Must be JSON serializable.
|
||||
"""
|
||||
self._config_data[key] = value
|
||||
self._save_config()
|
||||
|
||||
def delete(self, key):
|
||||
"""
|
||||
Deletes a configuration key-value pair from memory and saves to the file.
|
||||
|
||||
Args:
|
||||
key (str): The configuration key to delete.
|
||||
"""
|
||||
if key in self._config_data:
|
||||
del self._config_data[key]
|
||||
self._save_config()
|
||||
else:
|
||||
print(f"Warning: Key '{key}' not found in config.")
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
Allows accessing configuration values using attribute notation (e.g., config.my_key).
|
||||
|
||||
Args:
|
||||
name (str): The attribute name (configuration key).
|
||||
|
||||
Returns:
|
||||
The configuration value associated with the key.
|
||||
|
||||
Raises:
|
||||
AttributeError: If the key is not found in the configuration.
|
||||
"""
|
||||
if name in self._config_data:
|
||||
return self._config_data[name]
|
||||
# Fallback for standard attributes if not found in config data
|
||||
try:
|
||||
return self.__getattribute__(name)
|
||||
except AttributeError:
|
||||
raise AttributeError(f"'Config' object has no attribute '{name}' and key '{name}' not found in config.")
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""
|
||||
Allows setting configuration values using attribute notation (e.g., config.my_key = value).
|
||||
|
||||
Args:
|
||||
name (str): The attribute name (configuration key).
|
||||
value: The value to set. Must be JSON serializable.
|
||||
"""
|
||||
# Handle setting internal attributes like file_path or _config_data
|
||||
if name in ('file_path', '_config_data'):
|
||||
super().__setattr__(name, value)
|
||||
else:
|
||||
# Treat other attribute assignments as setting config values
|
||||
self.set(name, value)
|
||||
|
||||
def __delattr__(self, name):
|
||||
"""
|
||||
Allows deleting configuration values using attribute notation (e.g., del config.my_key).
|
||||
|
||||
Args:
|
||||
name (str): The attribute name (configuration key) to delete.
|
||||
"""
|
||||
if name in self._config_data:
|
||||
self.delete(name)
|
||||
else:
|
||||
# Fallback for standard attributes
|
||||
super().__delattr__(name)
|
||||
Reference in New Issue
Block a user