diff --git a/src/xray_manager/core/outbound.py b/src/xray_manager/core/outbound.py index 8ea8ebb..c12ea68 100644 --- a/src/xray_manager/core/outbound.py +++ b/src/xray_manager/core/outbound.py @@ -11,6 +11,7 @@ from urllib.parse import parse_qs, quote, unquote, urlencode, urlparse from .outbound_fields import OUTBOUND_FIELDS if TYPE_CHECKING: + from .target import Target, TargetStorage from .xray_config import XrayConfig from .xray_manager_config import XrayManagerConfig @@ -18,7 +19,7 @@ if TYPE_CHECKING: @dataclass(frozen=True) class OutboundSpec: protocol: str - target: str + target_id: str O = TypeVar("O", bound="Outbound") @@ -50,22 +51,19 @@ class OutboundFactory: return list(cls._registry) @classmethod - def from_link( - cls, xray_manager_config: XrayManagerConfig, link: str - ) -> Outbound: + def from_link(cls, target_storage: TargetStorage, link: str) -> Outbound: scheme = link.split("://", 1)[0] if scheme not in cls._scheme_registry: raise ValueError(f"Unsupported link scheme: {scheme}") outbound_cls = cls._scheme_registry[scheme] - targets = xray_manager_config.targets - return outbound_cls.from_link(targets, link) + return outbound_cls.from_link(target_storage, link) @classmethod def from_inbound( cls, - xray_manager_config: XrayManagerConfig, + target_storage: TargetStorage, host: str, client: dict, inbound: dict, @@ -76,9 +74,8 @@ class OutboundFactory: raise ValueError(f"Unsupported protocol: {protocol}") outbound_cls = cls._registry[protocol] - targets = xray_manager_config.targets username, outbound = outbound_cls.from_inbound( - targets, host, client, inbound + target_storage, host, client, inbound ) return username, outbound @@ -86,26 +83,18 @@ class OutboundFactory: def from_spec( cls, xray_config: XrayConfig, - xray_manager_config: XrayManagerConfig, + target_storage: TargetStorage, spec: OutboundSpec, ) -> Outbound: if spec.protocol not in cls._registry: raise ValueError(f"Unsupported protocol: {spec.protocol}") - if spec.target not in xray_manager_config.get_targets_list(): - raise ValueError(f"Target {spec.target} not found") + target = target_storage.load_by_id(spec.target_id) inbound = xray_config.find_managed_inbound_by_protocol(spec.protocol) outbound_cls = cls._registry[spec.protocol] - targets = xray_manager_config.targets - target_pretty_name = targets.get(spec.target, {}).get( - "pretty_name", spec.target - ) - - return outbound_cls.from_scratch( - spec.target, target_pretty_name, xray_config.host, inbound - ) + return outbound_cls.from_scratch(target, xray_config.host, inbound) class Outbound(ABC): @@ -115,14 +104,14 @@ class Outbound(ABC): @classmethod @abstractmethod def from_link( - cls, targets: dict[str, dict[str, str]], link: str + cls, target_storage: TargetStorage, link: str ) -> Outbound: ... @classmethod @abstractmethod def from_inbound( cls, - targets: dict[str, dict[str, str]], + target_storage: TargetStorage, host: str, client: dict, inbound: dict, @@ -131,7 +120,7 @@ class Outbound(ABC): @classmethod @abstractmethod def from_scratch( - cls, target: str, target_pretty_name: str, host: str, inbound: dict + cls, target: Target, host: str, inbound: dict ) -> Outbound: ... @abstractmethod @@ -184,7 +173,7 @@ class ShadowsocksOutbound(_ShadowsocksFields, Outbound): @classmethod def from_link( - cls, targets: dict[str, dict[str, str]], link: str + cls, target_storage: TargetStorage, link: str ) -> ShadowsocksOutbound: import base64 @@ -198,15 +187,12 @@ class ShadowsocksOutbound(_ShadowsocksFields, Outbound): method, server_password, client_password = prefix_str.split(":") target_pretty_name = unquote(quoted_target_pretty_name) - target = cls.find_target_by_pretty_name(targets, target_pretty_name) - if target is None: - target = "unknown" + target = target_storage.load_by_pretty(target_pretty_name) return cls( host=host, port=port, target=target, - target_pretty_name=target_pretty_name, method=method, server_password=server_password, client_password=client_password, @@ -215,7 +201,7 @@ class ShadowsocksOutbound(_ShadowsocksFields, Outbound): @classmethod def from_inbound( cls, - targets: dict[str, dict[str, str]], + target_storage: TargetStorage, host: str, client: dict, inbound: dict, @@ -225,15 +211,15 @@ class ShadowsocksOutbound(_ShadowsocksFields, Outbound): server_password = inbound["settings"]["password"] client_password = client["password"] email = client["email"] - username, target = cls.split_client_email(email) - target_pretty_name = targets.get(target, {}).get("pretty_name", target) + username, target_id = cls.split_client_email(email) + + target = target_storage.load_by_id(target_id) outbound = cls( host=host, port=port, target=target, - target_pretty_name=target_pretty_name, method=method, server_password=server_password, client_password=client_password, @@ -242,7 +228,7 @@ class ShadowsocksOutbound(_ShadowsocksFields, Outbound): @classmethod def from_scratch( - cls, target: str, target_pretty_name: str, host: str, inbound: dict + cls, target: Target, host: str, inbound: dict ) -> ShadowsocksOutbound: port = inbound["port"] method = inbound["settings"]["method"] @@ -253,7 +239,6 @@ class ShadowsocksOutbound(_ShadowsocksFields, Outbound): host=host, port=port, target=target, - target_pretty_name=target_pretty_name, method=method, server_password=server_password, client_password=client_password, @@ -264,7 +249,7 @@ class ShadowsocksOutbound(_ShadowsocksFields, Outbound): prefix = f"{self.method}:{self.server_password}:{self.client_password}" prefix_b64 = base64.urlsafe_b64encode(prefix.encode()).decode() - tag = quote(self.target_pretty_name) + tag = quote(self.target.pretty) link = f"ss://{prefix_b64}@{self.host}:{self.port}#{tag}" return link