from core.services.profiles.ProfileRepository import ProfileRepository from core.services.networking.systemwide.systemwide_wireguard import terminate_system_connection from core.services.networking.general_connection_tools.testing_evaluating import system_uses_wireguard_interface from core.services.networking.general_connection_tools.connection_enable import establish_connection from core.services.networking.systemwide.systemwide_utils import get_firewall_setting, get_dns_setting from core.services.networking.systemwide.encrypted_proxy.singbox_runner import end_singbox from core.services.networking.systemwide import killswitch from core.services.subscriptions import subscriptions from core.errors.exceptions import FirewallError from core.models.Result import Result, ResultError from core.observers.TicketObserver import TicketObserver # from core.services.assassin.ticket_respawn import respawn_profile # from core.services.assassin import assassin_tools from core.errors.logger import logger from core.Errors import InvalidSubscriptionError, MissingSubscriptionError, ConnectionTerminationError, ProfileActivationError, ProfileDeactivationError, MissingLocationError, ConnectionUnprotectedError, EndpointVerificationError, ProfileStateConflictError from core.controllers.ApplicationController import ApplicationController from core.controllers.ApplicationVersionController import ApplicationVersionController from core.controllers.SessionStateController import SessionStateController from core.controllers.SystemStateController import SystemStateController from core.models.BaseProfile import BaseProfile as Profile from core.models.Subscription import Subscription from core.models.session.SessionProfile import SessionProfile from core.models.system.SystemProfile import SystemProfile from core.observers.ApplicationVersionObserver import ApplicationVersionObserver from core.observers.ConnectionObserver import ConnectionObserver from core.observers.ProfileObserver import ProfileObserver from core.services.WebServiceApiService import WebServiceApiService from typing import Union, Optional import base64 import re import time class ProfileController: # Initialize the singleton repository _repo = ProfileRepository() @staticmethod def get(id: int) -> Union[SessionProfile, SystemProfile, None]: # Controller no longer talks to the Model; it talks to the Repo return ProfileController._repo.get(id) @staticmethod def get_all(): return ProfileController._repo.get_all() @staticmethod def create(profile: Union[SessionProfile, SystemProfile], profile_observer: ProfileObserver = None): # Call the helper with the 'created' event ProfileController._save_and_sync(profile, 'created', profile_observer) @staticmethod def update(profile: Union[SessionProfile, SystemProfile], profile_observer: ProfileObserver = None): # Call the helper with the 'updated' event ProfileController._save_and_sync(profile, 'updated', profile_observer) @staticmethod def _save_and_sync(profile, event_type: str, observer: ProfileObserver = None): """Private helper to handle the actual persistence and cache synchronization.""" # 1. Persist to disk profile.save() # 2. update cache to prevent drift ProfileController._repo.update_cache(profile) # 3. Notify observer if observer is not None: observer.notify(event_type, profile) @staticmethod def delete(profile: Union[SessionProfile, SystemProfile], profile_observer: ProfileObserver = None): """Remove the profile from both disk and cache""" # 1. Save to temp variable: profile_id = profile.id # 2. Permanently delete from disk profile.delete() # 3. update cache with the temp variable to prevent drift ProfileController._repo.invalidate(profile_id=profile_id) @staticmethod def attach_subscription(profile: Union[SessionProfile, SystemProfile], subscription: Subscription): profile.subscription = subscription profile.save() @staticmethod def activate_subscription(profile: Union[SessionProfile, SystemProfile], connection_observer: Optional[ConnectionObserver] = None): from core.controllers.ConnectionController import ConnectionController if profile.has_subscription(): subscription = subscriptions.get_subscription( billing_code=profile.subscription.billing_code, connection_observer=connection_observer ) # legacy: # subscription = ConnectionController.with_preferred_connection(profile.subscription.billing_code, task=WebServiceApiService.get_subscription, connection_observer=connection_observer) if subscription is not None: profile.subscription = subscription profile.save() else: raise InvalidSubscriptionError() else: raise MissingSubscriptionError() @staticmethod def get_invoice(profile: Union[SessionProfile, SystemProfile]): if profile.has_subscription(): # return subscriptions.get_invoice(billing_code=profile.subscription.billing_code) return WebServiceApiService.get_invoice(profile.subscription.billing_code) else: return None @staticmethod def attach_proxy_configuration(profile: Union[SessionProfile, SystemProfile]): if profile.is_session_profile() and profile.has_subscription(): proxy_configuration = WebServiceApiService.get_proxy_configuration(profile.subscription.billing_code) if proxy_configuration is not None: profile.attach_proxy_configuration(proxy_configuration) @staticmethod def get_proxy_configuration(profile: Union[SessionProfile, SystemProfile]): if profile.is_session_profile(): return profile.get_proxy_configuration() else: return None @staticmethod def has_proxy_configuration(profile: Union[SessionProfile, SystemProfile]): profile.has_proxy_configuration() @staticmethod def get_wireguard_configuration_path(profile: Union[SessionProfile, SystemProfile]): return profile.get_wireguard_configuration_path() @staticmethod def has_wireguard_configuration(profile: Union[SessionProfile, SystemProfile]): return profile.has_wireguard_configuration()