Improved Singbox Setup Error handling for edge cases

This commit is contained in:
SimplifiedPrivacy 2026-08-12 15:57:51 -04:00
parent 01bd5f8d3f
commit 951802fe5c
3 changed files with 29 additions and 22 deletions

View file

@ -34,6 +34,7 @@ class ResultError(Enum):
@dataclass @dataclass
class Result(): class Result():
valid: bool valid: bool
goal_result: Optional[bool] = None
error_type: ResultError = ResultError.SUCCESS error_type: ResultError = ResultError.SUCCESS
data: Optional[Any] = None data: Optional[Any] = None
message: Optional[str] = None message: Optional[str] = None

View file

@ -6,8 +6,6 @@ from core.Constants import Constants
from core.errors.logger import logger from core.errors.logger import logger
from core.utils.run_commands import run_generic_command from core.utils.run_commands import run_generic_command
from core.observers.ApplicationVersionObserver import ApplicationVersionObserver from core.observers.ApplicationVersionObserver import ApplicationVersionObserver
from core.Errors import FileIntegrityError
from core.services.networking.api_requests.subtools.extract_domain import extract_domain
from core.models.orm_models.Dependency import Dependency from core.models.orm_models.Dependency import Dependency
from core.models.orm_calls.dependency_calls import get_dependency_version from core.models.orm_calls.dependency_calls import get_dependency_version
from core.controllers.ConfigurationController import ConfigurationController from core.controllers.ConfigurationController import ConfigurationController
@ -17,13 +15,12 @@ import httpx
from io import BytesIO from io import BytesIO
from typing import Optional from typing import Optional
import hashlib import hashlib
import shutil
import tarfile import tarfile
import os import os
SUDO_SINGBOX_LOCATION = f"{Constants.SUDO_TARGET_FOLDER}/sing-box" SUDO_SINGBOX_LOCATION = f"{Constants.SUDO_TARGET_FOLDER}/sing-box"
def singbox_setup(application_version_observer: Optional[ApplicationVersionObserver]) -> Result: def setup_singbox_binary(application_version_observer: Optional[ApplicationVersionObserver]) -> Result:
""" """
Rank: Rank:
Module's Main Orchestrator Module's Main Orchestrator
@ -58,13 +55,18 @@ def singbox_setup(application_version_observer: Optional[ApplicationVersionObser
logger.info("Sync is required. We could not access the information on the most current version locally.") logger.info("Sync is required. We could not access the information on the most current version locally.")
return update_result # (user MUST sync) return update_result # (user MUST sync)
# this is blank if the checks worked, but nothing is required, if already_in_sudo_folder and update_result.goal_result:
# no update required:
no_update = "No update for singbox is required."
logger.info(no_update)
return Result(valid=True, message=no_update)
required_update = update_result.data required_update = update_result.data
# no update required: if required_update is None or not isinstance(required_update, Dependency):
if required_update is None and already_in_sudo_folder: error_msg = "You need to Sync, because you lack the dependency data for singbox."
logger.info("No update for singbox is required.") logger.error(error_msg)
return update_result return Result(valid=False, error_type=ResultError.NEED_SYNC, message=error_msg)
# UPDATE FROM HERE ON # UPDATE FROM HERE ON
logger.info("Updating Singbox..") logger.info("Updating Singbox..")
@ -168,13 +170,9 @@ def update_needed() -> Result:
Returns: Returns:
Always a Result Object. Always a Result Object.
No update needed = goal_result=True
** IMPORTANT** Update needed = goal_result=False
If an update is NOT needed, but it can complete the check, it returns valid=True, but None for Data. Can't complete the checks, valid=False
If an update IS needed, it returns data with the new version.
If it can't complete the checks, it returns False for valid.
""" """
app_name = "singbox" app_name = "singbox"
rejected_values = [None, False, ""] rejected_values = [None, False, ""]
@ -200,7 +198,7 @@ def update_needed() -> Result:
logger.info(f"While the version of {app_name} we installed in our configuration is {version_installed}") logger.info(f"While the version of {app_name} we installed in our configuration is {version_installed}")
if version_installed in rejected_values: if version_installed in rejected_values:
logger.info(f"We need to update to the new {new_version}") logger.info(f"We need to update to the new {new_version}")
return Result(valid=True, data=version_sql_query) return Result(valid=True, goal_result=False, data=version_sql_query)
################################################ ################################################
# EVALUATE # EVALUATE
@ -218,6 +216,8 @@ def update_needed() -> Result:
logger.error(f"The version_installed is in the wrong format: {str(e)}. But we can still update to the new version..") logger.error(f"The version_installed is in the wrong format: {str(e)}. But we can still update to the new version..")
# wipe config: # wipe config:
changed_config = ConfigurationController.update_singbox_version(None) # can make this dynamic if more apps are added. changed_config = ConfigurationController.update_singbox_version(None) # can make this dynamic if more apps are added.
if not changed_config:
logger.error("Critical Issue with wiping the config version")
need_update = True # the version_sql_query data is still valid. need_update = True # the version_sql_query data is still valid.
else: else:
error_msg = f"Corrupt data, corrupt filesystem, or outright developer bug. Please contact customer support with new_version: {new_version} and version_installed {version_installed} tried to see if it should update but {str(e)}" error_msg = f"Corrupt data, corrupt filesystem, or outright developer bug. Please contact customer support with new_version: {new_version} and version_installed {version_installed} tried to see if it should update but {str(e)}"
@ -225,9 +225,9 @@ def update_needed() -> Result:
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
if need_update: if need_update:
return Result(valid=True, data=version_sql_query, message="update") return Result(valid=True, goal_result=True, data=version_sql_query, message="update")
else: else:
return Result(valid=True, data=None, message="Not needed.") # BLANK DATA, BUT TRUE return Result(valid=True, goal_result=False, data=version_sql_query, message="Not needed.")
def already_downloaded(which_version: str) -> bool: def already_downloaded(which_version: str) -> bool:
@ -292,9 +292,10 @@ def download_and_verify(
else: else:
error_msg = f"Could not download {target_app_name} because of a Connection Error." error_msg = f"Could not download {target_app_name} because of a Connection Error."
logger.error(error_msg) logger.error(error_msg)
return Return(valid=False, error_type=ResultError.CONNECTION, message=error_msg) return Result(valid=False, error_type=ResultError.CONNECTION, message=error_msg)
application_version_observer.notify('downloaded', f"Downloaded {target_app_name}") if application_version_observer is not None:
application_version_observer.notify('downloaded', f"Downloaded {target_app_name}")
response_buffer.seek(0) response_buffer.seek(0)
################################################ ################################################
@ -305,7 +306,7 @@ def download_and_verify(
if real_file_hash != target_file_hash: if real_file_hash != target_file_hash:
error_msg = f'Application version file integrity could not be verified. We are targeting {target_file_hash}, but got {real_file_hash}' error_msg = f'Application version file integrity could not be verified. We are targeting {target_file_hash}, but got {real_file_hash}'
logger.error(error_msg) logger.error(error_msg)
return Return(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
################################################ ################################################
# SAVE IT IN CORRECT STRUCTURE # SAVE IT IN CORRECT STRUCTURE

View file

@ -1,5 +1,6 @@
from core.services.helpers.manage_assets import sudo_assets_folder_setup from core.services.helpers.manage_assets import sudo_assets_folder_setup
from core.utils.basic_operations.confirm_files_exist import confirm_files_and_folders_exist from core.utils.basic_operations.confirm_files_exist import confirm_files_and_folders_exist
from core.utils.basic_operations.does_file_exist import does_file_exist
from core.models.Result import Result, ResultError from core.models.Result import Result, ResultError
from core.Constants import Constants from core.Constants import Constants
from core.errors.logger import logger from core.errors.logger import logger
@ -68,3 +69,7 @@ def test_if_in_sudo_folder() -> Result:
logger.error(error_msg) logger.error(error_msg)
return Result(valid=False, message=error_msg) return Result(valid=False, message=error_msg)
def is_singbox_wrapper_ready() -> bool:
wrapper_location = f"{Constants.SUDO_TARGET_FOLDER}/singbox_wrapper"
return does_file_exist(wrapper_location)