Introduce Base profile types on load, which gradually will switch over on save/edits. Transition Connections to using Enum instead of raw strings.

This commit is contained in:
SimplifiedPrivacy 2026-07-18 16:44:54 -04:00
parent 00d6fc7738
commit 703fe57c12
5 changed files with 63 additions and 26 deletions

View file

@ -8,6 +8,7 @@ from core.controllers.ConfigurationController import ConfigurationController
from core.controllers.ProfileController import ProfileController from core.controllers.ProfileController import ProfileController
from core.controllers.SessionStateController import SessionStateController from core.controllers.SessionStateController import SessionStateController
from core.controllers.SystemStateController import SystemStateController from core.controllers.SystemStateController import SystemStateController
from core.models.BaseProfile import ProfileType
from core.models.session.SessionProfile import SessionProfile from core.models.session.SessionProfile import SessionProfile
from core.models.system.SystemProfile import SystemProfile from core.models.system.SystemProfile import SystemProfile
from core.models.system.SystemState import SystemState from core.models.system.SystemState import SystemState
@ -26,6 +27,7 @@ import subprocess
import sys import sys
import tempfile import tempfile
import time import time
from enum import Enum
class ConnectionController: class ConnectionController:
@ -58,6 +60,7 @@ class ConnectionController:
# needs_proxy_configuration comes from the child connection models # needs_proxy_configuration comes from the child connection models
# for the system it returns false blindly. for the session is checks if the connection type is masked. # for the system it returns false blindly. for the session is checks if the connection type is masked.
# if connection.masked and not profile.has_proxy_configuration():
if connection.needs_proxy_configuration() and not profile.has_proxy_configuration(): if connection.needs_proxy_configuration() and not profile.has_proxy_configuration():
if profile.has_subscription(): if profile.has_subscription():

View file

@ -1,19 +1,21 @@
from dataclasses import dataclass from dataclasses import dataclass
from dataclasses_json import dataclass_json from dataclasses_json import dataclass_json
@dataclass_json @dataclass_json
@dataclass @dataclass
class BaseConnection: class BaseConnection:
code: str code: str
# called by: connection controller for basic type checks on wireguard code # Called by: ConnectionController
# it uses it for basic type checks on wireguard code
def needs_wireguard_configuration(self): def needs_wireguard_configuration(self):
return self.code == 'wireguard' return self.code == 'wireguard'
# Called By SubscriptionPlan
def is_session_connection(self): def is_session_connection(self):
return type(self).__name__ == 'SessionConnection' return type(self).__name__ == 'SessionConnection'
# Not called. Dead code
def is_system_connection(self): def is_system_connection(self):
return type(self).__name__ == 'SystemConnection' return type(self).__name__ == 'SystemConnection'

View file

