diff --git a/src/config_manager.py b/src/config_manager.py index 99a1f4f5..98394383 100644 --- a/src/config_manager.py +++ b/src/config_manager.py @@ -9,6 +9,12 @@ class ConfigManager: self.secrets_path = secrets_path or "config/config_secrets.json" self.config: Dict[str, Any] = {} + def get_config_path(self) -> str: + return self.config_path + + def get_secrets_path(self) -> str: + return self.secrets_path + def load_config(self) -> Dict[str, Any]: """Load configuration from JSON files.""" try: @@ -105,4 +111,60 @@ class ConfigManager: def get_clock_config(self) -> Dict[str, Any]: """Get clock configuration.""" - return self.config.get('clock', {}) \ No newline at end of file + return self.config.get('clock', {}) + + def get_raw_file_content(self, file_type: str) -> Dict[str, Any]: + """Load raw content of 'main' config or 'secrets' config file.""" + path_to_load = "" + if file_type == "main": + path_to_load = self.config_path + elif file_type == "secrets": + path_to_load = self.secrets_path + else: + raise ValueError("Invalid file_type specified. Must be 'main' or 'secrets'.") + + if not os.path.exists(path_to_load): + # If a secrets file doesn't exist, it's not an error, just return empty + if file_type == "secrets": + return {} + print(f"{file_type.capitalize()} configuration file not found at {os.path.abspath(path_to_load)}") + raise FileNotFoundError(f"{file_type.capitalize()} configuration file not found at {os.path.abspath(path_to_load)}") + + try: + with open(path_to_load, 'r') as f: + return json.load(f) + except json.JSONDecodeError: + print(f"Error parsing {file_type} configuration file: {path_to_load}") + raise + except Exception as e: + print(f"Error loading {file_type} configuration file {path_to_load}: {str(e)}") + raise + + def save_raw_file_content(self, file_type: str, data: Dict[str, Any]) -> None: + """Save data directly to 'main' config or 'secrets' config file.""" + path_to_save = "" + if file_type == "main": + path_to_save = self.config_path + elif file_type == "secrets": + path_to_save = self.secrets_path + else: + raise ValueError("Invalid file_type specified. Must be 'main' or 'secrets'.") + + try: + # Create directory if it doesn't exist, especially for config/ + os.makedirs(os.path.dirname(path_to_save), exist_ok=True) + with open(path_to_save, 'w') as f: + json.dump(data, f, indent=4) + print(f"{file_type.capitalize()} configuration successfully saved to {os.path.abspath(path_to_save)}") + + # If we just saved the main config or secrets, the merged self.config might be stale. + # Reload it to reflect the new state. + if file_type == "main" or file_type == "secrets": + self.load_config() + + except IOError as e: + print(f"Error writing {file_type} configuration to file {os.path.abspath(path_to_save)}: {e}") + raise + except Exception as e: + print(f"An unexpected error occurred while saving {file_type} configuration: {str(e)}") + raise \ No newline at end of file diff --git a/web_interface.py b/web_interface.py index 95fd77ff..14396c95 100644 --- a/web_interface.py +++ b/web_interface.py @@ -1,62 +1,259 @@ -from flask import Flask, render_template_string, request, redirect, url_for -import json # Added import for json +from flask import Flask, render_template_string, request, redirect, url_for, flash +import json +import os # Added os import from src.config_manager import ConfigManager app = Flask(__name__) +app.secret_key = os.urandom(24) # Needed for flash messages config_manager = ConfigManager() -CONFIG_TEMPLATE = """ +CONFIG_PAGE_TEMPLATE = """ LED Matrix Config + + + + +

LED Matrix Configuration

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + + {% endif %} + {% endwith %} + +
+ + +
+
- -
- + + +
+

Main Configuration ({{ main_config_path }})

+ + +
+ +
+

Secrets Configuration ({{ secrets_config_path }})

+ + +
+ +
+ + + + + + + + + + + """ @app.route('/') def display_config_route(): + active_tab = request.args.get('tab', 'main') # Default to 'main' tab + main_config_data = {} + secrets_config_data = {} + error_message = None + try: - current_config = config_manager.load_config() - # Pretty print JSON for the textarea - config_json = json.dumps(current_config, indent=4) - return render_template_string(CONFIG_TEMPLATE, config_json=config_json) + main_config_data = config_manager.get_raw_file_content('main') except Exception as e: - return f"Error loading configuration: {str(e)}", 500 + flash(f"Error loading main config: {str(e)}", "error") + + try: + secrets_config_data = config_manager.get_raw_file_content('secrets') + except Exception as e: + flash(f"Error loading secrets config: {str(e)}", "error") + + main_config_json = json.dumps(main_config_data, indent=4, sort_keys=True) + secrets_config_json = json.dumps(secrets_config_data, indent=4, sort_keys=True) + + return render_template_string(CONFIG_PAGE_TEMPLATE, + main_config_json=main_config_json, + secrets_config_json=secrets_config_json, + active_tab=active_tab, + main_config_path=config_manager.get_config_path(), + secrets_config_path=config_manager.get_secrets_path()) @app.route('/save', methods=['POST']) def save_config_route(): - try: - new_config_str = request.form['config_data'] - new_config = json.loads(new_config_str) # Parse the JSON string from textarea - config_manager.save_config(new_config) + config_type = request.form.get('config_type', 'main') + data_to_save_str = "" + + if config_type == 'main': + data_to_save_str = request.form['main_config_data'] + elif config_type == 'secrets': + data_to_save_str = request.form['secrets_config_data'] + else: + flash("Invalid configuration type specified for saving.", "error") return redirect(url_for('display_config_route')) + + try: + new_data = json.loads(data_to_save_str) + config_manager.save_raw_file_content(config_type, new_data) + flash(f"{config_type.capitalize()} configuration saved successfully!", "success") except json.JSONDecodeError: - return "Error: Invalid JSON format submitted.", 400 + flash(f"Error: Invalid JSON format submitted for {config_type} config.", "error") except Exception as e: - return f"Error saving configuration: {str(e)}", 500 + flash(f"Error saving {config_type} configuration: {str(e)}", "error") + + return redirect(url_for('display_config_route', tab=config_type)) # Redirect back to the same tab if __name__ == '__main__': - # Make sure to run with debug=True only for development - # In a production environment, use a proper WSGI server like Gunicorn app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file