ApplicationController transitioned to the HTTPx Client system, instead of using requests library proxy for Tor. Also the GUI's observers now do strings to be more neutral to what object type is being downloaded (application_version objects vs dependency).
This commit is contained in:
parent
b84bb2eefa
commit
01bd5f8d3f
4 changed files with 78 additions and 32 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,3 +5,4 @@ prototype_client.py
|
|||
.venv
|
||||
__pycache__
|
||||
dist
|
||||
.mypy_cache
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
# Singbox Setup
|
||||
### Aug 12, 2026
|
||||
Prepared Singbox setup modules, which includes installation, download, move to sudo folder, and sudo setup scripts. Added a `Dependency` model, endpoint, and the ability to sync that model. (Related note: Server-side prepared the endpoint, and stocked with real data.) And also the sync service modules were adjusted to handle new data types more smoothly, before they had errors. As part of that sync flow change, the CachedSync metadata model was transitioned to ints instead of strings, with default 0 values. This should in theory migrate all clients without further changes needed.
|
||||
|
||||
### Additional Changes:
|
||||
ApplicationController transitioned to the HTTPx Client system, instead of using requests library proxy for Tor. Also the GUI's observers now do strings to be more neutral to what object type is being downloaded (application_version objects vs dependency).
|
||||
<br/>
|
||||
|
||||
# Singbox Orchestration
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
from core.services.networking.httpx import httpx_client
|
||||
from core.services.networking.httpx import connect
|
||||
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
|
||||
from core.controllers.ConfigurationController import ConfigurationController
|
||||
from core.models.Configuration import Configuration, ConnectionChoice
|
||||
|
||||
from core.Errors import FileIntegrityError, UnsupportedApplicationVersionError, ApplicationAlreadyInstalledError
|
||||
from core.controllers.ApplicationController import ApplicationController
|
||||
from core.models.session.Application import Application
|
||||
|
|
@ -12,6 +18,7 @@ from core.observers.ConnectionObserver import ConnectionObserver
|
|||
from core.services.WebServiceApiService import WebServiceApiService
|
||||
from core.errors.logger import logger
|
||||
|
||||
import httpx
|
||||
from io import BytesIO
|
||||
from typing import Optional
|
||||
import hashlib
|
||||
|
|
@ -19,6 +26,7 @@ import shutil
|
|||
import tarfile
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import select
|
||||
import os
|
||||
|
||||
class ApplicationVersionController:
|
||||
|
||||
|
|
@ -48,8 +56,12 @@ class ApplicationVersionController:
|
|||
if application_version.is_installed():
|
||||
raise ApplicationAlreadyInstalledError('The application in question is already installed.')
|
||||
|
||||
from core.controllers.ConnectionController import ConnectionController
|
||||
ConnectionController.with_preferred_connection(application_version, task=ApplicationVersionController.__install, application_version_observer=application_version_observer, connection_observer=connection_observer)
|
||||
# this used to go through "with_preferred_connection", but now re-uses the same HTTPx client as sync,
|
||||
ApplicationVersionController.__install(application_version, application_version_observer, connection_observer)
|
||||
|
||||
# legacy:
|
||||
# from core.controllers.ConnectionController import ConnectionController
|
||||
# ConnectionController.with_preferred_connection(application_version, task=ApplicationVersionController.__install, application_version_observer=application_version_observer, connection_observer=connection_observer)
|
||||
|
||||
@staticmethod
|
||||
def uninstall(application_version: ApplicationVersion):
|
||||
|
|
@ -72,41 +84,53 @@ class ApplicationVersionController:
|
|||
ApplicationVersion.save_many(application_versions)
|
||||
|
||||
@staticmethod
|
||||
def __install(application_version: ApplicationVersion, application_version_observer: Optional[ApplicationVersionObserver] = None, proxies: Optional[dict] = None):
|
||||
|
||||
import requests
|
||||
def __install(application_version: ApplicationVersion, application_version_observer: Optional[ApplicationVersionObserver] = None, connection_observer: Optional[ConnectionObserver] = None):
|
||||
target_app_name = application_version.application_code.capitalize()
|
||||
target_app_version = application_version.version_number
|
||||
|
||||
if application_version_observer is not None:
|
||||
application_version_observer.notify('downloading', application_version)
|
||||
application_version_observer.notify('downloading', f"Downloading {target_app_name} {target_app_version}. Connecting..")
|
||||
# legacy:
|
||||
# application_version_observer.notify('downloading', application_version)
|
||||
|
||||
if proxies is not None:
|
||||
response = requests.get(application_version.download_path, stream=True, proxies=proxies)
|
||||
else:
|
||||
response = requests.get(application_version.download_path, stream=True)
|
||||
################################################
|
||||
# SETUP HTTP CLIENT
|
||||
################################################
|
||||
client = httpx_client.get_http_session()
|
||||
logger.info(f"client type is {type(client)}")
|
||||
if client is None:
|
||||
client = _get_httpx_client(target_app_name=target_app_name, connection_observer=connection_observer)
|
||||
|
||||
################################################
|
||||
# GET THE DATA
|
||||
################################################
|
||||
download_path = application_version.download_path
|
||||
logger.info(f"download_path is {download_path}")
|
||||
with client.stream('GET', download_path) as response:
|
||||
logger.info("doing the stream...")
|
||||
if response.status_code == 200:
|
||||
|
||||
response_size = int(response.headers.get('Content-Length', 0))
|
||||
response_buffer = BytesIO()
|
||||
|
||||
block_size = 1024
|
||||
bytes_written = 0
|
||||
|
||||
for data in response.iter_content(block_size):
|
||||
for data in response.iter_bytes(block_size):
|
||||
|
||||
bytes_written += len(data)
|
||||
response_buffer.write(data)
|
||||
progress = (bytes_written / response_size) * 100 if response_size > 0 else 0
|
||||
|
||||
if application_version_observer is not None:
|
||||
application_version_observer.notify('download_progressing', f"Downloading {target_app_name} {progress:.2f}% v: {target_app_version}")
|
||||
else:
|
||||
raise ConnectionError('The application version could not be downloaded.')
|
||||
|
||||
application_version_observer.notify('download_progressing', application_version, dict(
|
||||
progress=progress
|
||||
))
|
||||
|
||||
application_version_observer.notify('downloaded', application_version)
|
||||
application_version_observer.notify('downloaded', f"Downloaded {target_app_name} {target_app_version}")
|
||||
response_buffer.seek(0)
|
||||
|
||||
################################################
|
||||
# VERIFY THE HASH
|
||||
################################################
|
||||
file_hash = ApplicationVersionController.__calculate_file_hash(response_buffer)
|
||||
|
||||
if file_hash != application_version.file_hash:
|
||||
|
|
@ -118,9 +142,6 @@ class ApplicationVersionController:
|
|||
with open(f'{application_version.get_installation_path()}/.sha3-512', 'w') as hash_file:
|
||||
hash_file.write(f'{file_hash}\n')
|
||||
|
||||
else:
|
||||
raise ConnectionError('The application version could not be downloaded.')
|
||||
|
||||
@staticmethod
|
||||
def __calculate_file_hash(file):
|
||||
|
||||
|
|
@ -136,6 +157,26 @@ class ApplicationVersionController:
|
|||
|
||||
return hasher.hexdigest()
|
||||
|
||||
# This function is a temporary transition for the connect module to get better public APIs
|
||||
def _get_httpx_client(target_app_name: str, connection_observer: Optional[ConnectionObserver]) -> httpx.Client:
|
||||
connection_type = ConfigurationController.get_connection_enum()
|
||||
client = connect.make_client(connection_type, connection_observer)
|
||||
logger.info(f"client type is {type(client)}")
|
||||
|
||||
if isinstance(client, ApiResponse):
|
||||
if not client.valid:
|
||||
raise ConnectionError(f'Could not connect, to download {target_app_name}.')
|
||||
|
||||
if isinstance(client, bool):
|
||||
if not client:
|
||||
raise ConnectionError(f'Could not connect, to download {target_app_name}.')
|
||||
else:
|
||||
logger.info("It's a boolean, getting the client now.")
|
||||
client = httpx_client.get_http_session()
|
||||
logger.info(f"client type is {type(client)}")
|
||||
return client
|
||||
|
||||
|
||||
|
||||
# legacy:
|
||||
# @staticmethod
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from essentials.observers.ConnectionObserver import ConnectionObserver
|
|||
from core.errors.logger import logger
|
||||
|
||||
import httpx
|
||||
from typing import Optional
|
||||
|
||||
_port_used = None
|
||||
|
||||
|
|
@ -43,7 +44,7 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload
|
|||
########################################################
|
||||
if client is None:
|
||||
observer.notify('message', "Testing Connection..")
|
||||
made_client = _make_client(connection_type, observer)
|
||||
made_client = make_client(connection_type, observer)
|
||||
if not made_client and connection_type == ConnectionChoice.TOR:
|
||||
observer.notify('message', "Tor Bootstrap..")
|
||||
return bootstrap_and_try_again(
|
||||
|
|
@ -113,7 +114,7 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload
|
|||
return initial_result
|
||||
|
||||
|
||||
def _make_client(connection_type: str, observer) -> httpx.Client | ApiResponse:
|
||||
def make_client(connection_type: str, observer: Optional[ConnectionObserver] = None) -> httpx.Client | ApiResponse:
|
||||
"""
|
||||
Rank:
|
||||
Coordinator
|
||||
|
|
|
|||
Loading…
Reference in a new issue