diff --git a/core/controllers/ClientController.py b/core/controllers/ClientController.py index 5e8289d..7be4a62 100644 --- a/core/controllers/ClientController.py +++ b/core/controllers/ClientController.py @@ -59,86 +59,86 @@ class ClientController: return not ClientVersionController.is_latest(version) - @staticmethod - def legacy_sync(client_observer: ClientObserver = None, connection_observer: ConnectionObserver = None): - if client_observer is not None: - client_observer.notify('synchronizing', "Fetching list of new data ..") + # @staticmethod + # def legacy_sync(client_observer: ClientObserver = None, connection_observer: ConnectionObserver = None): + # if client_observer is not None: + # client_observer.notify('synchronizing', "Fetching list of new data ..") - result = coordinate_cache_sync(client_observer, connection_observer) + # result = coordinate_cache_sync(client_observer, connection_observer) - # Outright Error: - if not result["success"]: - error_msg = result["error"] - if client_observer is not None: - client_observer.notify('synchronizing', f'Error! {error_msg}') - return + # # Outright Error: + # if not result["success"]: + # error_msg = result["error"] + # if client_observer is not None: + # client_observer.notify('synchronizing', f'Error! {error_msg}') + # return - # Same: - changed_tables = result["changed_tables"] - if not changed_tables: - if client_observer is not None: - client_observer.notify('synchronized') - return + # # Same: + # changed_tables = result["changed_tables"] + # if not changed_tables: + # if client_observer is not None: + # client_observer.notify('synchronized') + # return - # We only make it past this point if there's New Data + # # We only make it past this point if there's New Data - # flag for after the save, - data_was_saved = False + # # flag for after the save, + # data_was_saved = False - # Fetch and update the real data (no longer metadata)... + # # Fetch and update the real data (no longer metadata)... - # =================== ORM BASED MODELS ================== - """ - Note: for the new ORM based models, - it does the Tor/system check in the API call itself. - """ + # # =================== ORM BASED MODELS ================== + # """ + # Note: for the new ORM based models, + # it does the Tor/system check in the API call itself. + # """ - if "locations" in changed_tables: - logger.info("Sync of Locations") - if client_observer is not None: - client_observer.notify('synchronizing', 'Fetching Locations List..') + # if "locations" in changed_tables: + # logger.info("Sync of Locations") + # if client_observer is not None: + # client_observer.notify('synchronizing', 'Fetching Locations List..') - final_result = sync_one_orm_model(Location, "locations") - evaluate_errors(final_result) + # final_result = sync_one_orm_model(Location, "locations") + # evaluate_errors(final_result) - if "operators" in changed_tables: - logger.info("Sync of Operators") - if client_observer is not None: - client_observer.notify('synchronizing', 'Fetching Operators List..') + # if "operators" in changed_tables: + # logger.info("Sync of Operators") + # if client_observer is not None: + # client_observer.notify('synchronizing', 'Fetching Operators List..') - final_result_two = sync_one_orm_model(Operator, "operators") - evaluate_errors(final_result_two) + # final_result_two = sync_one_orm_model(Operator, "operators") + # evaluate_errors(final_result_two) - # =================== MANUAL-SQL BASED MODELS ================== - try: - from core.controllers.ConnectionController import ConnectionController - ConnectionController.with_preferred_connection(task=ClientController.__sync, changed_tables=changed_tables, client_observer=client_observer, connection_observer=connection_observer) + # # =================== MANUAL-SQL BASED MODELS ================== + # try: + # from core.controllers.ConnectionController import ConnectionController + # ConnectionController.with_preferred_connection(task=ClientController.__sync, changed_tables=changed_tables, client_observer=client_observer, connection_observer=connection_observer) - # We set the flag to true, - # the reason we use a flag, and don't just save it right here, - # is because we want to isolate the success (or failure) of the real data, - # from the potential failure of the ORM session metadata. - data_was_saved = True + # # We set the flag to true, + # # the reason we use a flag, and don't just save it right here, + # # is because we want to isolate the success (or failure) of the real data, + # # from the potential failure of the ORM session metadata. + # data_was_saved = True - except: - # sync failed here, - if client_observer is not None: - client_observer.notify('synchronizing', 'Fetch Failed, but you can use old data.') - finally: - if data_was_saved: - filtered_metadata = result["filtered_metadata"] # from the top of the function - save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service + # except: + # # sync failed here, + # if client_observer is not None: + # client_observer.notify('synchronizing', 'Fetch Failed, but you can use old data.') + # finally: + # if data_was_saved: + # filtered_metadata = result["filtered_metadata"] # from the top of the function + # save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service - if client_observer is None: - logger.error("Error: No client_observer to update the UI, the final part of the sync function skipped") - return # can't update their UI + # if client_observer is None: + # logger.error("Error: No client_observer to update the UI, the final part of the sync function skipped") + # return # can't update their UI - if save_successful: - logger.info("Metadata Saved Successfully") - client_observer.notify('synchronized', "Fetch & Save Complete!") - else: - client_observer.notify('synchronizing', "Saving List of Metadata Failed.") + # if save_successful: + # logger.info("Metadata Saved Successfully") + # client_observer.notify('synchronized', "Fetch & Save Complete!") + # else: + # client_observer.notify('synchronizing', "Saving List of Metadata Failed.") @staticmethod diff --git a/core/controllers/ConnectionController.py b/core/controllers/ConnectionController.py index 300d311..149c843 100644 --- a/core/controllers/ConnectionController.py +++ b/core/controllers/ConnectionController.py @@ -136,12 +136,10 @@ class ConnectionController: try: tor_module = TorModule(Constants.HV_TOR_STATE_HOME) tor_module.create_session(port_number, connection_observer) - except TorServiceInitializationError as e: - logger.error(f"TorServiceInitializationError. Tor Can't Start: {e}") - if connection_observer is not None: - connection_observer.notify('custom_message', "Tor Can't Initialize") except Exception as e: logger.error(f"Tor Can't Start: {e}") + if connection_observer is not None: + connection_observer.notify('custom_message', "Tor Can't Initialize") @staticmethod def terminate_tor_session_connection(port_number: int): diff --git a/core/controllers/SyncController.py b/core/controllers/SyncController.py index 56838f8..63ae333 100644 --- a/core/controllers/SyncController.py +++ b/core/controllers/SyncController.py @@ -165,12 +165,19 @@ def new_sync(client_observer: ClientObserver, connection_observer: ConnectionObs if total_skipped == 0: client_observer.notify('synchronized', "Fetch & Save Complete!") - save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service - return Result(valid=True, message="Finshed sync.") + save_successful = save_metadata(filtered_metadata) + if save_successful: + return Result(valid=True, message="Finshed sync.") + else: + error_msg = "Finshed sync, but had issues with the saving of metadata for next time." + logger.error(error_msg) + return Result(valid=True, message=error_msg) + elif total_skipped < quantity_of_entries: error_msg = f"Partial Success. {total_skipped} skipped." client_observer.notify('synchronized', error_msg) return Result(valid=True, data=skipped, message=error_msg) + else: error_msg = f"Sync Failed. All {total_skipped} entries were skipped!" client_observer.notify('synchronized', error_msg) diff --git a/core/controllers/tickets/TicketPayController.py b/core/controllers/tickets/TicketPayController.py index 05423aa..d1088ed 100644 --- a/core/controllers/tickets/TicketPayController.py +++ b/core/controllers/tickets/TicketPayController.py @@ -11,7 +11,8 @@ from core.observers.BaseObserver import BaseObserver from core.services.payment_phase.save_and_send_intitial_billing import save_and_send_intitial_billing from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType from core.services.networking.api_requests.step5_solve_api_problems import solve_api_problems - +from core.errors.logger import logger +from core.errors.exceptions import ServerSideError, NetworkingError from core.services.prepare_tickets.ticket_tracker import does_ticket_tracker_exist @@ -132,7 +133,7 @@ def initiate_payment( return invoice_data_object - except InvalidData as e: + except ValueError as e: error_msg = "Invalid Data." ticket_observer.notify("failed_input", subject=error_msg) invoice_data_object.add_error_code("invalid_data") diff --git a/core/controllers/tickets/UseTicketController.py b/core/controllers/tickets/UseTicketController.py index 0341528..37510f9 100644 --- a/core/controllers/tickets/UseTicketController.py +++ b/core/controllers/tickets/UseTicketController.py @@ -19,6 +19,7 @@ from core.services.helpers.get_value_from_config import get_value_from_config from core.utils.basic_operations.does_file_exist import does_file_exist from core.utils.basic_operations.write_or_read_from_json import update_json from core.services.prepare_tickets.ticket_tracker import get_all_unused_tickets +from core.errors.logger import logger import random @@ -162,8 +163,10 @@ def use_ticket( error_msg = f"Ticket is already tied to {location} with the subscription {subscription}" ticket_observer.notify("failed_input", subject=error_msg) return {"valid": False, "message": error_msg} - except: + except ValueError as e: error_msg = f"Your local ticket tracker has no value for ticket {which_ticket}" + logger.error(error_msg) + logger.error(str(e)) return {"valid": False, "message": error_msg} # the actual work here, everything else is just handling: diff --git a/core/models/Result.py b/core/models/Result.py index 5c3ca13..3178e27 100644 --- a/core/models/Result.py +++ b/core/models/Result.py @@ -37,7 +37,7 @@ class Result(): def user_message(self) -> str: """Human-readable error for the UI.""" messages = { - DBErrorType.SUCCESS: "Operation completed successfully.", - DBErrorType.UNKNOWN: f"Error: {self.message}", + ResultError.SUCCESS: "Operation completed successfully.", + ResultError.UNKNOWN: f"Error: {self.message}", } return messages.get(self.error_type, "Unknown error") diff --git a/core/services/WebServiceApiService.py b/core/services/WebServiceApiService.py index 3cfd0a0..57217f0 100644 --- a/core/services/WebServiceApiService.py +++ b/core/services/WebServiceApiService.py @@ -1,7 +1,7 @@ from core.Constants import Constants from core.models.ClientVersion import ClientVersion -# from core.models.Location import Location -# from core.models.Operator import Operator +from core.models.orm_models.Location import Location +from core.models.orm_models.Operator import Operator from core.models.Subscription import Subscription from core.models.SubscriptionPlan import SubscriptionPlan from core.models.invoice.Invoice import Invoice diff --git a/core/services/failed_verification/is_the_key_to_blame.py b/core/services/failed_verification/is_the_key_to_blame.py index 7c0a2b8..58639b5 100644 --- a/core/services/failed_verification/is_the_key_to_blame.py +++ b/core/services/failed_verification/is_the_key_to_blame.py @@ -13,6 +13,8 @@ from core.services.failed_verification.test_if_new_key_works import test_if_new_ from core.services.networking.make_url import make_url from core.services.networking.api_requests.step1_get_or_post import get_data_from_api +from core.services.networking.httpx import connect +from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType # utils from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file @@ -72,12 +74,21 @@ def get_new_pubkey_from_api(connection_observer: ConnectionObserver) -> dict | N url = make_url(which_key_plan) # the result of this is a python dictionary with single ' - api_results = get_data_from_server(url, None, connection_observer) + # api_results = get_data_from_server(url, None, connection_observer) + api_results = connect.single_endpoint( + method="get", + url=url, + observer=connection_observer, + payload=None + ) - if "data" in api_results: - new_public_key = api_results["data"] + if api_results.valid: + new_public_key = api_results.data + return new_public_key + else: + logger.error(f"API Results returned were invalid for that key.") + return None - return new_public_key def are_keys_different(old_public_key, new_public_key) -> bool: diff --git a/core/services/networking/api_requests/step2_execute.py b/core/services/networking/api_requests/step2_execute.py index 6ca5e1a..24eabd5 100644 --- a/core/services/networking/api_requests/step2_execute.py +++ b/core/services/networking/api_requests/step2_execute.py @@ -24,7 +24,7 @@ from core.errors.logger import logger # generic import json, os -from typing import Any +from typing import Any, Optional import time diff --git a/core/services/networking/api_requests/subtools/direct_dns_tools.py b/core/services/networking/api_requests/subtools/direct_dns_tools.py index 0c79723..cd8891a 100644 --- a/core/services/networking/api_requests/subtools/direct_dns_tools.py +++ b/core/services/networking/api_requests/subtools/direct_dns_tools.py @@ -174,6 +174,7 @@ def quad9_proxy_dns_lookup( domain: str, custom_proxy: str, timeout: int = 10, + client_observer: ClientObserver = None ) -> str: logger.debug("Doing a Proxy Quad9 DNS lookup") diff --git a/core/services/networking/general_connection_tools/connection_enable.py b/core/services/networking/general_connection_tools/connection_enable.py index 3449202..51e094f 100644 --- a/core/services/networking/general_connection_tools/connection_enable.py +++ b/core/services/networking/general_connection_tools/connection_enable.py @@ -11,10 +11,11 @@ from core.services.subscriptions.subscriptions import activate_subscription from core.errors.logger import logger from core.errors.exceptions import * +from core.errors.exceptions import FirewallError from typing import Union, Optional, Callable from core.models.session.SessionProfile import SessionProfile from core.models.system.SystemProfile import SystemProfile -from core.Errors import ConnectionTerminationError, InvalidSubscriptionError +from core.Errors import ConnectionTerminationError, InvalidSubscriptionError, MissingSubscriptionError from core.services.WebServiceApiService import WebServiceApiService from core.controllers.ConnectionController import ConnectionController from core.models.BaseProfile import ProfileType diff --git a/core/services/networking/httpx/async_request.py b/core/services/networking/httpx/async_request.py index fd63b11..a9df43a 100644 --- a/core/services/networking/httpx/async_request.py +++ b/core/services/networking/httpx/async_request.py @@ -182,4 +182,4 @@ async def switch_get_and_post(method: str, url: str, client: httpx.Client, paylo else: # nevermind, logger.info("Our strategy of switching GET/POST did NOT work.") - return initial_result + return second_result diff --git a/core/services/networking/httpx/connect.py b/core/services/networking/httpx/connect.py index 1e2ef1f..ddc3ea7 100644 --- a/core/services/networking/httpx/connect.py +++ b/core/services/networking/httpx/connect.py @@ -34,9 +34,7 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload Purpose: Try a request to a single endpoint using either an existing client, or creating a new one, then error handling. """ - - global _port_used - + connection_type = ConfigurationController.get_connection_enum() client = httpx_client.get_http_session() @@ -62,9 +60,6 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload ######################################################## # FROM THIS POINT ON, WE HAVE A CLIENT ######################################################## - # #### SPOOF - # _port_used = 9050 - # initial_result = ApiResponse(valid=False, error_type=ErrorType.DNS_TEMPORARY) initial_result = make_request( method=method, @@ -367,3 +362,7 @@ def custom_dns_resolver_for_SINGLE_ENDPOINT(method: str, url: str, observer: Con # """Merge two dictionaries. dict1 values take precedence on key collision.""" # return {**dict2, **dict1} +def replace_http_with_https(url): + if url.startswith("http://"): + return url.replace("http://", "https://", 1) + return url diff --git a/core/services/networking/httpx/make_request.py b/core/services/networking/httpx/make_request.py index fd37e11..6695c6d 100644 --- a/core/services/networking/httpx/make_request.py +++ b/core/services/networking/httpx/make_request.py @@ -176,4 +176,4 @@ def switch_get_and_post(method: str, url: str, client: httpx.Client, payload: di else: # nevermind, logger.info("Our strategy of switching GET/POST did NOT work.") - return initial_result + return second_result diff --git a/core/services/networking/legacy/get_data_from_server.py b/core/services/networking/legacy/get_data_from_server.py index 6b781ab..2a1992f 100644 --- a/core/services/networking/legacy/get_data_from_server.py +++ b/core/services/networking/legacy/get_data_from_server.py @@ -16,6 +16,7 @@ from core.services.networking.regular_get_request import regular_get_request from core.errors.exceptions import * from core.errors.logger import logger import traceback +from typing import Optional # Generic GET request to an endpoint, filtered by the user's preference of connection type (Tor or Not) diff --git a/core/services/networking/systemwide/dns_tools/process_dns_result.py b/core/services/networking/systemwide/dns_tools/process_dns_result.py index 176b4cb..2d3f357 100644 --- a/core/services/networking/systemwide/dns_tools/process_dns_result.py +++ b/core/services/networking/systemwide/dns_tools/process_dns_result.py @@ -84,13 +84,13 @@ def orchestrate_dns_check(target_interface: str) -> Result: else: logger.info(f"[DNS Orchestrator] Some of the DNS checks did NOT work. But this might be because the tunnel is down.") - targets_not_found = 0 + target_not_found = 0 for each_object in list_of_results_as_objects: if each_object.error_type == SearchError.TARGET_MISSING: target_not_found = target_not_found + 1 - return Result(valid=False, data=targets_not_found) + return Result(valid=False, data=target_not_found) diff --git a/core/services/networking/systemwide/encrypted_proxy/singbox.py b/core/services/networking/systemwide/encrypted_proxy/singbox.py index 4d91656..ad64147 100644 --- a/core/services/networking/systemwide/encrypted_proxy/singbox.py +++ b/core/services/networking/systemwide/encrypted_proxy/singbox.py @@ -1,6 +1,6 @@ from core.services.networking.systemwide.systemwide_errors import systemwide_hell_raiser from core.utils.basic_operations.wrap_with import wrap_with -from core.utils.run_commands import run_generic_command import run_generic_command +from core.utils.run_commands import run_generic_command from core.models.Result import Result, ResultError from core.errors.logger import logger diff --git a/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py b/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py index 27784da..0188898 100644 --- a/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py +++ b/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py @@ -10,6 +10,8 @@ from core.services.networking.systemwide.systemwide_utils import get_firewall_se from core.services.networking.systemwide import dns from core.services.networking.systemwide.general_connection_tools.general_firewall_dns_tools import generic_enable_firewall_w_retry from core.errors.exceptions import FirewallError, DNSError +from essentials.observers.ConnectionObserver import ConnectionObserver + # generic import subprocess @@ -106,7 +108,7 @@ def start_singbox( if not activation_result.valid: error_msg = f"Singbox failed to start after {QUANTITY_OF_ATTEMPTS} attempts" logger.error(f"[{function_name}] {error_msg}") - return Result(valid=False, error_type=ResultError=PROCESS_WONT_START, message=error_msg) + return Result(valid=False, error_type=ResultError.PROCESS_WONT_START, message=error_msg) process_id = activation_result.data time.sleep(2) @@ -129,7 +131,7 @@ def start_singbox( ) # ============= FIREWALL ============= - logger.info(f"[{function_name}] Attempting to enable the Firewall for {SINGBOX_TUN_IF} and {Constants.SINGBOX_INTERNAL_SUBNET}...") + logger.info(f"[{function_name}] Attempting to enable the Firewall for {Constants.SINGBOX_TUN_IF} and {Constants.SINGBOX_INTERNAL_SUBNET}...") # this is labeled "generic" for being protocol neutral firewall_result = generic_enable_firewall_w_retry( interface_name=Constants.SINGBOX_TUN_IF, diff --git a/core/services/networking/systemwide/systemwide_wireguard.py b/core/services/networking/systemwide/systemwide_wireguard.py index d934432..549f64f 100644 --- a/core/services/networking/systemwide/systemwide_wireguard.py +++ b/core/services/networking/systemwide/systemwide_wireguard.py @@ -230,6 +230,7 @@ def _establish_connection_with_retry( except ConnectionError as e: # Unrecoverable error, fail immediately + logger.error(str(e)) raise except CalledProcessError as e: diff --git a/core/services/networking/systemwide/wireguard/nmcli_tools.py b/core/services/networking/systemwide/wireguard/nmcli_tools.py index 0ceebc6..abd1a9d 100644 --- a/core/services/networking/systemwide/wireguard/nmcli_tools.py +++ b/core/services/networking/systemwide/wireguard/nmcli_tools.py @@ -1,5 +1,7 @@ from core.utils.run_commands import run_generic_command from core.models.Result import Result, ResultError +from core.errors.logger import logger + from subprocess import CalledProcessError import os import re diff --git a/core/services/networking/tor_tools/bootstrap.py b/core/services/networking/tor_tools/bootstrap.py index 0832804..ad953f4 100644 --- a/core/services/networking/tor_tools/bootstrap.py +++ b/core/services/networking/tor_tools/bootstrap.py @@ -1,4 +1,5 @@ from core.services.networking.tor_tools.monitor_bootstrap import monitor_bootstrap_progress +from core.services.networking.tor_tools.install_tor import install_tor from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType from core.services.networking.tor_tools import ports from core.Constants import Constants @@ -89,7 +90,7 @@ def bootstrap( current_port = ports.get_random_available_port() use_new_folder = True elif strategy == BootstrapStrategy.REINSTALL_AND_RETRY: - if not install_tor().valid: + if not install_tor(observer).valid: return ApiResponse(valid=False, error_type=ErrorType.INSTALL_FAILED) use_new_folder = True diff --git a/core/services/networking/tor_tools/install_tor.py b/core/services/networking/tor_tools/install_tor.py index 46fd388..ec7021a 100644 --- a/core/services/networking/tor_tools/install_tor.py +++ b/core/services/networking/tor_tools/install_tor.py @@ -27,7 +27,7 @@ def get_distro_package_manager() -> Optional[str]: return None -async def install_tor() -> ApiResponse: +async def install_tor(observer: ConnectionObserver) -> ApiResponse: """ Prompt user to install Tor with pkexec, detecting distro for correct package manager. """ diff --git a/core/services/networking/tor_tools/ports.py b/core/services/networking/tor_tools/ports.py index e21abe2..647fa44 100644 --- a/core/services/networking/tor_tools/ports.py +++ b/core/services/networking/tor_tools/ports.py @@ -7,13 +7,13 @@ from core.Constants import Constants from typing import Optional import subprocess import asyncio -import subprocess import socket import httpx from httpx_socks import AsyncProxyTransport import json import re - +import os +import signal def is_port_in_use(port: int) -> bool: """ diff --git a/core/services/networking/tor_tools/pre_bootstrap.py b/core/services/networking/tor_tools/pre_bootstrap.py index 0f49dc7..e564ded 100644 --- a/core/services/networking/tor_tools/pre_bootstrap.py +++ b/core/services/networking/tor_tools/pre_bootstrap.py @@ -71,7 +71,7 @@ def get_bootstrap_port(port_tried: int, observer: ConnectionObserver): def diagnose_tor_port(port_tried: int, observer: ConnectionObserver) -> ApiResponse: # Step 1) Is Tor Installed? if not is_installed('tor'): - installed = install_tor() + installed = install_tor(observer) if installed: return ApiResponse(valid=True, error_type=ErrorType.TOR_NOT_INSTALLED, port=Constants.DEFAULT_TOR_PORT) else: diff --git a/core/services/networking/tor_tools/tor_orchestrator.py b/core/services/networking/tor_tools/tor_orchestrator.py index fdd759c..17fcdbc 100644 --- a/core/services/networking/tor_tools/tor_orchestrator.py +++ b/core/services/networking/tor_tools/tor_orchestrator.py @@ -35,7 +35,7 @@ def establish_tor_connection(observer: ConnectionObserver) -> ApiResponse: # Step 2) Install Tor if needed. if result.error_type == ErrorType.TOR_NOT_INSTALLED: - installed = install_tor() + installed = install_tor(observer) if not installed: return ApiResponse(valid=False, error_type=ErrorType.REFUSAL_TO_INSTALL_TOR) diff --git a/core/services/payment_phase/save_and_send_intitial_billing.py b/core/services/payment_phase/save_and_send_intitial_billing.py index 2ccde47..358802a 100644 --- a/core/services/payment_phase/save_and_send_intitial_billing.py +++ b/core/services/payment_phase/save_and_send_intitial_billing.py @@ -11,6 +11,7 @@ from core.services.networking.api_requests.step1_get_or_post import send_data_to from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType from core.services.networking.api_requests.step5_solve_api_problems import solve_api_problems +from core.services.networking.httpx import connect from core.services.networking.make_url import make_url from core.services.payment_phase.extract_payment_details import extract_payment_details @@ -42,24 +43,35 @@ def save_and_send_intitial_billing( """ # Save choices: - save_billing_choices(payload) + saved_choices = save_billing_choices(payload) + if not saved_choices: + error_msg = "Unable to save billing choices locally. Check file permissions and storage space." + logger.error(error_msg) + return TicketInvoice(is_valid=False, final_error_msg=error_msg) # send them: which_endpoint = "start_payment" url = make_url(which_endpoint) - api_reply_object = send_data_to_server(payload, url, connection_observer) + # api_reply_object = send_data_to_server(payload, url, connection_observer) + api_reply_object = connect.single_endpoint( + method="post", + url=url, + observer=connection_observer, + payload=payload + ) + + # if not api_reply_object.valid: + # api_reply_object = solve_api_problems( + # api_reply_object=api_reply_object, + # get_or_post="post", + # url=url, + # payload=payload, + # connection_observer=connection_observer, + # client_observer=None + # ) if not api_reply_object.valid: - api_reply_object = solve_api_problems( - api_reply_object=api_reply_object, - get_or_post="post", - url=url, - payload=payload, - connection_observer=connection_observer, - client_observer=None - ) - if not api_reply_object.valid: - return api_reply_object + return api_reply_object reply_dict_data = api_reply_object.data diff --git a/core/services/payment_phase/save_billing_choices.py b/core/services/payment_phase/save_billing_choices.py index afbf469..40afa8d 100644 --- a/core/services/payment_phase/save_billing_choices.py +++ b/core/services/payment_phase/save_billing_choices.py @@ -8,4 +8,4 @@ def save_billing_choices(payload: dict) -> None: billing_folder = Constants.HV_TICKETING_CONFIG_HOME filepath = f"{billing_folder}/billing_choices.json" - write_json_to_file(payload, filepath) + return write_json_to_file(payload, filepath) diff --git a/core/services/prepare_tickets/get_pub_key.py b/core/services/prepare_tickets/get_pub_key.py index 73861eb..c009ccc 100644 --- a/core/services/prepare_tickets/get_pub_key.py +++ b/core/services/prepare_tickets/get_pub_key.py @@ -7,6 +7,8 @@ from typing import Any # services from core.services.networking.make_url import make_url from core.services.networking.api_requests.step1_get_or_post import get_data_from_api +from core.services.networking.httpx import connect +from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType # utils from core.utils.basic_operations.does_file_exist import does_file_exist @@ -30,18 +32,20 @@ def get_from_server_and_save( url = make_url(string_form_of_key_name) - # the result of this is a python dictionary with single ' - public_key_results = get_data_from_server(url, connection_observer) + # public_key_results = get_data_from_server(url, connection_observer) + public_key_results = connect.single_endpoint( + method="get", + url=url, + observer=connection_observer, + payload=None + ) + + if public_key_results.valid: + # extract: + public_key = public_key_results.data - if isinstance(public_key_results, dict) and "valid" in public_key_results: - status = public_key_results.get("valid", False) - - if status == True: - # extract: - public_key = public_key_results.get("data", False) - - # save it: - did_it_save = write_string_to_text_file(public_key, file_path) + # save it: + did_it_save = write_string_to_text_file(public_key, file_path) return public_key_results diff --git a/core/services/prepare_tickets/get_public_key_by_config.py b/core/services/prepare_tickets/get_public_key_by_config.py index 401a3db..46eea03 100644 --- a/core/services/prepare_tickets/get_public_key_by_config.py +++ b/core/services/prepare_tickets/get_public_key_by_config.py @@ -71,7 +71,7 @@ def get_public_key_by_config(connection_observer: ConnectionObserver) -> dict: if reply in list_of_failures: return complete_failure_msg - if not instance(reply, dict): + if not isinstance(reply, dict): return { "status": False, "message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}", @@ -141,7 +141,7 @@ def get_public_key_from_LOCAL_files_only( if reply in list_of_failures: return complete_failure_msg - if not instance(reply, dict): + if not isinstance(reply, dict): return { "status": False, "message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}", diff --git a/core/services/prepare_tickets/setup_ticket_tracker.py b/core/services/prepare_tickets/setup_ticket_tracker.py index 6b9efdd..88a86ce 100644 --- a/core/services/prepare_tickets/setup_ticket_tracker.py +++ b/core/services/prepare_tickets/setup_ticket_tracker.py @@ -21,4 +21,4 @@ def setup_ticket_tracker(how_many_profiles: int) -> None: } counter += 1 - write_json_to_file(ticket_data, ticket_tracker_path) + return write_json_to_file(ticket_data, ticket_tracker_path) diff --git a/core/services/prepare_tickets/ticket_prep_orchestrator.py b/core/services/prepare_tickets/ticket_prep_orchestrator.py index 8e4b59e..7461ec6 100644 --- a/core/services/prepare_tickets/ticket_prep_orchestrator.py +++ b/core/services/prepare_tickets/ticket_prep_orchestrator.py @@ -94,9 +94,9 @@ def ticket_prep_orchestrator( ) # make a json to keep track of which tickets are used: - setup_ticket_tracker(how_many_profiles) + setup_tracker = setup_ticket_tracker(how_many_profiles) - if did_prep_work: + if did_prep_work and setup_tracker: # this means it unblinded, & setup the tracker without erroring out, # but it does NOT mean that verification of the blind sigs worked. return {"valid": True, "message": "worked"} diff --git a/core/services/prepare_tickets/ticket_tracker.py b/core/services/prepare_tickets/ticket_tracker.py index 411100b..b3519fb 100644 --- a/core/services/prepare_tickets/ticket_tracker.py +++ b/core/services/prepare_tickets/ticket_tracker.py @@ -1,7 +1,4 @@ -from core.utils.basic_operations.write_or_read_from_json import ( - write_json_to_file, - read_entire_json, -) +from core.utils.basic_operations.write_or_read_from_json import read_entire_json from core.errors.exceptions import * from core.errors.logger import logger from core.Constants import Constants @@ -62,7 +59,7 @@ def get_data_for_a_single_ticket(which_ticket_as_int: int) -> tuple: error_msg = f"Entire file of tickets does not exist. Check the location {ticket_tracker_path}" logger.error(error_msg, exc_info=True) print(error_msg) - raise InvalidData(error_msg) + raise ValueError(error_msg) # the data is a string for lookups: which_ticket = str(which_ticket_as_int) @@ -74,6 +71,6 @@ def get_data_for_a_single_ticket(which_ticket_as_int: int) -> tuple: subscription = data.get(which_ticket, {}).get("subscription") return status, location, subscription else: - raise InvalidData( + raise ValueError( f"Key '{which_ticket}' does not exist in JSON file {ticket_tracker_path}" ) diff --git a/core/utils/basic_operations/write_or_read_from_json.py b/core/utils/basic_operations/write_or_read_from_json.py index aec49dc..c3ed6a3 100644 --- a/core/utils/basic_operations/write_or_read_from_json.py +++ b/core/utils/basic_operations/write_or_read_from_json.py @@ -1,4 +1,3 @@ -from core.errors.exceptions import * from core.errors.logger import logger import json @@ -6,7 +5,7 @@ import os from pathlib import Path -def write_json_to_file(data: dict, filepath: str) -> None: +def write_json_to_file(data: dict, filepath: str) -> bool: try: # Create directory if it doesn't exist directory = os.path.dirname(filepath) @@ -16,10 +15,20 @@ def write_json_to_file(data: dict, filepath: str) -> None: with open(filepath, "w") as f: json.dump(data, f, indent=4) + return True + except TypeError as e: - raise TypeError(f"Data is not JSON serializable: {e}") + logger.error(f"Data is not JSON serializable: {e}") + return False + except FileNotFoundError: + logger.error(f"File not found: {filepath}") + return False + except IsADirectoryError: + logger.error(f"Path is a directory, not a file: {filepath}") + return False except IOError as e: - raise IOError(f"Error writing to file {filepath}: {e}") + logger.error(f"Error reading file {filepath}: {e}") + return False def read_entire_json(filepath: str) -> dict: @@ -43,13 +52,13 @@ def read_entire_json(filepath: str) -> dict: f"Invalid JSON in file {filepath}: {e.msg}", e.doc, e.pos ) except FileNotFoundError: - raise FileNotFoundError(f"File not found: {filepath}") + logger.error(f"File not found: {filepath}") return False except IsADirectoryError: - raise IsADirectoryError(f"Path is a directory, not a file: {filepath}") + logger.error(f"Path is a directory, not a file: {filepath}") return False except IOError as e: - raise IOError(f"Error reading file {filepath}: {e}") + logger.error(f"Error reading file {filepath}: {e}") return False @@ -103,8 +112,14 @@ def update_value_in_json_with_two_values( return True - except: - raise InvalidData(f"Error reading file {filepath}") + except FileNotFoundError: + logger.error(f"File not found: {filepath}") + return False + except IsADirectoryError: + logger.error(f"Path is a directory, not a file: {filepath}") + return False + except IOError as e: + logger.error(f"Error reading file {filepath}: {e}") return False @@ -122,6 +137,12 @@ def update_json(filepath, key_to_add, value_to_update): write_json_to_file(data, filepath) return True - except: - raise InvalidData(f"Error reading file {filepath}") + except FileNotFoundError: + logger.error(f"File not found: {filepath}") + return False + except IsADirectoryError: + logger.error(f"Path is a directory, not a file: {filepath}") + return False + except IOError as e: + logger.error(f"Error reading file {filepath}: {e}") return False diff --git a/core/utils/run_commands.py b/core/utils/run_commands.py index 6d2a473..78eec59 100644 --- a/core/utils/run_commands.py +++ b/core/utils/run_commands.py @@ -131,14 +131,14 @@ def run_generic_command( try: returncode, stdout = _run_command_via_terminal(command, timeout) - output_data = stdout + # output_data = stdout if returncode == 0: logger.info(f"{human_readable_goal} was successful") - return Result(valid=True, data=output_data) + return Result(valid=True, data=stdout) else: # Try stderr first, fallback to stdout for error parsing - error_output = (stderr or stdout).strip() + error_output = stdout.strip() error_enum = parse_errors(error_output) logger.error(f"{human_readable_goal} Failed, error type is {error_enum}.") return Result(valid=False, error_type=error_enum, goal=human_readable_goal, message=error_output) @@ -147,3 +147,19 @@ def run_generic_command( return Result(valid=False, error_type=ResultError.TIMEOUT, goal=human_readable_goal, data=output_data, message=f"Command timed out {e}") except Exception as e: return Result(valid=False, error_type=ResultError.UNKNOWN, goal=human_readable_goal, data=output_data, message=str(e)) + + +def parse_errors(error_output: str) -> ResultError: + # Interface missing + if "No such device" in error_output: + error_enum = ResultError.INTERFACE + # Permission error + elif "No permissions" in error_output: + error_enum = ResultError.PERMISSION + elif "sudo: a password is required" or "password is required" in error_output: + error_enum = ResultError.PERMISSION + elif "command not found" in error_output: + error_enum = ResultError.MISSING_DEPENDENCY + else: + error_enum = ResultError.UNKNOWN + return error_enum