""" Settings API blueprint. Handles endpoints for managing application settings including SMTP configuration, authentication, and system preferences. """ from flask import Blueprint, current_app, jsonify, request from web.auth.decorators import api_auth_required from web.utils.settings import PasswordManager, SettingsManager bp = Blueprint('settings', __name__) def get_settings_manager(): """Get SettingsManager instance with current DB session.""" return SettingsManager(current_app.db_session) @bp.route('', methods=['GET']) @api_auth_required def get_settings(): """ Get all settings (sanitized - encrypted values masked). Returns: JSON response with all settings """ try: settings_manager = get_settings_manager() settings = settings_manager.get_all(decrypt=False, sanitize=True) return jsonify({ 'status': 'success', 'settings': settings }) except Exception as e: current_app.logger.error(f"Failed to retrieve settings: {e}") return jsonify({ 'status': 'error', 'message': 'Failed to retrieve settings' }), 500 @bp.route('', methods=['PUT']) @api_auth_required def update_settings(): """ Update multiple settings at once. Request body: settings: Dictionary of setting key-value pairs Returns: JSON response with update status """ data = request.get_json() or {} settings_dict = data.get('settings', {}) if not settings_dict: return jsonify({ 'status': 'error', 'message': 'No settings provided' }), 400 try: settings_manager = get_settings_manager() # Update each setting for key, value in settings_dict.items(): settings_manager.set(key, value) return jsonify({ 'status': 'success', 'message': f'Updated {len(settings_dict)} settings' }) except ValueError as e: # Handle read-only setting attempts return jsonify({ 'status': 'error', 'message': str(e) }), 403 except Exception as e: current_app.logger.error(f"Failed to update settings: {e}") return jsonify({ 'status': 'error', 'message': 'Failed to update settings' }), 500 @bp.route('/', methods=['GET']) @api_auth_required def get_setting(key): """ Get a specific setting by key. Args: key: Setting key Returns: JSON response with setting value """ try: settings_manager = get_settings_manager() value = settings_manager.get(key) if value is None: return jsonify({ 'status': 'error', 'message': f'Setting "{key}" not found' }), 404 # Sanitize if encrypted key if settings_manager._should_encrypt(key): value = '***ENCRYPTED***' return jsonify({ 'status': 'success', 'key': key, 'value': value, 'read_only': settings_manager._is_read_only(key) }) except Exception as e: current_app.logger.error(f"Failed to retrieve setting {key}: {e}") return jsonify({ 'status': 'error', 'message': 'Failed to retrieve setting' }), 500 @bp.route('/', methods=['PUT']) @api_auth_required def update_setting(key): """ Update a specific setting. Args: key: Setting key Request body: value: New value for the setting Returns: JSON response with update status """ data = request.get_json() or {} value = data.get('value') if value is None: return jsonify({ 'status': 'error', 'message': 'No value provided' }), 400 try: settings_manager = get_settings_manager() settings_manager.set(key, value) return jsonify({ 'status': 'success', 'message': f'Setting "{key}" updated' }) except ValueError as e: # Handle read-only setting attempts return jsonify({ 'status': 'error', 'message': str(e) }), 403 except Exception as e: current_app.logger.error(f"Failed to update setting {key}: {e}") return jsonify({ 'status': 'error', 'message': 'Failed to update setting' }), 500 @bp.route('/', methods=['DELETE']) @api_auth_required def delete_setting(key): """ Delete a setting. Args: key: Setting key to delete Returns: JSON response with deletion status """ try: settings_manager = get_settings_manager() # Prevent deletion of read-only settings if settings_manager._is_read_only(key): return jsonify({ 'status': 'error', 'message': f'Setting "{key}" is read-only and cannot be deleted' }), 403 deleted = settings_manager.delete(key) if not deleted: return jsonify({ 'status': 'error', 'message': f'Setting "{key}" not found' }), 404 return jsonify({ 'status': 'success', 'message': f'Setting "{key}" deleted' }) except Exception as e: current_app.logger.error(f"Failed to delete setting {key}: {e}") return jsonify({ 'status': 'error', 'message': 'Failed to delete setting' }), 500 @bp.route('/password', methods=['POST']) @api_auth_required def set_password(): """ Set the application password. Request body: password: New password Returns: JSON response with status """ data = request.get_json() or {} password = data.get('password') if not password: return jsonify({ 'status': 'error', 'message': 'No password provided' }), 400 if len(password) < 8: return jsonify({ 'status': 'error', 'message': 'Password must be at least 8 characters' }), 400 try: settings_manager = get_settings_manager() PasswordManager.set_app_password(settings_manager, password) return jsonify({ 'status': 'success', 'message': 'Password updated successfully' }) except Exception as e: current_app.logger.error(f"Failed to set password: {e}") return jsonify({ 'status': 'error', 'message': 'Failed to set password' }), 500 @bp.route('/test-email', methods=['POST']) @api_auth_required def test_email(): """ Test email configuration by sending a test email. Returns: JSON response with test result """ # TODO: Implement in Phase 4 (email support) return jsonify({ 'status': 'not_implemented', 'message': 'Email testing endpoint - to be implemented in Phase 4' }), 501 # Health check endpoint @bp.route('/health', methods=['GET']) def health_check(): """ Health check endpoint for monitoring. Returns: JSON response with API health status """ return jsonify({ 'status': 'healthy', 'api': 'settings', 'version': '1.0.0-phase1' })