mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-08-21 14:37:16 +00:00
176 lines
6.1 KiB
Python
176 lines
6.1 KiB
Python
"""Inbound configuration models"""
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional, Dict, Any, List, Union
|
|
|
|
from .base import BaseXrayModel, XrayConfig, XrayProtocol
|
|
from .protocols import (
|
|
VLESSInboundConfig, VMeSSInboundConfig,
|
|
TrojanServerConfig, ShadowsocksServerConfig
|
|
)
|
|
from .transports import StreamSettings
|
|
|
|
|
|
@dataclass
|
|
class SniffingConfig(BaseXrayModel):
|
|
"""Traffic sniffing configuration"""
|
|
enabled: bool = True
|
|
destOverride: Optional[List[str]] = None
|
|
metadataOnly: bool = False
|
|
|
|
def __post_init__(self):
|
|
if self.destOverride is None:
|
|
self.destOverride = ["http", "tls"]
|
|
|
|
|
|
@dataclass
|
|
class ReceiverConfig(XrayConfig):
|
|
"""Receiver configuration for inbound"""
|
|
__xray_type__ = "xray.app.proxyman.ReceiverConfig"
|
|
|
|
listen: str = "0.0.0.0"
|
|
port: Optional[int] = None
|
|
portList: Optional[Union[int, str]] = None # Can be int or range like "10000-20000"
|
|
streamSettings: Optional[StreamSettings] = None
|
|
|
|
def to_xray_json(self) -> Dict[str, Any]:
|
|
"""Convert to Xray format"""
|
|
config = {"listen": self.listen}
|
|
|
|
# Either port or portList must be set
|
|
if self.port is not None:
|
|
config["port"] = self.port
|
|
elif self.portList is not None:
|
|
config["portList"] = self.portList
|
|
else:
|
|
raise ValueError("Either port or portList must be specified")
|
|
|
|
if self.streamSettings:
|
|
config["streamSettings"] = self.streamSettings.to_xray_json()
|
|
|
|
return config
|
|
|
|
|
|
@dataclass
|
|
class InboundConfig(BaseXrayModel):
|
|
"""Complete inbound configuration"""
|
|
tag: str
|
|
protocol: XrayProtocol
|
|
settings: Union[VLESSInboundConfig, VMeSSInboundConfig, TrojanServerConfig, ShadowsocksServerConfig]
|
|
listen: str = "0.0.0.0"
|
|
port: Optional[int] = None
|
|
portList: Optional[Union[int, str]] = None
|
|
streamSettings: Optional[StreamSettings] = None
|
|
sniffing: Optional[SniffingConfig] = None
|
|
|
|
def to_xray_json(self) -> Dict[str, Any]:
|
|
"""Convert to Xray API format with proper field order and structure"""
|
|
config = {
|
|
"listen": self.listen,
|
|
"tag": self.tag,
|
|
"protocol": self.protocol.value if hasattr(self.protocol, 'value') else str(self.protocol),
|
|
}
|
|
|
|
# Add port or portList (port comes before protocol in working format)
|
|
if self.port is not None:
|
|
config["port"] = self.port
|
|
elif self.portList is not None:
|
|
config["portList"] = self.portList
|
|
else:
|
|
raise ValueError("Either port or portList must be specified")
|
|
|
|
# Add protocol settings with _TypedMessage_
|
|
settings = self.settings.to_xray_json()
|
|
if "_TypedMessage_" not in settings:
|
|
# Add _TypedMessage_ based on protocol
|
|
protocol_type_map = {
|
|
"vless": "xray.proxy.vless.inbound.Config",
|
|
"vmess": "xray.proxy.vmess.inbound.Config",
|
|
"trojan": "xray.proxy.trojan.inbound.Config",
|
|
"shadowsocks": "xray.proxy.shadowsocks.inbound.Config"
|
|
}
|
|
protocol_name = self.protocol.value if hasattr(self.protocol, 'value') else str(self.protocol)
|
|
if protocol_name in protocol_type_map:
|
|
settings["_TypedMessage_"] = protocol_type_map[protocol_name]
|
|
|
|
config["settings"] = settings
|
|
|
|
# Add stream settings
|
|
if self.streamSettings:
|
|
config["streamSettings"] = self.streamSettings.to_xray_json()
|
|
else:
|
|
config["streamSettings"] = {"network": "tcp"} # Default TCP
|
|
|
|
# Add sniffing
|
|
if self.sniffing:
|
|
config["sniffing"] = self.sniffing.to_dict()
|
|
|
|
return config
|
|
|
|
|
|
# Builder for easier configuration
|
|
class InboundBuilder:
|
|
"""Builder for creating inbound configurations"""
|
|
|
|
def __init__(self, tag: str, protocol: XrayProtocol):
|
|
self.tag = tag
|
|
self.protocol = protocol
|
|
self._settings = None
|
|
self._listen = "0.0.0.0"
|
|
self._port = None
|
|
self._port_list = None
|
|
self._stream_settings = None
|
|
self._sniffing = None
|
|
|
|
def listen(self, address: str) -> 'InboundBuilder':
|
|
"""Set listen address"""
|
|
self._listen = address
|
|
return self
|
|
|
|
def port(self, port: int) -> 'InboundBuilder':
|
|
"""Set single port"""
|
|
self._port = port
|
|
self._port_list = None
|
|
return self
|
|
|
|
def port_range(self, start: int, end: int) -> 'InboundBuilder':
|
|
"""Set port range"""
|
|
self._port_list = f"{start}-{end}"
|
|
self._port = None
|
|
return self
|
|
|
|
def stream_settings(self, settings: StreamSettings) -> 'InboundBuilder':
|
|
"""Set stream settings"""
|
|
self._stream_settings = settings
|
|
return self
|
|
|
|
def sniffing(self, enabled: bool = True, dest_override: Optional[List[str]] = None) -> 'InboundBuilder':
|
|
"""Configure sniffing"""
|
|
self._sniffing = SniffingConfig(
|
|
enabled=enabled,
|
|
destOverride=dest_override
|
|
)
|
|
return self
|
|
|
|
def protocol_settings(self, settings: Union[VLESSInboundConfig, VMeSSInboundConfig, TrojanServerConfig, ShadowsocksServerConfig]) -> 'InboundBuilder':
|
|
"""Set protocol-specific settings"""
|
|
self._settings = settings
|
|
return self
|
|
|
|
def build(self) -> InboundConfig:
|
|
"""Build the inbound configuration"""
|
|
if not self._settings:
|
|
raise ValueError("Protocol settings must be specified")
|
|
|
|
if not self._port and not self._port_list:
|
|
raise ValueError("Either port or port range must be specified")
|
|
|
|
return InboundConfig(
|
|
tag=self.tag,
|
|
protocol=self.protocol,
|
|
settings=self._settings,
|
|
listen=self._listen,
|
|
port=self._port,
|
|
portList=self._port_list,
|
|
streamSettings=self._stream_settings,
|
|
sniffing=self._sniffing
|
|
) |