Bug fixes and tweaks for transitions

This commit is contained in:
SimplifiedPrivacy 2026-09-21 12:35:37 -04:00
parent b29a44cf95
commit d35fc0db4b
10 changed files with 51 additions and 24 deletions

View file

@ -1,3 +0,0 @@
from core.utils.run_commands import test_broken_pipe_on_process_death
test_broken_pipe_on_process_death()

View file

@ -8,7 +8,7 @@ if TYPE_CHECKING:
from core.Constants import Constants
# from core.observers.BaseObserver import BaseObserver
from core.services.using_tickets.use_ticket_orchestrator import use_ticket_orchestrator
from core.models.Result import Result, ResultError
from core.services.prepare_tickets.ticket_tracker import does_ticket_tracker_exist
# from core.services.prepare_tickets import setup_ticket_tracker

View file

@ -52,7 +52,11 @@ def denormalize(data, mapping_file):
extract_from_nested - This function checks it's actually a dictionary and extracts it
"""
value = extract_from_nested(each_item, field['path'])
# If nested path didn't work, try using the field name as a flat key
if value is None:
value = each_item.get(field['name'])
# Store the extracted value. If it wasn't there, then it's None
each_denormalized_piece[field['name']] = value

View file

@ -8,7 +8,7 @@ def switch_endpoint_domain(domain: str) -> dict:
"operators": f"https://{domain}/api/v1/operators",
"client_version": f"https://{domain}/api/v1/platforms/linux-x86_64/appimage/client-versions",
"subscriptions": f"https://{domain}/api/v1/subscription-plans",
"applications": f"https://{domain}/api/v1/platforms/linux-x86_64/applications",
# "applications": f"https://{domain}/api/v1/platforms/linux-x86_64/applications",
"application_versions": f"https://{domain}/api/v1/platforms/linux-x86_64/application-versions",
"dependencies": f"https://{domain}/api/v1/dependencies"
}

View file

@ -161,8 +161,12 @@ def orchestrate_closing(current_state: SystemState, interface: str) -> Result:
for strategy in strategies:
result = _try_with_permission_fallback(strategy, interface)
if result.valid:
# remove state json:
SystemState.dissolve()
return result
if not interface_tools.exists(interface):
# remove state json:
SystemState.dissolve()
return Result(valid=True, message="Tunnel inactive despite strategy failure")
return Result(valid=False, message=f"All {quantity_of_strategies} strategies tried, we simply can not bring down the proxy, which is still up.")

View file

@ -6,6 +6,8 @@ from core.services.networking.systemwide.encrypted_proxy.hysteria_config import
from core.services.networking.systemwide.encrypted_proxy.vless_config import build_vless_config, parse_vless_link
from core.utils.basic_operations.write_or_read_from_json import write_json_to_file
from core.utils.run_commands import run_generic_command
from core.utils.basic_operations.does_file_exist import does_file_exist
# Models
from core.models.pydantic_models.HysteriaData import HysteriaData
@ -191,7 +193,7 @@ def attach_config_to_sudo_folder(
human_readable_goal = f"Attempt {failed_attempt_count} to write to the sudo folder"
logger.info(human_readable_goal)
command = ['pkexec', 'install', '-D', backup_path, sudo_filepath, '-o', 'root', '-m', '744']
result_object = run_generic_command(command, human_readable_goal, timeout=5)
result_object = run_generic_command(command, human_readable_goal, timeout=40)
if result_object.valid:
configuration_is_attached = True
else:
@ -202,7 +204,8 @@ def attach_config_to_sudo_folder(
raise ProfileModificationError('The configuration could not be attached.')
return False # redundant
return True
# confirm file exists before we continue,
return does_file_exist(sudo_filepath)
def post_operator_proxy(billing_code: str, protocol: str, connection_observer: ConnectionObserver) -> dict:

View file

@ -297,15 +297,22 @@ def start_singbox(
if not ip_result.valid:
logger.error(f"IP address check is unable to connect. Killing Singbox pid {process_id}")
closed = orchestrate_closing(current_state=current_state, interface=Constants.SINGBOX_TUN_IF)
raise ConnectionError("Could not connect with IP adddress check.")
# return Result(valid=False, error_type=ResultError.CONNECTION, message="Could not connect with IP adddress check.")
if closed.valid:
logger.info("Closed singbox. Redundant state removal now taking place..")
# redundant state removal. This is a repeat of the close_Singbox doing it.
SystemState.dissolve()
# raise ConnectionError("Could not connect with IP adddress check.")
return Result(valid=False, error_type=ResultError.CONNECTION, message="Could not connect with IP adddress check.")
observed_ip_address = ip_result.data
if observed_ip_address == server_ip:
logger.info("Observed IP matches the intended server IP. Proceeding..")
if connection_observer:
connection_observer.notify("connecting", f"IP Matched! {server_ip}")
else:
logger.info(f"""
observed_ip_address: {observed_ip_address}
observed_ip_address type: {type(observed_ip_address)}
server_ip: {server_ip}
server_ip type: {type(server_ip)}
""")
if str(observed_ip_address) != str(server_ip):
error_msg = f"Connection didn't work, it's leaking! IP address check of {observed_ip_address} does NOT match the intended {server_ip}."
logger.error(f"{error_msg} Killing Singbox pid {process_id}")
closed = orchestrate_closing(current_state=current_state, interface=Constants.SINGBOX_TUN_IF)
@ -314,12 +321,17 @@ def start_singbox(
connection_observer.notify("connecting", error_for_user)
return Result(valid=False, error_type=ResultError.LEAK_ISSUE, message=error_msg)
logger.info("Observed IP matches the intended server IP. Proceeding..")
if connection_observer:
connection_observer.notify("connecting", f"IP Matched! {server_ip}")
# ============= FIREWALL =============
logger.info(f"Attempting to enable the Firewall for {Constants.SINGBOX_TUN_IF} and {Constants.SINGBOX_INTERNAL_SUBNET}...")
firewall_result = enable_firewall_w_retry( # this function is "generic" as it's protocol neutral
interface_name=Constants.SINGBOX_TUN_IF,
server_ip=server_ip,
internal_subnet = Constants.SINGBOX_INTERNAL_SUBNET,
interface_name=str(Constants.SINGBOX_TUN_IF),
server_ip=str(server_ip),
internal_subnet = (Constants.SINGBOX_INTERNAL_SUBNET),
max_retries = 2
)

View file

@ -101,6 +101,7 @@ def is_subscription_ready(profile: Union[SessionProfile, SystemProfile]) -> bool
def get_subscription(billing_code: str, connection_observer: ConnectionObserver) -> Subscription:
print(f"calling get_subscription with {billing_code}")
billing_code = billing_code.replace('-', '').upper()
billing_code_fragments = re.findall('....?', billing_code)
@ -126,6 +127,7 @@ def get_subscription(billing_code: str, connection_observer: ConnectionObserver)
def get_invoice(billing_code: str, connection_observer: ConnectionObserver) -> Invoice:
print(f"calling get_invoice to get an invoice")
url = f'{Constants.SP_API_BASE_URL}/invoices/current'

View file

@ -197,15 +197,15 @@ def coordinate_cache_sync(
# ================== VALIDATE DATA ==================
# Extract the data from our own trusted function without type checking,
new_data = api_result.data
original_data = api_result.data
if new_data:
if original_data:
logger.debug(f"We got an API response with some data, let's check if it's a valid format...")
# Validate the API's payload, (we don't trust the API's structure)
if not isinstance(new_data, dict):
error_msg = f"API returned invalid metadata of {type(new_data)}. When 'new_data' has to be in dictionary format. The raw data is {new_data}"
client_observer.notify('synchronizing', f'Server replied with invalid metadata of type {type(new_data)}')
if not isinstance(original_data, dict):
error_msg = f"API returned invalid metadata of {type(original_data)}. When 'original_data' has to be in dictionary format. The raw data is {original_data}"
client_observer.notify('synchronizing', f'Server replied with invalid metadata of type {type(original_data)}')
logger.error(error_msg)
return {
"success": False,
@ -214,8 +214,12 @@ def coordinate_cache_sync(
"error": error_msg,
}
# de-"data" it if it has it:
new_data = original_data.get('data', original_data)
print(f"new_data is {new_data}")
if "version" not in new_data:
error_msg = "API returned invalid metadata: missing 'version' key"
error_msg = f"API returned invalid metadata: missing 'version' key. The data is {new_data}"
logger.error(error_msg)
return {
"success": False,

View file

@ -94,6 +94,7 @@ def use_ticket_orchestrator(
# even if not saved return it back, so it can be saved with the profile by other functions,
return Result(valid=True, data=billing_code)
# return {"valid": True, "data": billing_code}
# EXPIRED
if not valid and message == "expired":