pomera-ai-commander 0.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +105 -680
- package/bin/pomera-ai-commander.js +62 -62
- package/core/__init__.py +65 -65
- package/core/app_context.py +482 -482
- package/core/async_text_processor.py +421 -421
- package/core/backup_manager.py +655 -655
- package/core/backup_recovery_manager.py +1033 -1033
- package/core/content_hash_cache.py +508 -508
- package/core/context_menu.py +313 -313
- package/core/data_validator.py +1066 -1066
- package/core/database_connection_manager.py +744 -744
- package/core/database_curl_settings_manager.py +608 -608
- package/core/database_promera_ai_settings_manager.py +446 -446
- package/core/database_schema.py +411 -411
- package/core/database_schema_manager.py +395 -395
- package/core/database_settings_manager.py +1507 -1507
- package/core/database_settings_manager_interface.py +456 -456
- package/core/dialog_manager.py +734 -734
- package/core/efficient_line_numbers.py +510 -510
- package/core/error_handler.py +746 -746
- package/core/error_service.py +431 -431
- package/core/event_consolidator.py +511 -511
- package/core/mcp/__init__.py +43 -43
- package/core/mcp/protocol.py +288 -288
- package/core/mcp/schema.py +251 -251
- package/core/mcp/server_stdio.py +299 -299
- package/core/mcp/tool_registry.py +2372 -2345
- package/core/memory_efficient_text_widget.py +711 -711
- package/core/migration_manager.py +914 -914
- package/core/migration_test_suite.py +1085 -1085
- package/core/migration_validator.py +1143 -1143
- package/core/optimized_find_replace.py +714 -714
- package/core/optimized_pattern_engine.py +424 -424
- package/core/optimized_search_highlighter.py +552 -552
- package/core/performance_monitor.py +674 -674
- package/core/persistence_manager.py +712 -712
- package/core/progressive_stats_calculator.py +632 -632
- package/core/regex_pattern_cache.py +529 -529
- package/core/regex_pattern_library.py +350 -350
- package/core/search_operation_manager.py +434 -434
- package/core/settings_defaults_registry.py +1087 -1087
- package/core/settings_integrity_validator.py +1111 -1111
- package/core/settings_serializer.py +557 -557
- package/core/settings_validator.py +1823 -1823
- package/core/smart_stats_calculator.py +709 -709
- package/core/statistics_update_manager.py +619 -619
- package/core/stats_config_manager.py +858 -858
- package/core/streaming_text_handler.py +723 -723
- package/core/task_scheduler.py +596 -596
- package/core/update_pattern_library.py +168 -168
- package/core/visibility_monitor.py +596 -596
- package/core/widget_cache.py +498 -498
- package/mcp.json +51 -61
- package/package.json +61 -57
- package/pomera.py +7482 -7482
- package/pomera_mcp_server.py +183 -144
- package/requirements.txt +32 -0
- package/tools/__init__.py +4 -4
- package/tools/ai_tools.py +2891 -2891
- package/tools/ascii_art_generator.py +352 -352
- package/tools/base64_tools.py +183 -183
- package/tools/base_tool.py +511 -511
- package/tools/case_tool.py +308 -308
- package/tools/column_tools.py +395 -395
- package/tools/cron_tool.py +884 -884
- package/tools/curl_history.py +600 -600
- package/tools/curl_processor.py +1207 -1207
- package/tools/curl_settings.py +502 -502
- package/tools/curl_tool.py +5467 -5467
- package/tools/diff_viewer.py +1071 -1071
- package/tools/email_extraction_tool.py +248 -248
- package/tools/email_header_analyzer.py +425 -425
- package/tools/extraction_tools.py +250 -250
- package/tools/find_replace.py +1750 -1750
- package/tools/folder_file_reporter.py +1463 -1463
- package/tools/folder_file_reporter_adapter.py +480 -480
- package/tools/generator_tools.py +1216 -1216
- package/tools/hash_generator.py +255 -255
- package/tools/html_tool.py +656 -656
- package/tools/jsonxml_tool.py +729 -729
- package/tools/line_tools.py +419 -419
- package/tools/markdown_tools.py +561 -561
- package/tools/mcp_widget.py +1417 -1417
- package/tools/notes_widget.py +973 -973
- package/tools/number_base_converter.py +372 -372
- package/tools/regex_extractor.py +571 -571
- package/tools/slug_generator.py +310 -310
- package/tools/sorter_tools.py +458 -458
- package/tools/string_escape_tool.py +392 -392
- package/tools/text_statistics_tool.py +365 -365
- package/tools/text_wrapper.py +430 -430
- package/tools/timestamp_converter.py +421 -421
- package/tools/tool_loader.py +710 -710
- package/tools/translator_tools.py +522 -522
- package/tools/url_link_extractor.py +261 -261
- package/tools/url_parser.py +204 -204
- package/tools/whitespace_tools.py +355 -355
- package/tools/word_frequency_counter.py +146 -146
- package/core/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/__pycache__/app_context.cpython-313.pyc +0 -0
- package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
- package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
- package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
- package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
- package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/error_service.cpython-313.pyc +0 -0
- package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
- package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
- package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
- package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
- package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
- package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
- package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
- package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
- package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
- package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
- package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
- package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
- package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
- package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
- package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
- package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
- package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
- package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
- package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
- package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
- package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
- package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
- package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
- package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
- package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
- package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
- package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
- package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
- package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
- package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
- package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
package/tools/curl_settings.py
CHANGED
|
@@ -1,503 +1,503 @@
|
|
|
1
|
-
"""
|
|
2
|
-
cURL Tool Settings Management Module
|
|
3
|
-
|
|
4
|
-
This module provides settings persistence and management for the cURL GUI Tool.
|
|
5
|
-
It handles configuration storage, loading, and validation for tool settings.
|
|
6
|
-
|
|
7
|
-
Author: Pomera AI Commander
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
import os
|
|
12
|
-
from typing import Dict, Any, Optional
|
|
13
|
-
import logging
|
|
14
|
-
from datetime import datetime
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class CurlSettingsManager:
|
|
18
|
-
"""
|
|
19
|
-
Manages settings persistence and configuration for the cURL Tool.
|
|
20
|
-
|
|
21
|
-
Handles:
|
|
22
|
-
- Settings file storage and loading
|
|
23
|
-
- Default configuration values
|
|
24
|
-
- Settings validation
|
|
25
|
-
- Configuration backup and restore
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
def __init__(self, settings_file: str = "settings.json", logger=None):
|
|
29
|
-
"""
|
|
30
|
-
Initialize the settings manager.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
settings_file: Path to the main settings file
|
|
34
|
-
logger: Logger instance for debugging
|
|
35
|
-
"""
|
|
36
|
-
self.settings_file = settings_file
|
|
37
|
-
self.logger = logger or logging.getLogger(__name__)
|
|
38
|
-
self.tool_key = "cURL Tool" # Key in tool_settings section
|
|
39
|
-
|
|
40
|
-
# Default settings configuration
|
|
41
|
-
self.default_settings = {
|
|
42
|
-
# Request settings
|
|
43
|
-
"default_timeout": 30,
|
|
44
|
-
"follow_redirects": True,
|
|
45
|
-
"verify_ssl": True,
|
|
46
|
-
"max_redirects": 10,
|
|
47
|
-
"user_agent": "Pomera cURL Tool/1.0",
|
|
48
|
-
|
|
49
|
-
# History settings
|
|
50
|
-
"save_history": True,
|
|
51
|
-
"max_history_items": 100,
|
|
52
|
-
"auto_cleanup_history": True,
|
|
53
|
-
"history_retention_days": 30,
|
|
54
|
-
|
|
55
|
-
# Authentication settings
|
|
56
|
-
"persist_auth": True,
|
|
57
|
-
"auth_timeout_minutes": 60,
|
|
58
|
-
"clear_auth_on_exit": False,
|
|
59
|
-
|
|
60
|
-
# UI settings
|
|
61
|
-
"remember_window_size": True,
|
|
62
|
-
"default_body_type": "JSON",
|
|
63
|
-
"auto_format_json": True,
|
|
64
|
-
"syntax_highlighting": True,
|
|
65
|
-
"show_response_time": True,
|
|
66
|
-
|
|
67
|
-
# Download settings
|
|
68
|
-
"default_download_path": "",
|
|
69
|
-
"use_remote_filename": True,
|
|
70
|
-
"resume_downloads": True,
|
|
71
|
-
"download_chunk_size": 8192,
|
|
72
|
-
|
|
73
|
-
# Export/Import settings
|
|
74
|
-
"curl_export_format": "standard", # standard, minimal, verbose
|
|
75
|
-
"include_comments_in_export": True,
|
|
76
|
-
"auto_escape_special_chars": True,
|
|
77
|
-
"complex_options": "", # Additional cURL options not handled by UI
|
|
78
|
-
|
|
79
|
-
# Debug settings
|
|
80
|
-
"enable_debug_logging": False,
|
|
81
|
-
"log_request_headers": True,
|
|
82
|
-
"log_response_headers": True,
|
|
83
|
-
"max_log_size_mb": 10,
|
|
84
|
-
|
|
85
|
-
# Advanced settings
|
|
86
|
-
"connection_pool_size": 10,
|
|
87
|
-
"retry_attempts": 3,
|
|
88
|
-
"retry_delay_seconds": 1,
|
|
89
|
-
"enable_http2": False,
|
|
90
|
-
|
|
91
|
-
# Version and metadata
|
|
92
|
-
"settings_version": "1.0",
|
|
93
|
-
"last_updated": None,
|
|
94
|
-
"created_date": None
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
# Current settings (loaded from file or defaults)
|
|
98
|
-
self.settings = {}
|
|
99
|
-
|
|
100
|
-
# Load settings on initialization
|
|
101
|
-
self.load_settings()
|
|
102
|
-
|
|
103
|
-
def load_settings(self) -> Dict[str, Any]:
|
|
104
|
-
"""
|
|
105
|
-
Load settings from centralized settings.json file.
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
Dictionary of current settings
|
|
109
|
-
"""
|
|
110
|
-
try:
|
|
111
|
-
if os.path.exists(self.settings_file):
|
|
112
|
-
with open(self.settings_file, 'r', encoding='utf-8') as f:
|
|
113
|
-
all_settings = json.load(f)
|
|
114
|
-
|
|
115
|
-
# Get tool settings from tool_settings section
|
|
116
|
-
tool_settings = all_settings.get("tool_settings", {})
|
|
117
|
-
file_settings = tool_settings.get(self.tool_key, {})
|
|
118
|
-
|
|
119
|
-
# Merge with defaults to ensure all keys exist
|
|
120
|
-
self.settings = self.default_settings.copy()
|
|
121
|
-
self.settings.update(file_settings)
|
|
122
|
-
|
|
123
|
-
# Validate and fix any invalid settings
|
|
124
|
-
self._validate_settings()
|
|
125
|
-
|
|
126
|
-
self.logger.info(f"cURL Tool settings loaded from {self.settings_file}")
|
|
127
|
-
else:
|
|
128
|
-
# Use defaults and create file
|
|
129
|
-
self.settings = self.default_settings.copy()
|
|
130
|
-
self.settings["created_date"] = datetime.now().isoformat()
|
|
131
|
-
self.save_settings()
|
|
132
|
-
self.logger.info("Created new settings file with defaults")
|
|
133
|
-
|
|
134
|
-
except Exception as e:
|
|
135
|
-
self.logger.error(f"Error loading settings: {e}")
|
|
136
|
-
# Fall back to defaults
|
|
137
|
-
self.settings = self.default_settings.copy()
|
|
138
|
-
self.settings["created_date"] = datetime.now().isoformat()
|
|
139
|
-
|
|
140
|
-
return self.settings
|
|
141
|
-
|
|
142
|
-
def save_settings(self) -> bool:
|
|
143
|
-
"""
|
|
144
|
-
Save current settings to centralized settings.json file.
|
|
145
|
-
|
|
146
|
-
Returns:
|
|
147
|
-
True if successful, False otherwise
|
|
148
|
-
"""
|
|
149
|
-
try:
|
|
150
|
-
# Update last modified timestamp
|
|
151
|
-
self.settings["last_updated"] = datetime.now().isoformat()
|
|
152
|
-
|
|
153
|
-
# Load existing settings file
|
|
154
|
-
all_settings = {}
|
|
155
|
-
if os.path.exists(self.settings_file):
|
|
156
|
-
with open(self.settings_file, 'r', encoding='utf-8') as f:
|
|
157
|
-
all_settings = json.load(f)
|
|
158
|
-
|
|
159
|
-
# Ensure tool_settings section exists
|
|
160
|
-
if "tool_settings" not in all_settings:
|
|
161
|
-
all_settings["tool_settings"] = {}
|
|
162
|
-
|
|
163
|
-
# Update cURL Tool settings (merge with existing data to preserve history)
|
|
164
|
-
if self.tool_key not in all_settings["tool_settings"]:
|
|
165
|
-
all_settings["tool_settings"][self.tool_key] = {}
|
|
166
|
-
|
|
167
|
-
# Preserve existing non-settings data (like history, collections)
|
|
168
|
-
existing_data = all_settings["tool_settings"][self.tool_key]
|
|
169
|
-
|
|
170
|
-
# Update with current settings
|
|
171
|
-
all_settings["tool_settings"][self.tool_key] = {**existing_data, **self.settings}
|
|
172
|
-
|
|
173
|
-
# Create backup of existing settings
|
|
174
|
-
self._create_backup()
|
|
175
|
-
|
|
176
|
-
# Write settings to file
|
|
177
|
-
with open(self.settings_file, 'w', encoding='utf-8') as f:
|
|
178
|
-
json.dump(all_settings, f, indent=4, ensure_ascii=False)
|
|
179
|
-
|
|
180
|
-
self.logger.info(f"cURL Tool settings saved to {self.settings_file}")
|
|
181
|
-
return True
|
|
182
|
-
|
|
183
|
-
except Exception as e:
|
|
184
|
-
self.logger.error(f"Error saving settings: {e}")
|
|
185
|
-
return False
|
|
186
|
-
|
|
187
|
-
def get_setting(self, key: str, default: Any = None) -> Any:
|
|
188
|
-
"""
|
|
189
|
-
Get a setting value.
|
|
190
|
-
|
|
191
|
-
Args:
|
|
192
|
-
key: Setting key
|
|
193
|
-
default: Default value if key not found
|
|
194
|
-
|
|
195
|
-
Returns:
|
|
196
|
-
Setting value or default
|
|
197
|
-
"""
|
|
198
|
-
return self.settings.get(key, default)
|
|
199
|
-
|
|
200
|
-
def set_setting(self, key: str, value: Any) -> bool:
|
|
201
|
-
"""
|
|
202
|
-
Set a setting value.
|
|
203
|
-
|
|
204
|
-
Args:
|
|
205
|
-
key: Setting key
|
|
206
|
-
value: Setting value
|
|
207
|
-
|
|
208
|
-
Returns:
|
|
209
|
-
True if successful, False otherwise
|
|
210
|
-
"""
|
|
211
|
-
try:
|
|
212
|
-
# Validate the setting
|
|
213
|
-
if self._validate_setting(key, value):
|
|
214
|
-
self.settings[key] = value
|
|
215
|
-
return True
|
|
216
|
-
else:
|
|
217
|
-
self.logger.warning(f"Invalid setting value: {key} = {value}")
|
|
218
|
-
return False
|
|
219
|
-
|
|
220
|
-
except Exception as e:
|
|
221
|
-
self.logger.error(f"Error setting {key}: {e}")
|
|
222
|
-
return False
|
|
223
|
-
|
|
224
|
-
def reset_to_defaults(self) -> bool:
|
|
225
|
-
"""
|
|
226
|
-
Reset all settings to defaults.
|
|
227
|
-
|
|
228
|
-
Returns:
|
|
229
|
-
True if successful, False otherwise
|
|
230
|
-
"""
|
|
231
|
-
try:
|
|
232
|
-
# Keep creation date if it exists
|
|
233
|
-
created_date = self.settings.get("created_date")
|
|
234
|
-
|
|
235
|
-
self.settings = self.default_settings.copy()
|
|
236
|
-
if created_date:
|
|
237
|
-
self.settings["created_date"] = created_date
|
|
238
|
-
|
|
239
|
-
return self.save_settings()
|
|
240
|
-
|
|
241
|
-
except Exception as e:
|
|
242
|
-
self.logger.error(f"Error resetting settings: {e}")
|
|
243
|
-
return False
|
|
244
|
-
|
|
245
|
-
def export_settings(self, filepath: str) -> bool:
|
|
246
|
-
"""
|
|
247
|
-
Export settings to a file.
|
|
248
|
-
|
|
249
|
-
Args:
|
|
250
|
-
filepath: Path to export file
|
|
251
|
-
|
|
252
|
-
Returns:
|
|
253
|
-
True if successful, False otherwise
|
|
254
|
-
"""
|
|
255
|
-
try:
|
|
256
|
-
export_data = {
|
|
257
|
-
"settings": self.settings,
|
|
258
|
-
"export_date": datetime.now().isoformat(),
|
|
259
|
-
"version": self.settings.get("settings_version", "1.0")
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
with open(filepath, 'w', encoding='utf-8') as f:
|
|
263
|
-
json.dump(export_data, f, indent=2, ensure_ascii=False)
|
|
264
|
-
|
|
265
|
-
self.logger.info(f"Settings exported to {filepath}")
|
|
266
|
-
return True
|
|
267
|
-
|
|
268
|
-
except Exception as e:
|
|
269
|
-
self.logger.error(f"Error exporting settings: {e}")
|
|
270
|
-
return False
|
|
271
|
-
|
|
272
|
-
def import_settings(self, filepath: str) -> bool:
|
|
273
|
-
"""
|
|
274
|
-
Import settings from a file.
|
|
275
|
-
|
|
276
|
-
Args:
|
|
277
|
-
filepath: Path to import file
|
|
278
|
-
|
|
279
|
-
Returns:
|
|
280
|
-
True if successful, False otherwise
|
|
281
|
-
"""
|
|
282
|
-
try:
|
|
283
|
-
with open(filepath, 'r', encoding='utf-8') as f:
|
|
284
|
-
import_data = json.load(f)
|
|
285
|
-
|
|
286
|
-
# Extract settings from import data
|
|
287
|
-
if "settings" in import_data:
|
|
288
|
-
imported_settings = import_data["settings"]
|
|
289
|
-
else:
|
|
290
|
-
# Assume the file contains settings directly
|
|
291
|
-
imported_settings = import_data
|
|
292
|
-
|
|
293
|
-
# Merge with current settings (don't overwrite everything)
|
|
294
|
-
for key, value in imported_settings.items():
|
|
295
|
-
if key in self.default_settings:
|
|
296
|
-
if self._validate_setting(key, value):
|
|
297
|
-
self.settings[key] = value
|
|
298
|
-
|
|
299
|
-
# Save the updated settings
|
|
300
|
-
return self.save_settings()
|
|
301
|
-
|
|
302
|
-
except Exception as e:
|
|
303
|
-
self.logger.error(f"Error importing settings: {e}")
|
|
304
|
-
return False
|
|
305
|
-
|
|
306
|
-
def get_all_settings(self) -> Dict[str, Any]:
|
|
307
|
-
"""
|
|
308
|
-
Get all current settings.
|
|
309
|
-
|
|
310
|
-
Returns:
|
|
311
|
-
Dictionary of all settings
|
|
312
|
-
"""
|
|
313
|
-
return self.settings.copy()
|
|
314
|
-
|
|
315
|
-
def get_default_settings(self) -> Dict[str, Any]:
|
|
316
|
-
"""
|
|
317
|
-
Get default settings.
|
|
318
|
-
|
|
319
|
-
Returns:
|
|
320
|
-
Dictionary of default settings
|
|
321
|
-
"""
|
|
322
|
-
return self.default_settings.copy()
|
|
323
|
-
|
|
324
|
-
def _validate_settings(self):
|
|
325
|
-
"""Validate and fix invalid settings."""
|
|
326
|
-
# Ensure numeric settings are within valid ranges
|
|
327
|
-
numeric_ranges = {
|
|
328
|
-
"default_timeout": (1, 300),
|
|
329
|
-
"max_redirects": (0, 50),
|
|
330
|
-
"max_history_items": (10, 1000),
|
|
331
|
-
"history_retention_days": (1, 365),
|
|
332
|
-
"auth_timeout_minutes": (5, 1440),
|
|
333
|
-
"download_chunk_size": (1024, 1048576),
|
|
334
|
-
"connection_pool_size": (1, 100),
|
|
335
|
-
"retry_attempts": (0, 10),
|
|
336
|
-
"retry_delay_seconds": (0, 60),
|
|
337
|
-
"max_log_size_mb": (1, 100)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
for key, (min_val, max_val) in numeric_ranges.items():
|
|
341
|
-
if key in self.settings:
|
|
342
|
-
value = self.settings[key]
|
|
343
|
-
if not isinstance(value, (int, float)) or value < min_val or value > max_val:
|
|
344
|
-
self.settings[key] = self.default_settings[key]
|
|
345
|
-
self.logger.warning(f"Reset invalid setting {key} to default")
|
|
346
|
-
|
|
347
|
-
# Ensure string settings are not empty where required
|
|
348
|
-
required_strings = ["user_agent", "curl_export_format", "settings_version"]
|
|
349
|
-
for key in required_strings:
|
|
350
|
-
if key in self.settings and not isinstance(self.settings[key], str):
|
|
351
|
-
self.settings[key] = self.default_settings[key]
|
|
352
|
-
self.logger.warning(f"Reset invalid string setting {key} to default")
|
|
353
|
-
|
|
354
|
-
# Ensure boolean settings are actually boolean
|
|
355
|
-
boolean_settings = [
|
|
356
|
-
"follow_redirects", "verify_ssl", "save_history", "auto_cleanup_history",
|
|
357
|
-
"persist_auth", "clear_auth_on_exit", "remember_window_size",
|
|
358
|
-
"auto_format_json", "syntax_highlighting", "show_response_time",
|
|
359
|
-
"use_remote_filename", "resume_downloads", "include_comments_in_export",
|
|
360
|
-
"auto_escape_special_chars", "enable_debug_logging", "log_request_headers",
|
|
361
|
-
"log_response_headers", "enable_http2"
|
|
362
|
-
]
|
|
363
|
-
|
|
364
|
-
for key in boolean_settings:
|
|
365
|
-
if key in self.settings and not isinstance(self.settings[key], bool):
|
|
366
|
-
self.settings[key] = self.default_settings[key]
|
|
367
|
-
self.logger.warning(f"Reset invalid boolean setting {key} to default")
|
|
368
|
-
|
|
369
|
-
def _validate_setting(self, key: str, value: Any) -> bool:
|
|
370
|
-
"""
|
|
371
|
-
Validate a single setting value.
|
|
372
|
-
|
|
373
|
-
Args:
|
|
374
|
-
key: Setting key
|
|
375
|
-
value: Setting value
|
|
376
|
-
|
|
377
|
-
Returns:
|
|
378
|
-
True if valid, False otherwise
|
|
379
|
-
"""
|
|
380
|
-
if key not in self.default_settings:
|
|
381
|
-
return False
|
|
382
|
-
|
|
383
|
-
# Type validation
|
|
384
|
-
expected_type = type(self.default_settings[key])
|
|
385
|
-
if not isinstance(value, expected_type):
|
|
386
|
-
# Allow None for optional settings
|
|
387
|
-
if value is None and key in ["default_download_path", "last_updated", "created_date"]:
|
|
388
|
-
return True
|
|
389
|
-
return False
|
|
390
|
-
|
|
391
|
-
# Range validation for numeric values
|
|
392
|
-
numeric_ranges = {
|
|
393
|
-
"default_timeout": (1, 300),
|
|
394
|
-
"max_redirects": (0, 50),
|
|
395
|
-
"max_history_items": (10, 1000),
|
|
396
|
-
"history_retention_days": (1, 365),
|
|
397
|
-
"auth_timeout_minutes": (5, 1440),
|
|
398
|
-
"download_chunk_size": (1024, 1048576),
|
|
399
|
-
"connection_pool_size": (1, 100),
|
|
400
|
-
"retry_attempts": (0, 10),
|
|
401
|
-
"retry_delay_seconds": (0, 60),
|
|
402
|
-
"max_log_size_mb": (1, 100)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if key in numeric_ranges:
|
|
406
|
-
min_val, max_val = numeric_ranges[key]
|
|
407
|
-
return min_val <= value <= max_val
|
|
408
|
-
|
|
409
|
-
# Enum validation for specific settings
|
|
410
|
-
if key == "curl_export_format":
|
|
411
|
-
return value in ["standard", "minimal", "verbose"]
|
|
412
|
-
|
|
413
|
-
if key == "default_body_type":
|
|
414
|
-
return value in ["None", "JSON", "Form Data", "Multipart Form", "Raw Text", "Binary"]
|
|
415
|
-
|
|
416
|
-
return True
|
|
417
|
-
|
|
418
|
-
def _create_backup(self):
|
|
419
|
-
"""Create a backup of the current settings file."""
|
|
420
|
-
try:
|
|
421
|
-
if os.path.exists(self.settings_file):
|
|
422
|
-
backup_file = f"{self.settings_file}.backup"
|
|
423
|
-
|
|
424
|
-
# Read current file
|
|
425
|
-
with open(self.settings_file, 'r', encoding='utf-8') as f:
|
|
426
|
-
content = f.read()
|
|
427
|
-
|
|
428
|
-
# Write backup
|
|
429
|
-
with open(backup_file, 'w', encoding='utf-8') as f:
|
|
430
|
-
f.write(content)
|
|
431
|
-
|
|
432
|
-
self.logger.debug(f"Settings backup created: {backup_file}")
|
|
433
|
-
|
|
434
|
-
except Exception as e:
|
|
435
|
-
self.logger.warning(f"Could not create settings backup: {e}")
|
|
436
|
-
|
|
437
|
-
def restore_from_backup(self) -> bool:
|
|
438
|
-
"""
|
|
439
|
-
Restore settings from backup file.
|
|
440
|
-
|
|
441
|
-
Returns:
|
|
442
|
-
True if successful, False otherwise
|
|
443
|
-
"""
|
|
444
|
-
try:
|
|
445
|
-
backup_file = f"{self.settings_file}.backup"
|
|
446
|
-
|
|
447
|
-
if os.path.exists(backup_file):
|
|
448
|
-
# Load backup
|
|
449
|
-
with open(backup_file, 'r', encoding='utf-8') as f:
|
|
450
|
-
backup_settings = json.load(f)
|
|
451
|
-
|
|
452
|
-
# Validate backup settings
|
|
453
|
-
temp_settings = self.settings.copy()
|
|
454
|
-
self.settings = backup_settings
|
|
455
|
-
self._validate_settings()
|
|
456
|
-
|
|
457
|
-
# Save restored settings
|
|
458
|
-
if self.save_settings():
|
|
459
|
-
self.logger.info("Settings restored from backup")
|
|
460
|
-
return True
|
|
461
|
-
else:
|
|
462
|
-
# Restore original settings if save failed
|
|
463
|
-
self.settings = temp_settings
|
|
464
|
-
return False
|
|
465
|
-
else:
|
|
466
|
-
self.logger.warning("No backup file found")
|
|
467
|
-
return False
|
|
468
|
-
|
|
469
|
-
except Exception as e:
|
|
470
|
-
self.logger.error(f"Error restoring from backup: {e}")
|
|
471
|
-
return False
|
|
472
|
-
|
|
473
|
-
def cleanup_old_backups(self, max_backups: int = 5):
|
|
474
|
-
"""
|
|
475
|
-
Clean up old backup files.
|
|
476
|
-
|
|
477
|
-
Args:
|
|
478
|
-
max_backups: Maximum number of backup files to keep
|
|
479
|
-
"""
|
|
480
|
-
try:
|
|
481
|
-
backup_pattern = f"{self.settings_file}.backup"
|
|
482
|
-
backup_dir = os.path.dirname(self.settings_file) or "."
|
|
483
|
-
|
|
484
|
-
# Find all backup files
|
|
485
|
-
backup_files = []
|
|
486
|
-
for filename in os.listdir(backup_dir):
|
|
487
|
-
if filename.startswith(os.path.basename(backup_pattern)):
|
|
488
|
-
filepath = os.path.join(backup_dir, filename)
|
|
489
|
-
backup_files.append((filepath, os.path.getmtime(filepath)))
|
|
490
|
-
|
|
491
|
-
# Sort by modification time (newest first)
|
|
492
|
-
backup_files.sort(key=lambda x: x[1], reverse=True)
|
|
493
|
-
|
|
494
|
-
# Remove old backups
|
|
495
|
-
for filepath, _ in backup_files[max_backups:]:
|
|
496
|
-
try:
|
|
497
|
-
os.remove(filepath)
|
|
498
|
-
self.logger.debug(f"Removed old backup: {filepath}")
|
|
499
|
-
except Exception as e:
|
|
500
|
-
self.logger.warning(f"Could not remove backup {filepath}: {e}")
|
|
501
|
-
|
|
502
|
-
except Exception as e:
|
|
1
|
+
"""
|
|
2
|
+
cURL Tool Settings Management Module
|
|
3
|
+
|
|
4
|
+
This module provides settings persistence and management for the cURL GUI Tool.
|
|
5
|
+
It handles configuration storage, loading, and validation for tool settings.
|
|
6
|
+
|
|
7
|
+
Author: Pomera AI Commander
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
from typing import Dict, Any, Optional
|
|
13
|
+
import logging
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CurlSettingsManager:
|
|
18
|
+
"""
|
|
19
|
+
Manages settings persistence and configuration for the cURL Tool.
|
|
20
|
+
|
|
21
|
+
Handles:
|
|
22
|
+
- Settings file storage and loading
|
|
23
|
+
- Default configuration values
|
|
24
|
+
- Settings validation
|
|
25
|
+
- Configuration backup and restore
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, settings_file: str = "settings.json", logger=None):
|
|
29
|
+
"""
|
|
30
|
+
Initialize the settings manager.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
settings_file: Path to the main settings file
|
|
34
|
+
logger: Logger instance for debugging
|
|
35
|
+
"""
|
|
36
|
+
self.settings_file = settings_file
|
|
37
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
38
|
+
self.tool_key = "cURL Tool" # Key in tool_settings section
|
|
39
|
+
|
|
40
|
+
# Default settings configuration
|
|
41
|
+
self.default_settings = {
|
|
42
|
+
# Request settings
|
|
43
|
+
"default_timeout": 30,
|
|
44
|
+
"follow_redirects": True,
|
|
45
|
+
"verify_ssl": True,
|
|
46
|
+
"max_redirects": 10,
|
|
47
|
+
"user_agent": "Pomera cURL Tool/1.0",
|
|
48
|
+
|
|
49
|
+
# History settings
|
|
50
|
+
"save_history": True,
|
|
51
|
+
"max_history_items": 100,
|
|
52
|
+
"auto_cleanup_history": True,
|
|
53
|
+
"history_retention_days": 30,
|
|
54
|
+
|
|
55
|
+
# Authentication settings
|
|
56
|
+
"persist_auth": True,
|
|
57
|
+
"auth_timeout_minutes": 60,
|
|
58
|
+
"clear_auth_on_exit": False,
|
|
59
|
+
|
|
60
|
+
# UI settings
|
|
61
|
+
"remember_window_size": True,
|
|
62
|
+
"default_body_type": "JSON",
|
|
63
|
+
"auto_format_json": True,
|
|
64
|
+
"syntax_highlighting": True,
|
|
65
|
+
"show_response_time": True,
|
|
66
|
+
|
|
67
|
+
# Download settings
|
|
68
|
+
"default_download_path": "",
|
|
69
|
+
"use_remote_filename": True,
|
|
70
|
+
"resume_downloads": True,
|
|
71
|
+
"download_chunk_size": 8192,
|
|
72
|
+
|
|
73
|
+
# Export/Import settings
|
|
74
|
+
"curl_export_format": "standard", # standard, minimal, verbose
|
|
75
|
+
"include_comments_in_export": True,
|
|
76
|
+
"auto_escape_special_chars": True,
|
|
77
|
+
"complex_options": "", # Additional cURL options not handled by UI
|
|
78
|
+
|
|
79
|
+
# Debug settings
|
|
80
|
+
"enable_debug_logging": False,
|
|
81
|
+
"log_request_headers": True,
|
|
82
|
+
"log_response_headers": True,
|
|
83
|
+
"max_log_size_mb": 10,
|
|
84
|
+
|
|
85
|
+
# Advanced settings
|
|
86
|
+
"connection_pool_size": 10,
|
|
87
|
+
"retry_attempts": 3,
|
|
88
|
+
"retry_delay_seconds": 1,
|
|
89
|
+
"enable_http2": False,
|
|
90
|
+
|
|
91
|
+
# Version and metadata
|
|
92
|
+
"settings_version": "1.0",
|
|
93
|
+
"last_updated": None,
|
|
94
|
+
"created_date": None
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Current settings (loaded from file or defaults)
|
|
98
|
+
self.settings = {}
|
|
99
|
+
|
|
100
|
+
# Load settings on initialization
|
|
101
|
+
self.load_settings()
|
|
102
|
+
|
|
103
|
+
def load_settings(self) -> Dict[str, Any]:
|
|
104
|
+
"""
|
|
105
|
+
Load settings from centralized settings.json file.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Dictionary of current settings
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
if os.path.exists(self.settings_file):
|
|
112
|
+
with open(self.settings_file, 'r', encoding='utf-8') as f:
|
|
113
|
+
all_settings = json.load(f)
|
|
114
|
+
|
|
115
|
+
# Get tool settings from tool_settings section
|
|
116
|
+
tool_settings = all_settings.get("tool_settings", {})
|
|
117
|
+
file_settings = tool_settings.get(self.tool_key, {})
|
|
118
|
+
|
|
119
|
+
# Merge with defaults to ensure all keys exist
|
|
120
|
+
self.settings = self.default_settings.copy()
|
|
121
|
+
self.settings.update(file_settings)
|
|
122
|
+
|
|
123
|
+
# Validate and fix any invalid settings
|
|
124
|
+
self._validate_settings()
|
|
125
|
+
|
|
126
|
+
self.logger.info(f"cURL Tool settings loaded from {self.settings_file}")
|
|
127
|
+
else:
|
|
128
|
+
# Use defaults and create file
|
|
129
|
+
self.settings = self.default_settings.copy()
|
|
130
|
+
self.settings["created_date"] = datetime.now().isoformat()
|
|
131
|
+
self.save_settings()
|
|
132
|
+
self.logger.info("Created new settings file with defaults")
|
|
133
|
+
|
|
134
|
+
except Exception as e:
|
|
135
|
+
self.logger.error(f"Error loading settings: {e}")
|
|
136
|
+
# Fall back to defaults
|
|
137
|
+
self.settings = self.default_settings.copy()
|
|
138
|
+
self.settings["created_date"] = datetime.now().isoformat()
|
|
139
|
+
|
|
140
|
+
return self.settings
|
|
141
|
+
|
|
142
|
+
def save_settings(self) -> bool:
|
|
143
|
+
"""
|
|
144
|
+
Save current settings to centralized settings.json file.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
True if successful, False otherwise
|
|
148
|
+
"""
|
|
149
|
+
try:
|
|
150
|
+
# Update last modified timestamp
|
|
151
|
+
self.settings["last_updated"] = datetime.now().isoformat()
|
|
152
|
+
|
|
153
|
+
# Load existing settings file
|
|
154
|
+
all_settings = {}
|
|
155
|
+
if os.path.exists(self.settings_file):
|
|
156
|
+
with open(self.settings_file, 'r', encoding='utf-8') as f:
|
|
157
|
+
all_settings = json.load(f)
|
|
158
|
+
|
|
159
|
+
# Ensure tool_settings section exists
|
|
160
|
+
if "tool_settings" not in all_settings:
|
|
161
|
+
all_settings["tool_settings"] = {}
|
|
162
|
+
|
|
163
|
+
# Update cURL Tool settings (merge with existing data to preserve history)
|
|
164
|
+
if self.tool_key not in all_settings["tool_settings"]:
|
|
165
|
+
all_settings["tool_settings"][self.tool_key] = {}
|
|
166
|
+
|
|
167
|
+
# Preserve existing non-settings data (like history, collections)
|
|
168
|
+
existing_data = all_settings["tool_settings"][self.tool_key]
|
|
169
|
+
|
|
170
|
+
# Update with current settings
|
|
171
|
+
all_settings["tool_settings"][self.tool_key] = {**existing_data, **self.settings}
|
|
172
|
+
|
|
173
|
+
# Create backup of existing settings
|
|
174
|
+
self._create_backup()
|
|
175
|
+
|
|
176
|
+
# Write settings to file
|
|
177
|
+
with open(self.settings_file, 'w', encoding='utf-8') as f:
|
|
178
|
+
json.dump(all_settings, f, indent=4, ensure_ascii=False)
|
|
179
|
+
|
|
180
|
+
self.logger.info(f"cURL Tool settings saved to {self.settings_file}")
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
self.logger.error(f"Error saving settings: {e}")
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
def get_setting(self, key: str, default: Any = None) -> Any:
|
|
188
|
+
"""
|
|
189
|
+
Get a setting value.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
key: Setting key
|
|
193
|
+
default: Default value if key not found
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Setting value or default
|
|
197
|
+
"""
|
|
198
|
+
return self.settings.get(key, default)
|
|
199
|
+
|
|
200
|
+
def set_setting(self, key: str, value: Any) -> bool:
|
|
201
|
+
"""
|
|
202
|
+
Set a setting value.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
key: Setting key
|
|
206
|
+
value: Setting value
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
True if successful, False otherwise
|
|
210
|
+
"""
|
|
211
|
+
try:
|
|
212
|
+
# Validate the setting
|
|
213
|
+
if self._validate_setting(key, value):
|
|
214
|
+
self.settings[key] = value
|
|
215
|
+
return True
|
|
216
|
+
else:
|
|
217
|
+
self.logger.warning(f"Invalid setting value: {key} = {value}")
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
except Exception as e:
|
|
221
|
+
self.logger.error(f"Error setting {key}: {e}")
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
def reset_to_defaults(self) -> bool:
|
|
225
|
+
"""
|
|
226
|
+
Reset all settings to defaults.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
True if successful, False otherwise
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
# Keep creation date if it exists
|
|
233
|
+
created_date = self.settings.get("created_date")
|
|
234
|
+
|
|
235
|
+
self.settings = self.default_settings.copy()
|
|
236
|
+
if created_date:
|
|
237
|
+
self.settings["created_date"] = created_date
|
|
238
|
+
|
|
239
|
+
return self.save_settings()
|
|
240
|
+
|
|
241
|
+
except Exception as e:
|
|
242
|
+
self.logger.error(f"Error resetting settings: {e}")
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
def export_settings(self, filepath: str) -> bool:
|
|
246
|
+
"""
|
|
247
|
+
Export settings to a file.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
filepath: Path to export file
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
True if successful, False otherwise
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
export_data = {
|
|
257
|
+
"settings": self.settings,
|
|
258
|
+
"export_date": datetime.now().isoformat(),
|
|
259
|
+
"version": self.settings.get("settings_version", "1.0")
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
263
|
+
json.dump(export_data, f, indent=2, ensure_ascii=False)
|
|
264
|
+
|
|
265
|
+
self.logger.info(f"Settings exported to {filepath}")
|
|
266
|
+
return True
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
self.logger.error(f"Error exporting settings: {e}")
|
|
270
|
+
return False
|
|
271
|
+
|
|
272
|
+
def import_settings(self, filepath: str) -> bool:
|
|
273
|
+
"""
|
|
274
|
+
Import settings from a file.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
filepath: Path to import file
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
True if successful, False otherwise
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
284
|
+
import_data = json.load(f)
|
|
285
|
+
|
|
286
|
+
# Extract settings from import data
|
|
287
|
+
if "settings" in import_data:
|
|
288
|
+
imported_settings = import_data["settings"]
|
|
289
|
+
else:
|
|
290
|
+
# Assume the file contains settings directly
|
|
291
|
+
imported_settings = import_data
|
|
292
|
+
|
|
293
|
+
# Merge with current settings (don't overwrite everything)
|
|
294
|
+
for key, value in imported_settings.items():
|
|
295
|
+
if key in self.default_settings:
|
|
296
|
+
if self._validate_setting(key, value):
|
|
297
|
+
self.settings[key] = value
|
|
298
|
+
|
|
299
|
+
# Save the updated settings
|
|
300
|
+
return self.save_settings()
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
self.logger.error(f"Error importing settings: {e}")
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
def get_all_settings(self) -> Dict[str, Any]:
|
|
307
|
+
"""
|
|
308
|
+
Get all current settings.
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Dictionary of all settings
|
|
312
|
+
"""
|
|
313
|
+
return self.settings.copy()
|
|
314
|
+
|
|
315
|
+
def get_default_settings(self) -> Dict[str, Any]:
|
|
316
|
+
"""
|
|
317
|
+
Get default settings.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Dictionary of default settings
|
|
321
|
+
"""
|
|
322
|
+
return self.default_settings.copy()
|
|
323
|
+
|
|
324
|
+
def _validate_settings(self):
|
|
325
|
+
"""Validate and fix invalid settings."""
|
|
326
|
+
# Ensure numeric settings are within valid ranges
|
|
327
|
+
numeric_ranges = {
|
|
328
|
+
"default_timeout": (1, 300),
|
|
329
|
+
"max_redirects": (0, 50),
|
|
330
|
+
"max_history_items": (10, 1000),
|
|
331
|
+
"history_retention_days": (1, 365),
|
|
332
|
+
"auth_timeout_minutes": (5, 1440),
|
|
333
|
+
"download_chunk_size": (1024, 1048576),
|
|
334
|
+
"connection_pool_size": (1, 100),
|
|
335
|
+
"retry_attempts": (0, 10),
|
|
336
|
+
"retry_delay_seconds": (0, 60),
|
|
337
|
+
"max_log_size_mb": (1, 100)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
for key, (min_val, max_val) in numeric_ranges.items():
|
|
341
|
+
if key in self.settings:
|
|
342
|
+
value = self.settings[key]
|
|
343
|
+
if not isinstance(value, (int, float)) or value < min_val or value > max_val:
|
|
344
|
+
self.settings[key] = self.default_settings[key]
|
|
345
|
+
self.logger.warning(f"Reset invalid setting {key} to default")
|
|
346
|
+
|
|
347
|
+
# Ensure string settings are not empty where required
|
|
348
|
+
required_strings = ["user_agent", "curl_export_format", "settings_version"]
|
|
349
|
+
for key in required_strings:
|
|
350
|
+
if key in self.settings and not isinstance(self.settings[key], str):
|
|
351
|
+
self.settings[key] = self.default_settings[key]
|
|
352
|
+
self.logger.warning(f"Reset invalid string setting {key} to default")
|
|
353
|
+
|
|
354
|
+
# Ensure boolean settings are actually boolean
|
|
355
|
+
boolean_settings = [
|
|
356
|
+
"follow_redirects", "verify_ssl", "save_history", "auto_cleanup_history",
|
|
357
|
+
"persist_auth", "clear_auth_on_exit", "remember_window_size",
|
|
358
|
+
"auto_format_json", "syntax_highlighting", "show_response_time",
|
|
359
|
+
"use_remote_filename", "resume_downloads", "include_comments_in_export",
|
|
360
|
+
"auto_escape_special_chars", "enable_debug_logging", "log_request_headers",
|
|
361
|
+
"log_response_headers", "enable_http2"
|
|
362
|
+
]
|
|
363
|
+
|
|
364
|
+
for key in boolean_settings:
|
|
365
|
+
if key in self.settings and not isinstance(self.settings[key], bool):
|
|
366
|
+
self.settings[key] = self.default_settings[key]
|
|
367
|
+
self.logger.warning(f"Reset invalid boolean setting {key} to default")
|
|
368
|
+
|
|
369
|
+
def _validate_setting(self, key: str, value: Any) -> bool:
|
|
370
|
+
"""
|
|
371
|
+
Validate a single setting value.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
key: Setting key
|
|
375
|
+
value: Setting value
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
True if valid, False otherwise
|
|
379
|
+
"""
|
|
380
|
+
if key not in self.default_settings:
|
|
381
|
+
return False
|
|
382
|
+
|
|
383
|
+
# Type validation
|
|
384
|
+
expected_type = type(self.default_settings[key])
|
|
385
|
+
if not isinstance(value, expected_type):
|
|
386
|
+
# Allow None for optional settings
|
|
387
|
+
if value is None and key in ["default_download_path", "last_updated", "created_date"]:
|
|
388
|
+
return True
|
|
389
|
+
return False
|
|
390
|
+
|
|
391
|
+
# Range validation for numeric values
|
|
392
|
+
numeric_ranges = {
|
|
393
|
+
"default_timeout": (1, 300),
|
|
394
|
+
"max_redirects": (0, 50),
|
|
395
|
+
"max_history_items": (10, 1000),
|
|
396
|
+
"history_retention_days": (1, 365),
|
|
397
|
+
"auth_timeout_minutes": (5, 1440),
|
|
398
|
+
"download_chunk_size": (1024, 1048576),
|
|
399
|
+
"connection_pool_size": (1, 100),
|
|
400
|
+
"retry_attempts": (0, 10),
|
|
401
|
+
"retry_delay_seconds": (0, 60),
|
|
402
|
+
"max_log_size_mb": (1, 100)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if key in numeric_ranges:
|
|
406
|
+
min_val, max_val = numeric_ranges[key]
|
|
407
|
+
return min_val <= value <= max_val
|
|
408
|
+
|
|
409
|
+
# Enum validation for specific settings
|
|
410
|
+
if key == "curl_export_format":
|
|
411
|
+
return value in ["standard", "minimal", "verbose"]
|
|
412
|
+
|
|
413
|
+
if key == "default_body_type":
|
|
414
|
+
return value in ["None", "JSON", "Form Data", "Multipart Form", "Raw Text", "Binary"]
|
|
415
|
+
|
|
416
|
+
return True
|
|
417
|
+
|
|
418
|
+
def _create_backup(self):
|
|
419
|
+
"""Create a backup of the current settings file."""
|
|
420
|
+
try:
|
|
421
|
+
if os.path.exists(self.settings_file):
|
|
422
|
+
backup_file = f"{self.settings_file}.backup"
|
|
423
|
+
|
|
424
|
+
# Read current file
|
|
425
|
+
with open(self.settings_file, 'r', encoding='utf-8') as f:
|
|
426
|
+
content = f.read()
|
|
427
|
+
|
|
428
|
+
# Write backup
|
|
429
|
+
with open(backup_file, 'w', encoding='utf-8') as f:
|
|
430
|
+
f.write(content)
|
|
431
|
+
|
|
432
|
+
self.logger.debug(f"Settings backup created: {backup_file}")
|
|
433
|
+
|
|
434
|
+
except Exception as e:
|
|
435
|
+
self.logger.warning(f"Could not create settings backup: {e}")
|
|
436
|
+
|
|
437
|
+
def restore_from_backup(self) -> bool:
|
|
438
|
+
"""
|
|
439
|
+
Restore settings from backup file.
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
True if successful, False otherwise
|
|
443
|
+
"""
|
|
444
|
+
try:
|
|
445
|
+
backup_file = f"{self.settings_file}.backup"
|
|
446
|
+
|
|
447
|
+
if os.path.exists(backup_file):
|
|
448
|
+
# Load backup
|
|
449
|
+
with open(backup_file, 'r', encoding='utf-8') as f:
|
|
450
|
+
backup_settings = json.load(f)
|
|
451
|
+
|
|
452
|
+
# Validate backup settings
|
|
453
|
+
temp_settings = self.settings.copy()
|
|
454
|
+
self.settings = backup_settings
|
|
455
|
+
self._validate_settings()
|
|
456
|
+
|
|
457
|
+
# Save restored settings
|
|
458
|
+
if self.save_settings():
|
|
459
|
+
self.logger.info("Settings restored from backup")
|
|
460
|
+
return True
|
|
461
|
+
else:
|
|
462
|
+
# Restore original settings if save failed
|
|
463
|
+
self.settings = temp_settings
|
|
464
|
+
return False
|
|
465
|
+
else:
|
|
466
|
+
self.logger.warning("No backup file found")
|
|
467
|
+
return False
|
|
468
|
+
|
|
469
|
+
except Exception as e:
|
|
470
|
+
self.logger.error(f"Error restoring from backup: {e}")
|
|
471
|
+
return False
|
|
472
|
+
|
|
473
|
+
def cleanup_old_backups(self, max_backups: int = 5):
|
|
474
|
+
"""
|
|
475
|
+
Clean up old backup files.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
max_backups: Maximum number of backup files to keep
|
|
479
|
+
"""
|
|
480
|
+
try:
|
|
481
|
+
backup_pattern = f"{self.settings_file}.backup"
|
|
482
|
+
backup_dir = os.path.dirname(self.settings_file) or "."
|
|
483
|
+
|
|
484
|
+
# Find all backup files
|
|
485
|
+
backup_files = []
|
|
486
|
+
for filename in os.listdir(backup_dir):
|
|
487
|
+
if filename.startswith(os.path.basename(backup_pattern)):
|
|
488
|
+
filepath = os.path.join(backup_dir, filename)
|
|
489
|
+
backup_files.append((filepath, os.path.getmtime(filepath)))
|
|
490
|
+
|
|
491
|
+
# Sort by modification time (newest first)
|
|
492
|
+
backup_files.sort(key=lambda x: x[1], reverse=True)
|
|
493
|
+
|
|
494
|
+
# Remove old backups
|
|
495
|
+
for filepath, _ in backup_files[max_backups:]:
|
|
496
|
+
try:
|
|
497
|
+
os.remove(filepath)
|
|
498
|
+
self.logger.debug(f"Removed old backup: {filepath}")
|
|
499
|
+
except Exception as e:
|
|
500
|
+
self.logger.warning(f"Could not remove backup {filepath}: {e}")
|
|
501
|
+
|
|
502
|
+
except Exception as e:
|
|
503
503
|
self.logger.warning(f"Error cleaning up backups: {e}")
|