diff --git a/contact/ui/default_config.py b/contact/ui/default_config.py index 6bbb9b2..b81a776 100644 --- a/contact/ui/default_config.py +++ b/contact/ui/default_config.py @@ -56,9 +56,13 @@ def _get_config_root(preferred_dir: str, fallback_name: str = ".contact_client") return fallback_dir - -# Pick the root now. -config_root = _get_config_root(parent_dir) +# Allow overriding config root via environment variable +config_root = os.getenv("CONTACT_CONFIG_ROOT") +if config_root: + if not _is_writable_dir(config_root): + raise RuntimeError(f"CONTACT_CONFIG_ROOT={config_root} is not writable") +else: + config_root = _get_config_root(parent_dir) # Paths (derived from the chosen root) json_file_path = os.path.join(config_root, "config.json") diff --git a/contact/utilities/db_handler.py b/contact/utilities/db_handler.py index 1fb14a1..d10fb26 100644 --- a/contact/utilities/db_handler.py +++ b/contact/utilities/db_handler.py @@ -31,7 +31,8 @@ def save_message_to_db(channel: str, user_id: str, message_text: str) -> Optiona """ ensure_table_exists(quoted_table_name, schema) - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") db_cursor = db_connection.cursor() timestamp = int(time.time()) @@ -53,7 +54,8 @@ def save_message_to_db(channel: str, user_id: str, message_text: str) -> Optiona def update_ack_nak(channel: str, timestamp: int, message: str, ack: str) -> None: try: - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") db_cursor = db_connection.cursor() update_query = f""" UPDATE {get_table_name(channel)} @@ -76,7 +78,8 @@ def update_ack_nak(channel: str, timestamp: int, message: str, ack: str) -> None def load_messages_from_db() -> None: """Load messages from the database for all channels and update ui_state.all_messages and ui_state.channel_list.""" try: - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") db_cursor = db_connection.cursor() query = "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE ?" @@ -92,6 +95,7 @@ def load_messages_from_db() -> None: if "ack_type" not in table_columns: update_table_query = f"ALTER TABLE {quoted_table_name} ADD COLUMN ack_type TEXT" db_cursor.execute(update_table_query) + db_connection.commit() query = f"SELECT user_id, message_text, timestamp, ack_type FROM {quoted_table_name}" @@ -228,7 +232,8 @@ def update_node_info_in_db( try: ensure_node_table_exists() # Ensure the table exists before any operation - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") db_cursor = db_connection.cursor() table_name = f'"{interface_state.myNodeNum}_nodedb"' # Quote in case of numeric names @@ -236,6 +241,7 @@ def update_node_info_in_db( if "chat_archived" not in table_columns: update_table_query = f"ALTER TABLE {table_name} ADD COLUMN chat_archived INTEGER" db_cursor.execute(update_table_query) + db_connection.commit() # Fetch existing values to preserve unchanged fields db_cursor.execute(f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,)) @@ -311,7 +317,8 @@ def ensure_node_table_exists() -> None: def ensure_table_exists(table_name: str, schema: str) -> None: """Ensure the given table exists in the database.""" try: - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") db_cursor = db_connection.cursor() create_table_query = f"CREATE TABLE IF NOT EXISTS {table_name} ({schema})" db_cursor.execute(create_table_query) @@ -331,7 +338,8 @@ def get_name_from_database(user_id: int, type: str = "long") -> str: :return: The retrieved name or the hex of the user id """ try: - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") db_cursor = db_connection.cursor() # Construct table name @@ -359,7 +367,8 @@ def get_name_from_database(user_id: int, type: str = "long") -> str: def is_chat_archived(user_id: int) -> int: try: - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") db_cursor = db_connection.cursor() table_name = f"{str(interface_state.myNodeNum)}_nodedb" nodeinfo_table = f'"{table_name}"' diff --git a/contact/utilities/demo_data.py b/contact/utilities/demo_data.py index 2e76f68..c4e843b 100644 --- a/contact/utilities/demo_data.py +++ b/contact/utilities/demo_data.py @@ -141,7 +141,8 @@ def seed_demo_messages() -> None: ack_type TEXT """ - with sqlite3.connect(config.db_file_path) as db_connection: + with sqlite3.connect(config.db_file_path, timeout=10.0) as db_connection: + db_connection.execute("PRAGMA busy_timeout=10000") cursor = db_connection.cursor() for channel_name, rows in _demo_messages().items():