@ -28,6 +28,7 @@ import re
import shutil import shutil
import tempfile import tempfile
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from enum import Enum
@safe_db_operation @safe_db_operation
def execute_location_sql(country_code: str, city_code: str, session: Session) -> DatabaseOperation: def execute_location_sql(country_code: str, city_code: str, session: Session) -> DatabaseOperation:
@ -51,6 +52,9 @@ def get_profile_location_data(country_code: str, city_code: str) -> Location:
return None return None
class ProfileType(str, Enum):
SESSION = "session"
SYSTEM = "system"
@dataclass_json @dataclass_json
@dataclass @dataclass
@ -61,6 +65,7 @@ class BaseProfile(ABC):
name: str name: str
subscription: Optional[Subscription] subscription: Optional[Subscription]
location: Optional[Location] = field(metadata=config(exclude=Exclude.ALWAYS)) # SQLAlchemy object location: Optional[Location] = field(metadata=config(exclude=Exclude.ALWAYS)) # SQLAlchemy object
type: ProfileType
# legacy version included it to be serialized, but now it's an SQLAlchemy object. # legacy version included it to be serialized, but now it's an SQLAlchemy object.
# location: Optional[Location] # location: Optional[Location]
@ -237,16 +242,9 @@ class BaseProfile(ABC):
# this needs error handling if there's no location or malconformed config. # this needs error handling if there's no location or malconformed config.
# legacy phased out since SQLAlchemy handles the time_zone. # =========== SESSION ===========
# if location is not None:
# if profile['location'].get('time_zone') is not None:
# location.time_zone = profile['location']['time_zone']
# profile['location'] = location
if 'application_version' in profile: if 'application_version' in profile:
profile['type'] = ProfileType.SESSION
if profile['application_version'] is not None: if profile['application_version'] is not None:
application_version = ApplicationVersion.find(profile['application_version']['application_code'] or None, profile['application_version']['version_number'] or None) application_version = ApplicationVersion.find(profile['application_version']['application_code'] or None, profile['application_version']['version_number'] or None)
@ -258,7 +256,10 @@ class BaseProfile(ABC):
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
profile = SessionProfile.from_dict(profile) profile = SessionProfile.from_dict(profile)
# =========== SYSTEM ===========
else: else:
profile['type'] = ProfileType.SYSTEM
from core.models.system.SystemProfile import SystemProfile from core.models.system.SystemProfile import SystemProfile
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
@ -299,3 +300,10 @@ class BaseProfile(ABC):
@staticmethod @staticmethod
def __get_data_path(id: int): def __get_data_path(id: int):
return f'{Constants.HV_PROFILE_DATA_HOME}/{str(id)}' return f'{Constants.HV_PROFILE_DATA_HOME}/{str(id)}'
# legacy phased out since SQLAlchemy handles the time_zone.
# if location is not None:
# if profile['location'].get('time_zone') is not None:
# location.time_zone = profile['location']['time_zone']
# profile['location'] = location

View file

@ -1,20 +1,32 @@
from core.models.BaseConnection import BaseConnection from core.models.BaseConnection import BaseConnection
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
class SessionConnectionTypes(str, Enum):
WIREGUARD = "wireguard"
TOR = "tor"
@dataclass @dataclass
class SessionConnection(BaseConnection): class SessionConnection(BaseConnection):
masked: bool = False code: SessionConnectionTypes
masked: bool
def __post_init__(self): # Called by: ConnectionController
if self.code not in ('system', 'tor', 'wireguard'):
raise ValueError('Invalid connection code.')
# called by connection controller
# doesn't even make sense, it's checking if the Session is code system. this will always be false. # doesn't even make sense, it's checking if the Session is code system. this will always be false.
def is_unprotected(self): def is_unprotected(self):
return self.code == 'system' and self.masked is False return self.code == 'system' and self.masked is False
# Called by: SessionProfile.determine_timezone
def needs_proxy_configuration(self): def needs_proxy_configuration(self):
return self.masked is True return self.masked is True
# legacy:
# @dataclass
# class SessionConnection(BaseConnection):
# masked: bool = False
# def __post_init__(self):
# if self.code not in ('system', 'tor', 'wireguard'):
# raise ValueError('Invalid connection code.')

View file

@ -1,16 +1,28 @@
from core.models.BaseConnection import BaseConnection from core.models.BaseConnection import BaseConnection
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
from typing import Literal
class SystemConnectionTypes(str, Enum):
WIREGUARD = "wireguard"
HYSTERIA2 = "hysteria2"
VLESS = "vless"
@dataclass @dataclass
class SystemConnection(BaseConnection): class SystemConnection(BaseConnection):
code: SystemConnectionTypes
masked: Literal[False] = False
def __post_init__(self):
if self.code not in ('vless', 'hysteria2', 'wireguard'):
raise ValueError('Invalid connection code.')
@staticmethod @staticmethod
def needs_proxy_configuration(): def needs_proxy_configuration():
return False return False
# legacy:
# @dataclass
# class SystemConnection (BaseConnection):
# def __post_init__(self):
# if self.code not in ('vless', 'hysteria2', 'wireguard'):
# raise ValueError('Invalid connection code.')