From 35d94908f476556bd8b9b3f0bb049a4dda2cb85c Mon Sep 17 00:00:00 2001 From: zenaku Date: Wed, 3 Jun 2026 17:54:29 -0500 Subject: [PATCH] Core configuration synchronized with the CLI --- core/controllers/ConnectionController.py | 88 +++++++++++++--------- core/controllers/ProfileController.py | 12 ++- core/controllers/SubscriptionController.py | 50 +++++++++++- core/models/SubscriptionPlan.py | 8 +- core/models/system/SystemConnection.py | 17 +++-- 5 files changed, 127 insertions(+), 48 deletions(-) diff --git a/core/controllers/ConnectionController.py b/core/controllers/ConnectionController.py index 689cde8..f8ffef4 100644 --- a/core/controllers/ConnectionController.py +++ b/core/controllers/ConnectionController.py @@ -45,6 +45,8 @@ class ConnectionController: @staticmethod def establish_connection(profile: Union[SessionProfile, SystemProfile], ignore: tuple[type[Exception]] = (), connection_observer: Optional[ConnectionObserver] = None): + from core.controllers.ConnectionController import ConnectionController + connection = profile.connection if connection.needs_proxy_configuration() and not profile.has_proxy_configuration(): @@ -66,32 +68,6 @@ class ConnectionController: if connection.needs_wireguard_configuration() and not profile.has_wireguard_configuration(): - - - - if connection.needs_operator_proxy(): - - if profile.has_subscription(): - - if not profile.subscription.has_been_activated(): - ProfileController.activate_subscription(profile, connection_observer=connection_observer) - - operator_proxy_session = ConnectionController.with_preferred_connection( - profile.subscription.billing_code, - profile.subscription.operator_id, - connection.get_protocol(), - task=WebServiceApiService.post_operator_proxy, - connection_observer=connection_observer - ) - - if operator_proxy_session is None: - raise InvalidSubscriptionError() - - profile.attach_operator_proxy_session(operator_proxy_session) - - else: - raise MissingSubscriptionError() - if profile.has_subscription(): if not profile.subscription.has_been_activated(): @@ -112,6 +88,29 @@ class ConnectionController: raise MissingSubscriptionError() + if connection.needs_operator_proxy() and not profile.has_operator_proxy_session(): + + if profile.has_subscription(): + + if not profile.subscription.has_been_activated(): + ProfileController.activate_subscription(profile, connection_observer=connection_observer) + + operator_proxy_session = ConnectionController.with_preferred_connection( + profile.subscription.billing_code, + profile.subscription.operator_id, + connection.get_protocol(), + task=WebServiceApiService.post_operator_proxy, + connection_observer=connection_observer + ) + + if operator_proxy_session is None: + raise InvalidSubscriptionError() + + profile.attach_operator_proxy_session(operator_proxy_session) + + else: + raise MissingSubscriptionError() + if profile.is_session_profile(): try: @@ -143,7 +142,6 @@ class ConnectionController: raise ConnectionError('The connection could not be established.') return None - @staticmethod def establish_session_connection(profile: SessionProfile, ignore: tuple[type[Exception]] = (), connection_observer: Optional[ConnectionObserver] = None): @@ -212,6 +210,20 @@ class ConnectionController: @staticmethod def establish_system_connection(profile: SystemProfile, ignore: tuple[type[Exception]] = (), connection_observer: Optional[ConnectionObserver] = None): + if profile.connection.needs_operator_proxy(): + operator_proxy_session = profile.get_operator_proxy_session() + protocol = profile.connection.get_protocol() + + if protocol == 'vless': + from core.controllers.encrypted_proxy.VlessController import VlessController + VlessController(1080).enable(operator_proxy_session.links[0], operator_proxy_session.username, connection_observer) + elif protocol == 'hysteria2': + from core.controllers.encrypted_proxy.HysteriaController import HysteriaController + HysteriaController(1080).enable(operator_proxy_session.username, operator_proxy_session.password, operator_proxy_session.operator_hysteria2_host, connection_observer) + + SystemStateController.create(profile.id) + return + if ConfigurationController.get_endpoint_verification_enabled(): ProfileController.verify_wireguard_endpoint(profile, ignore=ignore) @@ -219,36 +231,28 @@ class ConnectionController: ConnectionController.__establish_system_connection(profile, connection_observer) except ConnectionError: - try: ConnectionController.terminate_system_connection() except ConnectionTerminationError: pass - raise ConnectionError('The connection could not be established.') except CalledProcessError: - try: ConnectionController.terminate_system_connection() except ConnectionTerminationError: pass - try: ConnectionController.__establish_system_connection(profile, connection_observer) - except (ConnectionError, CalledProcessError): - try: ConnectionController.terminate_system_connection() except ConnectionTerminationError: pass - raise ConnectionError('The connection could not be established.') ConnectionController.terminate_tor_connection() time.sleep(1.0) - @staticmethod def establish_tor_connection(connection_observer: Optional[ConnectionObserver] = None): @@ -334,6 +338,19 @@ class ConnectionController: @staticmethod def terminate_system_connection(): + system_state = SystemStateController.get() + if system_state is not None: + profile = ProfileController.get(system_state.profile_id) + if profile is not None and profile.connection.needs_operator_proxy(): + protocol = profile.connection.get_protocol() + if protocol == 'vless': + from core.controllers.encrypted_proxy.VlessController import VlessController + VlessController(1080).disable() + elif protocol == 'hysteria2': + from core.controllers.encrypted_proxy.HysteriaController import HysteriaController + HysteriaController(1080).disable() + SystemState.dissolve() + return if shutil.which('nmcli') is None: raise CommandNotFoundError('nmcli') @@ -351,7 +368,6 @@ class ConnectionController: else: raise ConnectionTerminationError('The connection could not be terminated.') - @staticmethod def get_proxies(port_number: int): diff --git a/core/controllers/ProfileController.py b/core/controllers/ProfileController.py index ffdad9c..14473eb 100644 --- a/core/controllers/ProfileController.py +++ b/core/controllers/ProfileController.py @@ -155,20 +155,24 @@ class ProfileController: from core.controllers.ConnectionController import ConnectionController if profile.has_subscription(): - + + print(f"DEBUG activate_subscription:") + print(f" billing_code: {profile.subscription.billing_code}") + print(f" operator_id: {profile.subscription.operator_id}") + subscription = ConnectionController.with_preferred_connection(profile.subscription.billing_code, task=WebServiceApiService.get_subscription, connection_observer=connection_observer) + print(f" get_subscription returned: {subscription}") + if subscription is not None: - profile.subscription = subscription profile.save() - else: raise InvalidSubscriptionError() else: raise MissingSubscriptionError() - + @staticmethod def is_enabled(profile: Union[SessionProfile, SystemProfile]): diff --git a/core/controllers/SubscriptionController.py b/core/controllers/SubscriptionController.py index 79b75bc..dfaa3f1 100644 --- a/core/controllers/SubscriptionController.py +++ b/core/controllers/SubscriptionController.py @@ -5,7 +5,6 @@ from core.observers.ConnectionObserver import ConnectionObserver from core.services.WebServiceApiService import WebServiceApiService from typing import Union - class SubscriptionController: @staticmethod @@ -18,4 +17,51 @@ class SubscriptionController: def create(subscription_plan: SubscriptionPlan, profile: Union[SessionProfile, SystemProfile], connection_observer: ConnectionObserver = None): from core.controllers.ConnectionController import ConnectionController - return ConnectionController.with_preferred_connection(subscription_plan.id, profile.location.id, task=WebServiceApiService.post_subscription, connection_observer=connection_observer) + + if profile.location: + return ConnectionController.with_preferred_connection( + subscription_plan.id, + profile.location.id, + task=WebServiceApiService.post_subscription, + connection_observer=connection_observer + ) + else: + return ConnectionController.with_preferred_connection( + subscription_plan.id, + operator_id=profile.connection.operator_id, + task=WebServiceApiService.post_subscription, + connection_observer=connection_observer + ) + + from core.controllers.ConnectionController import ConnectionController + + if profile.location: + return ConnectionController.with_preferred_connection( + subscription_plan.id, + profile.location.id, + task=WebServiceApiService.post_subscription, + connection_observer=connection_observer + ) + else: + return ConnectionController.with_preferred_connection( + subscription_plan.id, + task=WebServiceApiService.post_subscription, + connection_observer=connection_observer + ) + + from core.controllers.ConnectionController import ConnectionController + + if profile.location: + # Para WireGuard (con location) + return ConnectionController.with_preferred_connection( + subscription_plan.id, + profile.location.id, + task=WebServiceApiService.post_subscription, + connection_observer=connection_observer + ) + else: + return ConnectionController.with_preferred_connection( + subscription_plan.id, + task=WebServiceApiService.post_subscription, + connection_observer=connection_observer + ) \ No newline at end of file diff --git a/core/models/SubscriptionPlan.py b/core/models/SubscriptionPlan.py index 5a55c8e..540e7fa 100644 --- a/core/models/SubscriptionPlan.py +++ b/core/models/SubscriptionPlan.py @@ -53,6 +53,9 @@ class SubscriptionPlan(Model): if connection.code == 'wireguard': features_wireguard = True + if connection.code == 'operator': + features_proxy = True + Model._create_table_if_not_exists(table_name=_table_name, table_definition=_table_definition) return Model._query_one('SELECT * FROM subscription_plans WHERE features_proxy = ? AND features_wireguard = ? AND duration = ? LIMIT 1', SubscriptionPlan.factory, [features_proxy, features_wireguard, duration]) @@ -76,6 +79,9 @@ class SubscriptionPlan(Model): if connection.code == 'wireguard': features_wireguard = True + if connection.code == 'operator': + features_proxy = True + return Model._query_all('SELECT * FROM subscription_plans WHERE features_proxy = ? AND features_wireguard = ?', SubscriptionPlan.factory, [features_proxy, features_wireguard]) @staticmethod @@ -94,4 +100,4 @@ class SubscriptionPlan(Model): @staticmethod def tuple_factory(subscription_plan): - return subscription_plan.id, subscription_plan.code, subscription_plan.wireguard_session_limit, subscription_plan.duration, subscription_plan.price, subscription_plan.features_proxy, subscription_plan.features_wireguard + return subscription_plan.id, subscription_plan.code, subscription_plan.wireguard_session_limit, subscription_plan.duration, subscription_plan.price, subscription_plan.features_proxy, subscription_plan.features_wireguard \ No newline at end of file diff --git a/core/models/system/SystemConnection.py b/core/models/system/SystemConnection.py index 8122b76..d146002 100644 --- a/core/models/system/SystemConnection.py +++ b/core/models/system/SystemConnection.py @@ -1,15 +1,22 @@ from core.models.BaseConnection import BaseConnection -from dataclasses import dataclass - +from dataclasses import dataclass, field +from typing import Optional @dataclass -class SystemConnection (BaseConnection): +class SystemConnection(BaseConnection): + operator_id: Optional[int] = field(default=None) + protocol: Optional[str] = field(default=None) def __post_init__(self): - - if self.code != 'wireguard': + if self.code not in ('wireguard', 'operator'): raise ValueError('Invalid connection code.') @staticmethod def needs_proxy_configuration(): return False + + def needs_operator_proxy(self): + return self.code == 'operator' + + def get_protocol(self): + return self.protocol \ No newline at end of file