update: added gui handler for core db exceptions
This commit is contained in:
parent
aecfdbad33
commit
3c67e72d2d
12 changed files with 405 additions and 87 deletions
|
|
@ -10,6 +10,7 @@ from core.controllers.SyncController import new_sync
|
||||||
from core.controllers.ConfigurationController import ConfigurationController
|
from core.controllers.ConfigurationController import ConfigurationController
|
||||||
from essentials.observers.ConnectionObserver import ConnectionObserver
|
from essentials.observers.ConnectionObserver import ConnectionObserver
|
||||||
from core.observers.ClientObserver import ClientObserver
|
from core.observers.ClientObserver import ClientObserver
|
||||||
|
from gui.v2.actions.database_health import GuiStorageDatabaseError, validate_storage_database, has_required_sync_data
|
||||||
|
|
||||||
# generic
|
# generic
|
||||||
import os
|
import os
|
||||||
|
|
@ -121,12 +122,14 @@ if not assets_folder_setup():
|
||||||
# INITIALIZE DATABASE
|
# INITIALIZE DATABASE
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
lock_file = Path(f"{Constants.HV_DATA_HOME}/deleted_db.lock")
|
lock_file = Path(f"{Constants.HV_DATA_HOME}/deleted_db.lock")
|
||||||
|
system_path = get_path()
|
||||||
|
database_path = system_path / "storage.db"
|
||||||
|
main_db_exists = False
|
||||||
logger.info("[DB MANAGEMENT] Starting DB init..")
|
logger.info("[DB MANAGEMENT] Starting DB init..")
|
||||||
try:
|
try:
|
||||||
# Does the database exist?
|
|
||||||
system_path = get_path()
|
|
||||||
database_path = system_path / "storage.db"
|
|
||||||
main_db_exists = does_it_exist(database_path)
|
main_db_exists = does_it_exist(database_path)
|
||||||
|
if main_db_exists:
|
||||||
|
validate_storage_database(database_path)
|
||||||
|
|
||||||
# Setup operations on the main DB which create it
|
# Setup operations on the main DB which create it
|
||||||
init_session() # (engine, Session, _session all initialized from session_management)
|
init_session() # (engine, Session, _session all initialized from session_management)
|
||||||
|
|
@ -134,9 +137,14 @@ try:
|
||||||
|
|
||||||
# does the version checker table exist?
|
# does the version checker table exist?
|
||||||
version_table_exists = does_db_version_table_exist()
|
version_table_exists = does_db_version_table_exist()
|
||||||
except:
|
except GuiStorageDatabaseError as error:
|
||||||
logger.error("Critical Error with database initialization!")
|
logger.error(f"[DB MANAGEMENT] Critical storage database error during initialization: {error}")
|
||||||
sys.exit()
|
close_session()
|
||||||
|
recovery_dialog(str(error), "Unreadable")
|
||||||
|
except Exception as error:
|
||||||
|
logger.error(f"[DB MANAGEMENT] Critical Error with database initialization: {error}")
|
||||||
|
close_session()
|
||||||
|
recovery_dialog("HydraVeil could not initialize the local storage database. Please move or reset storage.db, then restart.", "Startup Error")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# LEGACY DATABASE CHECKS
|
# LEGACY DATABASE CHECKS
|
||||||
|
|
@ -248,6 +256,14 @@ did_it_insert = insert_new_version(session, Constants.DB_VERSION_THIS_APP_WANTS)
|
||||||
if not did_it_insert:
|
if not did_it_insert:
|
||||||
logger.error(f"[DB MANAGEMENT] Critical Failure with updating/inserting the new DB version into the database.")
|
logger.error(f"[DB MANAGEMENT] Critical Failure with updating/inserting the new DB version into the database.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
sync_data_missing = not has_required_sync_data(database_path)
|
||||||
|
except GuiStorageDatabaseError as error:
|
||||||
|
logger.error(f"[DB MANAGEMENT] Critical storage database error during synced data check: {error}")
|
||||||
|
close_session()
|
||||||
|
recovery_dialog(str(error), "Unreadable")
|
||||||
|
sync_data_missing = True
|
||||||
|
|
||||||
# Load GUI either way:
|
# Load GUI either way:
|
||||||
from gui.main_ui import start_ui
|
from gui.main_ui import start_ui
|
||||||
|
|
||||||
|
|
@ -256,7 +272,10 @@ if migration_happened:
|
||||||
force_sync = True
|
force_sync = True
|
||||||
clear_sync_cache()
|
clear_sync_cache()
|
||||||
else:
|
else:
|
||||||
force_sync = False
|
force_sync = sync_data_missing
|
||||||
|
|
||||||
|
if sync_data_missing:
|
||||||
|
logger.info("[DB MANAGEMENT] Required synced data is missing. GUI will prompt for sync.")
|
||||||
|
|
||||||
# if we deleted their DB, we want to force sync,
|
# if we deleted their DB, we want to force sync,
|
||||||
if lock_file.exists():
|
if lock_file.exists():
|
||||||
|
|
@ -266,5 +285,3 @@ if lock_file.exists():
|
||||||
# Start GUI either way:
|
# Start GUI either way:
|
||||||
logger.info(f"[DB MANAGEMENT] Starting GUI..")
|
logger.info(f"[DB MANAGEMENT] Starting GUI..")
|
||||||
start_ui(force_sync)
|
start_ui(force_sync)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ from gui.v2.actions.profile_data import (
|
||||||
write_profile_data,
|
write_profile_data,
|
||||||
clear_profile_data,
|
clear_profile_data,
|
||||||
)
|
)
|
||||||
|
from gui.v2.actions.database_health import GuiStorageDatabaseError
|
||||||
from gui.v2.actions.ticket_failure import (
|
from gui.v2.actions.ticket_failure import (
|
||||||
save_ticket_verification_failure,
|
save_ticket_verification_failure,
|
||||||
get_ticket_verification_failure,
|
get_ticket_verification_failure,
|
||||||
|
|
@ -303,6 +304,18 @@ class CustomWindow(QMainWindow):
|
||||||
if issubclass(identifier, UnknownConnectionTypeError):
|
if issubclass(identifier, UnknownConnectionTypeError):
|
||||||
self.setup_popup()
|
self.setup_popup()
|
||||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||||
|
elif issubclass(identifier, GuiStorageDatabaseError):
|
||||||
|
core_logger.error(
|
||||||
|
f"Storage database error:\n"
|
||||||
|
f"Type: {identifier.__name__}\n"
|
||||||
|
f"Value: {str(message)}\n"
|
||||||
|
f"Traceback:\n{''.join(traceback.format_tb(trace))}"
|
||||||
|
)
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
"Database Error",
|
||||||
|
"The local storage database could not be read. Restart HydraVeil and use the database recovery options.",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
config = self._load_gui_config()
|
config = self._load_gui_config()
|
||||||
if config and config["logging"]["gui_logging_enabled"] == True:
|
if config and config["logging"]["gui_logging_enabled"] == True:
|
||||||
|
|
|
||||||
89
gui/v2/actions/database_health.py
Executable file
89
gui/v2/actions/database_health.py
Executable file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class GuiStorageDatabaseError(Exception):
|
||||||
|
def __init__(self, message, detail=None):
|
||||||
|
self.detail = detail
|
||||||
|
super().__init__(f"{message} Details: {detail}" if detail else message)
|
||||||
|
|
||||||
|
|
||||||
|
def _connect_readonly(database_path):
|
||||||
|
path = Path(database_path).resolve()
|
||||||
|
return sqlite3.connect(f"{path.as_uri()}?mode=ro", uri=True)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_storage_database(database_path):
|
||||||
|
path = Path(database_path)
|
||||||
|
if not path.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
connection = None
|
||||||
|
try:
|
||||||
|
connection = _connect_readonly(path)
|
||||||
|
connection.execute("PRAGMA schema_version").fetchone()
|
||||||
|
result = connection.execute("PRAGMA quick_check").fetchone()
|
||||||
|
if result and result[0] != "ok":
|
||||||
|
raise GuiStorageDatabaseError(
|
||||||
|
"The local storage database failed SQLite integrity checks.",
|
||||||
|
str(result[0]),
|
||||||
|
)
|
||||||
|
table_rows = connection.execute("SELECT name FROM sqlite_master WHERE type = 'table'").fetchall()
|
||||||
|
table_names = {row[0] for row in table_rows}
|
||||||
|
known_tables = {
|
||||||
|
"applications",
|
||||||
|
"application_versions",
|
||||||
|
"cached_sync",
|
||||||
|
"client_versions",
|
||||||
|
"database_version",
|
||||||
|
"encryptedproxies",
|
||||||
|
"locations",
|
||||||
|
"operators",
|
||||||
|
"subscription_plans",
|
||||||
|
}
|
||||||
|
if table_names and table_names.isdisjoint(known_tables):
|
||||||
|
raise GuiStorageDatabaseError(
|
||||||
|
"The local storage database does not look like a HydraVeil storage database.",
|
||||||
|
", ".join(sorted(table_names)),
|
||||||
|
)
|
||||||
|
except GuiStorageDatabaseError:
|
||||||
|
raise
|
||||||
|
except sqlite3.Error as error:
|
||||||
|
raise GuiStorageDatabaseError(
|
||||||
|
"The local storage database could not be read. It may be malformed, stale, or incompatible.",
|
||||||
|
str(error),
|
||||||
|
) from error
|
||||||
|
finally:
|
||||||
|
if connection is not None:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _table_has_rows(database_path, table_name):
|
||||||
|
path = Path(database_path)
|
||||||
|
if not path.exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
connection = None
|
||||||
|
try:
|
||||||
|
connection = _connect_readonly(path)
|
||||||
|
return connection.execute(f'SELECT 1 FROM "{table_name}" LIMIT 1').fetchone() is not None
|
||||||
|
except sqlite3.OperationalError as error:
|
||||||
|
if "no such table" in str(error).lower():
|
||||||
|
return False
|
||||||
|
raise GuiStorageDatabaseError(
|
||||||
|
"The local storage database could not be checked for synced data.",
|
||||||
|
str(error),
|
||||||
|
) from error
|
||||||
|
except sqlite3.Error as error:
|
||||||
|
raise GuiStorageDatabaseError(
|
||||||
|
"The local storage database could not be checked for synced data.",
|
||||||
|
str(error),
|
||||||
|
) from error
|
||||||
|
finally:
|
||||||
|
if connection is not None:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
def has_required_sync_data(database_path):
|
||||||
|
required_tables = ("applications", "application_versions", "locations")
|
||||||
|
return all(_table_has_rows(database_path, table_name) for table_name in required_tables)
|
||||||
|
|
@ -53,16 +53,23 @@ def read_bwrap_enabled():
|
||||||
|
|
||||||
|
|
||||||
def prepare(gui_config_file):
|
def prepare(gui_config_file):
|
||||||
profiles = ProfileController.get_all()
|
try:
|
||||||
|
profiles = ProfileController.get_all()
|
||||||
|
except Exception:
|
||||||
|
profiles = {}
|
||||||
profile_order = normalize_profile_order(gui_config_file, profiles.keys())
|
profile_order = normalize_profile_order(gui_config_file, profiles.keys())
|
||||||
try:
|
try:
|
||||||
endpoint_verification_enabled = ConfigurationController.get_endpoint_verification_enabled()
|
endpoint_verification_enabled = ConfigurationController.get_endpoint_verification_enabled()
|
||||||
except Exception:
|
except Exception:
|
||||||
endpoint_verification_enabled = False
|
endpoint_verification_enabled = False
|
||||||
|
try:
|
||||||
|
current_connection = ConfigurationController.get_connection()
|
||||||
|
except Exception:
|
||||||
|
current_connection = None
|
||||||
return {
|
return {
|
||||||
"profiles": profiles,
|
"profiles": profiles,
|
||||||
"profile_order": profile_order,
|
"profile_order": profile_order,
|
||||||
"current_connection": ConfigurationController.get_connection(),
|
"current_connection": current_connection,
|
||||||
"endpoint_verification_enabled": endpoint_verification_enabled,
|
"endpoint_verification_enabled": endpoint_verification_enabled,
|
||||||
"systemwide_enabled": read_systemwide_enabled(),
|
"systemwide_enabled": read_systemwide_enabled(),
|
||||||
"bwrap_enabled": read_bwrap_enabled(),
|
"bwrap_enabled": read_bwrap_enabled(),
|
||||||
|
|
@ -115,8 +122,9 @@ def truncate_key(text, max_length=50):
|
||||||
|
|
||||||
def build_verification_view(profile):
|
def build_verification_view(profile):
|
||||||
operator = None
|
operator = None
|
||||||
if profile and getattr(profile, 'location', None) and profile.location.operator:
|
location = getattr(profile, 'location', None) if profile else None
|
||||||
operator = profile.location.operator
|
if location and not isinstance(location, dict) and getattr(location, 'operator', None):
|
||||||
|
operator = location.operator
|
||||||
if not operator:
|
if not operator:
|
||||||
return {
|
return {
|
||||||
"operator_name": "N/A",
|
"operator_name": "N/A",
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,10 @@ class ConnectionManager:
|
||||||
self.profile_button_objects[profile_id] = profile_button_objects
|
self.profile_button_objects[profile_id] = profile_button_objects
|
||||||
|
|
||||||
def get_available_resolutions(self, profile_id):
|
def get_available_resolutions(self, profile_id):
|
||||||
profile = ProfileController.get(profile_id)
|
try:
|
||||||
|
profile = ProfileController.get(profile_id)
|
||||||
|
except Exception:
|
||||||
|
profile = None
|
||||||
if profile and hasattr(profile, 'resolution') and profile.resolution:
|
if profile and hasattr(profile, 'resolution') and profile.resolution:
|
||||||
self.available_resolutions.append(profile.resolution)
|
self.available_resolutions.append(profile.resolution)
|
||||||
return self.available_resolutions
|
return self.available_resolutions
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ class BrowserPage(Page):
|
||||||
self.title.setText("Pick a Browser")
|
self.title.setText("Pick a Browser")
|
||||||
self.button_back.setVisible(True)
|
self.button_back.setVisible(True)
|
||||||
|
|
||||||
cm = main_window.connection_manager
|
self.connection_manager = main_window.connection_manager
|
||||||
if cm.is_synced():
|
if self.connection_manager.is_synced():
|
||||||
from gui.v2.actions.sync import generate_grid_positions
|
from gui.v2.actions.sync import generate_grid_positions
|
||||||
browsers = cm.get_browser_list()
|
browsers = self.connection_manager.get_browser_list()
|
||||||
positions = generate_grid_positions(len(browsers))
|
positions = generate_grid_positions(len(browsers))
|
||||||
available = [(QPushButton, brw, positions[i]) for i, brw in enumerate(browsers)]
|
available = [(QPushButton, brw, positions[i]) for i, brw in enumerate(browsers)]
|
||||||
self.create_interface_elements(available)
|
self.create_interface_elements(available)
|
||||||
|
|
@ -239,3 +239,21 @@ class BrowserPage(Page):
|
||||||
|
|
||||||
def gestionar_next(self):
|
def gestionar_next(self):
|
||||||
self.custom_window.navigator.navigate("screen")
|
self.custom_window.navigator.navigate("screen")
|
||||||
|
|
||||||
|
def gestionar_back(self):
|
||||||
|
profile_data = self.update_status.read_data()
|
||||||
|
protocol = profile_data.get("protocol", "")
|
||||||
|
|
||||||
|
if not self.connection_manager.is_synced() or not self.connection_manager.get_browser_list():
|
||||||
|
self.custom_window.navigator.navigate("menu")
|
||||||
|
return
|
||||||
|
|
||||||
|
if protocol == "wireguard":
|
||||||
|
self.custom_window.navigator.navigate("location")
|
||||||
|
return
|
||||||
|
|
||||||
|
if protocol in ("hidetor", "residential"):
|
||||||
|
self.custom_window.navigator.navigate("hidetor")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.custom_window.navigator.navigate("menu")
|
||||||
|
|
|
||||||
|
|
@ -359,14 +359,25 @@ class EditorPage(Page):
|
||||||
if browser_version == '':
|
if browser_version == '':
|
||||||
browser_version = data_profile.get('browser_version', '')
|
browser_version = data_profile.get('browser_version', '')
|
||||||
browser_value = f"{browser_type} {browser_version}"
|
browser_value = f"{browser_type} {browser_version}"
|
||||||
if connection == 'system-wide' or not browser_value.strip():
|
fallback_path = os.path.join(
|
||||||
|
self.btn_path, "default_browser_button.png")
|
||||||
|
normalized_browser = browser_value.strip().lower()
|
||||||
|
unknown_browser = (
|
||||||
|
not normalized_browser
|
||||||
|
or normalized_browser == "unknown"
|
||||||
|
or normalized_browser.startswith("unknown browser")
|
||||||
|
)
|
||||||
|
if connection == 'system-wide':
|
||||||
base_image = QPixmap()
|
base_image = QPixmap()
|
||||||
|
elif unknown_browser:
|
||||||
|
base_image = QPixmap(fallback_path)
|
||||||
|
if base_image.isNull():
|
||||||
|
base_image = BrowserPage.create_browser_button_image(
|
||||||
|
"Browser", fallback_path, True)
|
||||||
else:
|
else:
|
||||||
base_image = BrowserPage.create_browser_button_image(
|
base_image = BrowserPage.create_browser_button_image(
|
||||||
browser_value, self.btn_path)
|
browser_value, self.btn_path)
|
||||||
if base_image.isNull():
|
if base_image.isNull():
|
||||||
fallback_path = os.path.join(
|
|
||||||
self.btn_path, "default_browser_button.png")
|
|
||||||
base_image = BrowserPage.create_browser_button_image(
|
base_image = BrowserPage.create_browser_button_image(
|
||||||
browser_value, fallback_path, True)
|
browser_value, fallback_path, True)
|
||||||
elif key == 'location':
|
elif key == 'location':
|
||||||
|
|
@ -593,8 +604,14 @@ class EditorPage(Page):
|
||||||
self.on_sync_complete_for_edit_profile)
|
self.on_sync_complete_for_edit_profile)
|
||||||
return
|
return
|
||||||
|
|
||||||
previous_index = (index - 1) % len(parameters[key])
|
values = parameters.get(key, [])
|
||||||
previous_value = parameters[key][previous_index]
|
if not values:
|
||||||
|
self.update_status.update_status(
|
||||||
|
f"No {key} data available. Sync the database and try again.")
|
||||||
|
return
|
||||||
|
|
||||||
|
previous_index = (index - 1) % len(values)
|
||||||
|
previous_value = values[previous_index]
|
||||||
self.update_temp_value(key, previous_value)
|
self.update_temp_value(key, previous_value)
|
||||||
|
|
||||||
def show_next_value(self, key: str, index: int, parameters: dict) -> None:
|
def show_next_value(self, key: str, index: int, parameters: dict) -> None:
|
||||||
|
|
@ -606,8 +623,14 @@ class EditorPage(Page):
|
||||||
self.on_sync_complete_for_edit_profile)
|
self.on_sync_complete_for_edit_profile)
|
||||||
return
|
return
|
||||||
|
|
||||||
next_index = (index + 1) % len(parameters[key])
|
values = parameters.get(key, [])
|
||||||
next_value = parameters[key][next_index]
|
if not values:
|
||||||
|
self.update_status.update_status(
|
||||||
|
f"No {key} data available. Sync the database and try again.")
|
||||||
|
return
|
||||||
|
|
||||||
|
next_index = (index + 1) % len(values)
|
||||||
|
next_value = values[next_index]
|
||||||
|
|
||||||
self.update_temp_value(key, next_value)
|
self.update_temp_value(key, next_value)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ from PyQt6.QtWidgets import (
|
||||||
|
|
||||||
from core.controllers.ConfigurationController import ConfigurationController
|
from core.controllers.ConfigurationController import ConfigurationController
|
||||||
from core.controllers.ProfileController import ProfileController
|
from core.controllers.ProfileController import ProfileController
|
||||||
|
from core.errors.logger import logger
|
||||||
from core.controllers.tickets.UseTicketController import (
|
from core.controllers.tickets.UseTicketController import (
|
||||||
do_we_use_a_random_ticket,
|
do_we_use_a_random_ticket,
|
||||||
get_unused_tickets,
|
get_unused_tickets,
|
||||||
|
|
@ -20,6 +21,7 @@ from core.models.session.SessionProfile import SessionProfile
|
||||||
from core.models.system.SystemProfile import SystemProfile
|
from core.models.system.SystemProfile import SystemProfile
|
||||||
|
|
||||||
from gui.v2.infrastructure.setup_observers import ticket_observer
|
from gui.v2.infrastructure.setup_observers import ticket_observer
|
||||||
|
from gui.v2.actions.database_health import GuiStorageDatabaseError
|
||||||
from gui.v2.actions.profile_order import normalize_profile_order
|
from gui.v2.actions.profile_order import normalize_profile_order
|
||||||
from gui.v2.ui.pages.Page import Page
|
from gui.v2.ui.pages.Page import Page
|
||||||
from gui.v2.ui.pages.location_verification_page import LocationVerificationPage
|
from gui.v2.ui.pages.location_verification_page import LocationVerificationPage
|
||||||
|
|
@ -205,6 +207,88 @@ class MenuPage(Page):
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _safe_profiles(self):
|
||||||
|
try:
|
||||||
|
return ProfileController.get_all()
|
||||||
|
except GuiStorageDatabaseError as error:
|
||||||
|
logger.error(f"[MENU] Storage database error while loading profiles: {error}")
|
||||||
|
self.update_status.update_status("Local storage database could not be read. Restart and recover storage.db.")
|
||||||
|
except Exception as error:
|
||||||
|
logger.error(f"[MENU] Error while loading profiles: {error}")
|
||||||
|
self.update_status.update_status("Could not load profiles. Sync or restart and try again.")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _safe_profile(self, profile_id):
|
||||||
|
try:
|
||||||
|
return ProfileController.get(int(profile_id))
|
||||||
|
except GuiStorageDatabaseError as error:
|
||||||
|
logger.error(f"[MENU] Storage database error while loading profile {profile_id}: {error}")
|
||||||
|
self.update_status.update_status("Local storage database could not be read. Restart and recover storage.db.")
|
||||||
|
except Exception as error:
|
||||||
|
logger.error(f"[MENU] Error while loading profile {profile_id}: {error}")
|
||||||
|
self.update_status.update_status("Could not load profile data. Sync or restart and try again.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _connection_code(self, profile):
|
||||||
|
connection = getattr(profile, 'connection', None)
|
||||||
|
return getattr(connection, 'code', None) or 'unknown'
|
||||||
|
|
||||||
|
def _location_key(self, profile):
|
||||||
|
location = getattr(profile, 'location', None)
|
||||||
|
if isinstance(location, dict):
|
||||||
|
country_code = location.get("country_code") or "na"
|
||||||
|
code = location.get("code") or "na"
|
||||||
|
return f'{country_code}_{code}'
|
||||||
|
|
||||||
|
country_code = getattr(location, 'country_code', None)
|
||||||
|
code = getattr(location, 'code', None)
|
||||||
|
if country_code and code:
|
||||||
|
return f'{country_code}_{code}'
|
||||||
|
|
||||||
|
return 'na_na'
|
||||||
|
|
||||||
|
def _browser_info(self, profile):
|
||||||
|
if not isinstance(profile, SessionProfile):
|
||||||
|
return "unknown browser", "", False
|
||||||
|
|
||||||
|
application_version = getattr(profile, 'application_version', None)
|
||||||
|
if isinstance(application_version, dict):
|
||||||
|
browser = application_version.get('application_code') or "unknown browser"
|
||||||
|
version = application_version.get('version_number') or ""
|
||||||
|
return browser, version, False
|
||||||
|
|
||||||
|
browser = getattr(application_version, 'application_code', None) or "unknown browser"
|
||||||
|
version = getattr(application_version, 'version_number', None) or ""
|
||||||
|
supported = bool(getattr(application_version, 'supported', False))
|
||||||
|
return browser, version, supported
|
||||||
|
|
||||||
|
def _profile_incomplete_reason(self, profile):
|
||||||
|
if profile is None:
|
||||||
|
return "Profile data could not be loaded. Sync or restart and try again."
|
||||||
|
|
||||||
|
connection = getattr(profile, 'connection', None)
|
||||||
|
if not getattr(connection, 'code', None):
|
||||||
|
return "Profile connection data is incomplete. Sync the database and try again."
|
||||||
|
|
||||||
|
location = getattr(profile, 'location', None)
|
||||||
|
if location is None or isinstance(location, dict):
|
||||||
|
return "Profile location data is incomplete. Sync the database and try again."
|
||||||
|
|
||||||
|
if isinstance(profile, SessionProfile):
|
||||||
|
application_version = getattr(profile, 'application_version', None)
|
||||||
|
if isinstance(application_version, dict) or application_version is None:
|
||||||
|
return "Profile browser data is incomplete. Sync the database and try again."
|
||||||
|
if not getattr(application_version, 'application_code', None) or not getattr(application_version, 'version_number', None):
|
||||||
|
return "Profile browser data is incomplete. Sync the database and try again."
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _location_operator(self, profile):
|
||||||
|
location = getattr(profile, 'location', None)
|
||||||
|
if isinstance(location, dict) or location is None:
|
||||||
|
return None
|
||||||
|
return getattr(location, 'operator', None)
|
||||||
|
|
||||||
def match_core_profiles(self, profiles_dict):
|
def match_core_profiles(self, profiles_dict):
|
||||||
new_dict = {}
|
new_dict = {}
|
||||||
|
|
||||||
|
|
@ -213,26 +297,13 @@ class MenuPage(Page):
|
||||||
profiles_dict.keys())
|
profiles_dict.keys())
|
||||||
|
|
||||||
for idx in profile_ids:
|
for idx in profile_ids:
|
||||||
try:
|
profile = profiles_dict.get(idx)
|
||||||
profile = profiles_dict[idx]
|
if profile is None:
|
||||||
new_profile = {}
|
continue
|
||||||
|
|
||||||
protocol = profile.connection.code
|
new_profile = {}
|
||||||
|
protocol = self._connection_code(profile)
|
||||||
# print(f"DEBUG: the type is = {type(profile.location)}, value = {profile.location}")
|
location = self._location_key(profile)
|
||||||
|
|
||||||
if isinstance(profile.location, dict):
|
|
||||||
# Fake/missing data — show placeholders from data,
|
|
||||||
country_code = profile.location.get("country_code", "na")
|
|
||||||
code = profile.location.get("code", "na")
|
|
||||||
location = f'{country_code}_{code}'
|
|
||||||
else:
|
|
||||||
# Proper Location object
|
|
||||||
location = f'{profile.location.country_code}_{profile.location.code}'
|
|
||||||
except:
|
|
||||||
import sys
|
|
||||||
print(f"idx: {idx}")
|
|
||||||
sys.exit("STOPPING TO READ PRINTS FOR IDX. this failed on getting the location data inside match_core_profiles")
|
|
||||||
|
|
||||||
new_profile['location'] = location
|
new_profile['location'] = location
|
||||||
|
|
||||||
|
|
@ -251,23 +322,17 @@ class MenuPage(Page):
|
||||||
else:
|
else:
|
||||||
new_profile['connection'] = 'just proxy'
|
new_profile['connection'] = 'just proxy'
|
||||||
|
|
||||||
if isinstance(profile, SessionProfile):
|
browser, browser_version, browser_supported = self._browser_info(profile)
|
||||||
browser = profile.application_version.application_code
|
new_profile['browser'] = browser
|
||||||
else:
|
new_profile['browser_version'] = browser_version
|
||||||
browser = 'unknown'
|
new_profile['browser_supported'] = browser_supported
|
||||||
if browser != 'unknown':
|
|
||||||
new_profile['browser'] = browser
|
|
||||||
new_profile['browser_version'] = profile.application_version.version_number
|
|
||||||
new_profile['browser_supported'] = profile.application_version.supported
|
|
||||||
else:
|
|
||||||
new_profile['browser'] = 'unknown browser'
|
|
||||||
new_profile['browser_supported'] = False
|
|
||||||
|
|
||||||
resolution = profile.resolution if hasattr(
|
resolution = profile.resolution if hasattr(
|
||||||
profile, 'resolution') else 'None'
|
profile, 'resolution') else 'None'
|
||||||
new_profile['dimentions'] = resolution
|
new_profile['dimentions'] = resolution
|
||||||
|
|
||||||
new_profile['name'] = profile.name
|
new_profile['name'] = getattr(profile, 'name', None) or f'Profile {idx}'
|
||||||
|
new_profile['incomplete_reason'] = self._profile_incomplete_reason(profile)
|
||||||
|
|
||||||
|
|
||||||
new_dict[f'Profile_{idx}'] = new_profile
|
new_dict[f'Profile_{idx}'] = new_profile
|
||||||
|
|
@ -281,7 +346,7 @@ class MenuPage(Page):
|
||||||
if hasattr(self, 'verification_button'):
|
if hasattr(self, 'verification_button'):
|
||||||
self.verification_button.setEnabled(False)
|
self.verification_button.setEnabled(False)
|
||||||
self.profiles_data = self.match_core_profiles(
|
self.profiles_data = self.match_core_profiles(
|
||||||
ProfileController.get_all())
|
self._safe_profiles())
|
||||||
|
|
||||||
self.number_of_profiles = len(self.profiles_data)
|
self.number_of_profiles = len(self.profiles_data)
|
||||||
self.profile_info = dict(self.profiles_data)
|
self.profile_info = dict(self.profiles_data)
|
||||||
|
|
@ -296,7 +361,7 @@ class MenuPage(Page):
|
||||||
|
|
||||||
def refresh_profiles_data(self):
|
def refresh_profiles_data(self):
|
||||||
self.profiles_data = self.match_core_profiles(
|
self.profiles_data = self.match_core_profiles(
|
||||||
ProfileController.get_all())
|
self._safe_profiles())
|
||||||
self.number_of_profiles = len(self.profiles_data)
|
self.number_of_profiles = len(self.profiles_data)
|
||||||
self.profile_info = dict(self.profiles_data)
|
self.profile_info = dict(self.profiles_data)
|
||||||
for profile_name, profile_value in self.profiles_data.items():
|
for profile_name, profile_value in self.profiles_data.items():
|
||||||
|
|
@ -304,12 +369,16 @@ class MenuPage(Page):
|
||||||
self.update_scroll_widget_size()
|
self.update_scroll_widget_size()
|
||||||
|
|
||||||
def refresh_menu_buttons(self):
|
def refresh_menu_buttons(self):
|
||||||
profiles = ProfileController.get_all()
|
profiles = self._safe_profiles()
|
||||||
self.button_states.clear()
|
self.button_states.clear()
|
||||||
self.connection_manager._connected_profiles.clear()
|
self.connection_manager._connected_profiles.clear()
|
||||||
self.IsSystem = 0
|
self.IsSystem = 0
|
||||||
for profile_id, profile in profiles.items():
|
for profile_id, profile in profiles.items():
|
||||||
if ProfileController.is_enabled(profile):
|
try:
|
||||||
|
is_enabled = ProfileController.is_enabled(profile)
|
||||||
|
except Exception:
|
||||||
|
is_enabled = False
|
||||||
|
if is_enabled:
|
||||||
self.connection_manager.add_connected_profile(profile_id)
|
self.connection_manager.add_connected_profile(profile_id)
|
||||||
if isinstance(profile, SessionProfile):
|
if isinstance(profile, SessionProfile):
|
||||||
if profile.connection and profile.connection.code == 'tor':
|
if profile.connection and profile.connection.code == 'tor':
|
||||||
|
|
@ -424,7 +493,7 @@ class MenuPage(Page):
|
||||||
def create_profile_name_label(self, parent_label, name):
|
def create_profile_name_label(self, parent_label, name):
|
||||||
child_label = QLabel(parent_label)
|
child_label = QLabel(parent_label)
|
||||||
child_label.setGeometry(0, 65, 175, 30)
|
child_label.setGeometry(0, 65, 175, 30)
|
||||||
child_label.setText(name)
|
child_label.setText(str(name or "Unnamed Profile"))
|
||||||
child_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
child_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
child_label.setAttribute(
|
child_label.setAttribute(
|
||||||
Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||||
|
|
@ -537,9 +606,7 @@ class MenuPage(Page):
|
||||||
verification_layout.setContentsMargins(20, 20, 20, 20)
|
verification_layout.setContentsMargins(20, 20, 20, 20)
|
||||||
verification_layout.setSpacing(15)
|
verification_layout.setSpacing(15)
|
||||||
|
|
||||||
operator = None
|
operator = self._location_operator(profile_obj)
|
||||||
if profile_obj.location and profile_obj.location.operator:
|
|
||||||
operator = profile_obj.location.operator
|
|
||||||
|
|
||||||
info_items = [
|
info_items = [
|
||||||
("Operator Name", "operator_name"),
|
("Operator Name", "operator_name"),
|
||||||
|
|
@ -696,14 +763,18 @@ class MenuPage(Page):
|
||||||
connection = profile.get("connection", "")
|
connection = profile.get("connection", "")
|
||||||
country_garaje = profile.get("country_garaje", "")
|
country_garaje = profile.get("country_garaje", "")
|
||||||
|
|
||||||
profile_obj = ProfileController.get(self.reverse_id)
|
profile_obj = self._safe_profile(self.reverse_id)
|
||||||
is_profile_enabled = self.connection_manager.is_profile_connected(
|
is_profile_enabled = self.connection_manager.is_profile_connected(
|
||||||
self.reverse_id)
|
self.reverse_id)
|
||||||
show_verification_widget = False
|
show_verification_widget = False
|
||||||
label_principal = None
|
label_principal = None
|
||||||
label_tor = None
|
label_tor = None
|
||||||
text_color = "white"
|
text_color = "white"
|
||||||
operator_name = profile_obj.location.operator.name if profile_obj.location and profile_obj.location.operator else ""
|
profile_location = getattr(profile_obj, 'location', None)
|
||||||
|
if isinstance(profile_location, dict):
|
||||||
|
profile_location = None
|
||||||
|
profile_operator = self._location_operator(profile_obj)
|
||||||
|
operator_name = getattr(profile_operator, 'name', '') if profile_operator else ""
|
||||||
|
|
||||||
if protocol.lower() == "wireguard" and is_profile_enabled and profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard':
|
if protocol.lower() == "wireguard" and is_profile_enabled and profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard':
|
||||||
verification_widget = self.create_verification_widget(
|
verification_widget = self.create_verification_widget(
|
||||||
|
|
@ -713,7 +784,7 @@ class MenuPage(Page):
|
||||||
label_principal = None
|
label_principal = None
|
||||||
|
|
||||||
elif operator_name != 'Simplified Privacy' and protocol.lower() == "wireguard":
|
elif operator_name != 'Simplified Privacy' and protocol.lower() == "wireguard":
|
||||||
if profile_obj.is_session_profile():
|
if isinstance(profile_obj, SessionProfile):
|
||||||
text_color = "black"
|
text_color = "black"
|
||||||
label_background = QLabel(self)
|
label_background = QLabel(self)
|
||||||
label_background.setGeometry(0, 60, 410, 354)
|
label_background.setGeometry(0, 60, 410, 354)
|
||||||
|
|
@ -733,9 +804,9 @@ class MenuPage(Page):
|
||||||
l_img.setScaledContents(True)
|
l_img.setScaledContents(True)
|
||||||
l_img.show()
|
l_img.show()
|
||||||
self.additional_labels.append(l_img)
|
self.additional_labels.append(l_img)
|
||||||
l_name = f"{profile_obj.location.country_name}, {profile_obj.location.name}" if profile_obj.location else ""
|
l_name = f"{profile_location.country_name}, {profile_location.name}" if profile_location else ""
|
||||||
o_name = profile_obj.location.operator.name if profile_obj.location and profile_obj.location.operator else ""
|
o_name = getattr(profile_operator, 'name', '') if profile_operator else ""
|
||||||
n_key = profile_obj.location.operator.nostr_public_key if profile_obj.location and profile_obj.location.operator else ""
|
n_key = getattr(profile_operator, 'nostr_public_key', '') if profile_operator else ""
|
||||||
|
|
||||||
info_txt = QTextEdit(self)
|
info_txt = QTextEdit(self)
|
||||||
info_txt.setGeometry(130, 110, 260, 250)
|
info_txt.setGeometry(130, 110, 260, 250)
|
||||||
|
|
@ -884,11 +955,12 @@ class MenuPage(Page):
|
||||||
self.current_profile_location = location
|
self.current_profile_location = location
|
||||||
|
|
||||||
def show_profile_verification(self, profile_id):
|
def show_profile_verification(self, profile_id):
|
||||||
profile = ProfileController.get(profile_id)
|
profile = self._safe_profile(profile_id)
|
||||||
if not profile or not profile.location:
|
profile_location = getattr(profile, 'location', None)
|
||||||
|
if not profile or not profile_location or isinstance(profile_location, dict):
|
||||||
return
|
return
|
||||||
|
|
||||||
location_key = f'{profile.location.country_code}_{profile.location.code}'
|
location_key = f'{profile_location.country_code}_{profile_location.code}'
|
||||||
location_info = self.connection_manager.get_location_info(location_key)
|
location_info = self.connection_manager.get_location_info(location_key)
|
||||||
if not location_info:
|
if not location_info:
|
||||||
return
|
return
|
||||||
|
|
@ -919,9 +991,22 @@ class MenuPage(Page):
|
||||||
self.boton_just.setEnabled(False)
|
self.boton_just.setEnabled(False)
|
||||||
self.boton_just_session.setEnabled(False)
|
self.boton_just_session.setEnabled(False)
|
||||||
|
|
||||||
profile = ProfileController.get(int(self.reverse_id))
|
profile = self._safe_profile(int(self.reverse_id))
|
||||||
is_tor = self.update_status.get_current_connection() == 'tor'
|
incomplete_reason = self._profile_incomplete_reason(profile)
|
||||||
if profile.connection.code == 'tor' and not profile.application_version.installed and not is_tor:
|
if incomplete_reason:
|
||||||
|
self.update_status.update_status(incomplete_reason)
|
||||||
|
self.boton_just.setEnabled(True)
|
||||||
|
self.boton_just_session.setEnabled(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
is_tor = self.update_status.get_current_connection() == 'tor'
|
||||||
|
except Exception:
|
||||||
|
is_tor = False
|
||||||
|
|
||||||
|
connection = getattr(profile, 'connection', None)
|
||||||
|
application_version = getattr(profile, 'application_version', None)
|
||||||
|
if getattr(connection, 'code', None) == 'tor' and not getattr(application_version, 'installed', False) and not is_tor:
|
||||||
message = f'You are using a Tor profile, but the associated browser is not installed. If you want the browser to be downloaded with Tor, you must switch to a Tor connection. Otherwise, proceed with a clearweb connection.'
|
message = f'You are using a Tor profile, but the associated browser is not installed. If you want the browser to be downloaded with Tor, you must switch to a Tor connection. Otherwise, proceed with a clearweb connection.'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -973,7 +1058,12 @@ class MenuPage(Page):
|
||||||
profile_data = {
|
profile_data = {
|
||||||
'id': int(self.reverse_id)
|
'id': int(self.reverse_id)
|
||||||
}
|
}
|
||||||
profile = ProfileController.get(int(self.reverse_id))
|
profile = self._safe_profile(int(self.reverse_id))
|
||||||
|
if profile is None:
|
||||||
|
self.update_status.update_status("Could not load profile data. Sync or restart and try again.")
|
||||||
|
self.disconnect_button.setEnabled(True)
|
||||||
|
self.disconnect_system_wide_button.setEnabled(True)
|
||||||
|
return
|
||||||
is_session_profile = isinstance(profile, SessionProfile)
|
is_session_profile = isinstance(profile, SessionProfile)
|
||||||
if not is_session_profile:
|
if not is_session_profile:
|
||||||
connected_profiles = self.connection_manager.get_connected_profiles()
|
connected_profiles = self.connection_manager.get_connected_profiles()
|
||||||
|
|
@ -1030,7 +1120,7 @@ class MenuPage(Page):
|
||||||
'Sync failed. Please try again later.')
|
'Sync failed. Please try again later.')
|
||||||
|
|
||||||
def change_connect_button(self):
|
def change_connect_button(self):
|
||||||
profile = ProfileController.get(int(self.reverse_id))
|
profile = self._safe_profile(int(self.reverse_id))
|
||||||
is_connected = self.connection_manager.is_profile_connected(
|
is_connected = self.connection_manager.is_profile_connected(
|
||||||
int(self.reverse_id))
|
int(self.reverse_id))
|
||||||
is_session_profile = isinstance(profile, SessionProfile)
|
is_session_profile = isinstance(profile, SessionProfile)
|
||||||
|
|
@ -1047,6 +1137,10 @@ class MenuPage(Page):
|
||||||
is_connected and not is_session_profile)
|
is_connected and not is_session_profile)
|
||||||
|
|
||||||
def update_status_message(self, profile, is_connected):
|
def update_status_message(self, profile, is_connected):
|
||||||
|
if profile is None:
|
||||||
|
self.update_status.update_status("Profile data could not be loaded. Sync or restart and try again.")
|
||||||
|
return
|
||||||
|
|
||||||
if is_connected:
|
if is_connected:
|
||||||
message = Worker.generate_profile_message(profile, is_enabled=True)
|
message = Worker.generate_profile_message(profile, is_enabled=True)
|
||||||
self.update_status.enable_marquee(message)
|
self.update_status.enable_marquee(message)
|
||||||
|
|
@ -1214,9 +1308,15 @@ class MenuPage(Page):
|
||||||
def change_app_page(self, text, Is_changed):
|
def change_app_page(self, text, Is_changed):
|
||||||
self.update_status.update_status(str(text))
|
self.update_status.update_status(str(text))
|
||||||
if Is_changed:
|
if Is_changed:
|
||||||
current_connection = self.update_status.get_current_connection()
|
try:
|
||||||
profile = ProfileController.get(int(self.reverse_id))
|
current_connection = self.update_status.get_current_connection()
|
||||||
if current_connection != 'tor' and profile.connection.code == 'tor':
|
except Exception:
|
||||||
|
current_connection = None
|
||||||
|
profile = self._safe_profile(int(self.reverse_id))
|
||||||
|
incomplete_reason = self._profile_incomplete_reason(profile)
|
||||||
|
if incomplete_reason:
|
||||||
|
self.update_status.update_status(incomplete_reason)
|
||||||
|
elif current_connection != 'tor' and getattr(getattr(profile, 'connection', None), 'code', None) == 'tor':
|
||||||
message = f'You are using a Tor profile, but the profile subscription is missing or expired. If you want the billing to be done thourgh Tor, you must switch to a Tor connection. Otherwise, proceed with a clearweb connection.'
|
message = f'You are using a Tor profile, but the profile subscription is missing or expired. If you want the billing to be done thourgh Tor, you must switch to a Tor connection. Otherwise, proceed with a clearweb connection.'
|
||||||
self.popup = self._make_confirmation_popup(message, button_text='Proceed')
|
self.popup = self._make_confirmation_popup(message, button_text='Proceed')
|
||||||
self.popup.finished.connect(
|
self.popup.finished.connect(
|
||||||
|
|
@ -1261,7 +1361,7 @@ class MenuPage(Page):
|
||||||
self.boton_edit.setEnabled(False)
|
self.boton_edit.setEnabled(False)
|
||||||
|
|
||||||
if profile_id == self.reverse_id:
|
if profile_id == self.reverse_id:
|
||||||
profile_obj = ProfileController.get(profile_id)
|
profile_obj = self._safe_profile(profile_id)
|
||||||
if profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard' and ConfigurationController.get_endpoint_verification_enabled():
|
if profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard' and ConfigurationController.get_endpoint_verification_enabled():
|
||||||
self.print_profile_details(f"Profile_{profile_id}")
|
self.print_profile_details(f"Profile_{profile_id}")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
0
gui/v2/ui/popups/generic_choice.py
Normal file → Executable file
0
gui/v2/ui/popups/generic_choice.py
Normal file → Executable file
0
gui/v2/ui/popups/terminal_threading.py
Normal file → Executable file
0
gui/v2/ui/popups/terminal_threading.py
Normal file → Executable file
|
|
@ -25,6 +25,7 @@ from core.Errors import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from gui.v2.actions.locations import location_candidates
|
from gui.v2.actions.locations import location_candidates
|
||||||
|
from gui.v2.actions.database_health import GuiStorageDatabaseError
|
||||||
from gui.v2.infrastructure.setup_observers import (
|
from gui.v2.infrastructure.setup_observers import (
|
||||||
application_version_observer,
|
application_version_observer,
|
||||||
connection_observer,
|
connection_observer,
|
||||||
|
|
@ -50,7 +51,22 @@ class Worker(QObject):
|
||||||
self._consumed_ticket = None
|
self._consumed_ticket = None
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.profile = ProfileController.get(int(self.profile_data['id']))
|
try:
|
||||||
|
self.profile = ProfileController.get(int(self.profile_data['id']))
|
||||||
|
except GuiStorageDatabaseError:
|
||||||
|
self.update_signal.emit(
|
||||||
|
"Local storage database could not be read. Restart and recover storage.db.", False, None, None, None)
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
self.update_signal.emit(
|
||||||
|
"Could not load profile data. Sync or restart and try again.", False, None, None, None)
|
||||||
|
return
|
||||||
|
|
||||||
|
incomplete_reason = self._profile_incomplete_reason()
|
||||||
|
if incomplete_reason:
|
||||||
|
self.update_signal.emit(incomplete_reason, False, None, None, None)
|
||||||
|
return
|
||||||
|
|
||||||
if 'use_ticket' in self.profile_data:
|
if 'use_ticket' in self.profile_data:
|
||||||
ticket_billing_code = self._consume_ticket(
|
ticket_billing_code = self._consume_ticket(
|
||||||
self.profile_data['use_ticket'],
|
self.profile_data['use_ticket'],
|
||||||
|
|
@ -131,6 +147,27 @@ class Worker(QObject):
|
||||||
self.update_signal.emit(
|
self.update_signal.emit(
|
||||||
f"No profile found with ID: {self.profile_data['id']}", False, None, None, None)
|
f"No profile found with ID: {self.profile_data['id']}", False, None, None, None)
|
||||||
|
|
||||||
|
def _profile_incomplete_reason(self):
|
||||||
|
if self.profile is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
connection = getattr(self.profile, 'connection', None)
|
||||||
|
if not getattr(connection, 'code', None):
|
||||||
|
return "Profile connection data is incomplete. Sync the database and try again."
|
||||||
|
|
||||||
|
location = getattr(self.profile, 'location', None)
|
||||||
|
if location is None or isinstance(location, dict):
|
||||||
|
return "Profile location data is incomplete. Sync the database and try again."
|
||||||
|
|
||||||
|
if isinstance(self.profile, SessionProfile):
|
||||||
|
application_version = getattr(self.profile, 'application_version', None)
|
||||||
|
if isinstance(application_version, dict) or application_version is None:
|
||||||
|
return "Profile browser data is incomplete. Sync the database and try again."
|
||||||
|
if not getattr(application_version, 'application_code', None) or not getattr(application_version, 'version_number', None):
|
||||||
|
return "Profile browser data is incomplete. Sync the database and try again."
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def _location_candidates(self, preferred=None):
|
def _location_candidates(self, preferred=None):
|
||||||
return location_candidates(self.profile, preferred)
|
return location_candidates(self.profile, preferred)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,12 +168,19 @@ class WorkerThread(QThread):
|
||||||
self.finished.emit(False)
|
self.finished.emit(False)
|
||||||
|
|
||||||
def list_profiles(self):
|
def list_profiles(self):
|
||||||
profiles = ProfileController.get_all()
|
try:
|
||||||
|
profiles = ProfileController.get_all()
|
||||||
|
except Exception:
|
||||||
|
profiles = {}
|
||||||
|
self.text_output.emit("Could not load profiles. Sync or restart and try again.")
|
||||||
self.profiles_output.emit(profiles)
|
self.profiles_output.emit(profiles)
|
||||||
|
|
||||||
def create_profile(self, profile_type):
|
def create_profile(self, profile_type):
|
||||||
location = LocationController.get(
|
try:
|
||||||
self.profile_data['country_code'], self.profile_data['code'])
|
location = LocationController.get(
|
||||||
|
self.profile_data['country_code'], self.profile_data['code'])
|
||||||
|
except Exception:
|
||||||
|
location = None
|
||||||
if location is None:
|
if location is None:
|
||||||
self.text_output.emit(
|
self.text_output.emit(
|
||||||
f"Invalid location code: {self.profile_data['location_code']}")
|
f"Invalid location code: {self.profile_data['location_code']}")
|
||||||
|
|
@ -188,8 +195,11 @@ class WorkerThread(QThread):
|
||||||
|
|
||||||
application_details = self.profile_data['application'].split(
|
application_details = self.profile_data['application'].split(
|
||||||
':', 1)
|
':', 1)
|
||||||
application_version = ApplicationVersionController.get(
|
try:
|
||||||
application_details[0], application_details[1] if len(application_details) > 1 else None)
|
application_version = ApplicationVersionController.get(
|
||||||
|
application_details[0], application_details[1] if len(application_details) > 1 else None)
|
||||||
|
except Exception:
|
||||||
|
application_version = None
|
||||||
if application_version is None:
|
if application_version is None:
|
||||||
self.text_output.emit(
|
self.text_output.emit(
|
||||||
f"Invalid application: {self.profile_data['application']}")
|
f"Invalid application: {self.profile_data['application']}")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue