1from enum import Enum
2
3from pydantic import ConfigDict, BaseModel, Field, field_validator
4
5from app.models.node import Node
6
7
8class ProxyTypes(str, Enum):
9 # proxy_type = protocol
10 VMess = "vmess"
11 VLESS = "vless"
12 Trojan = "trojan"
13 Shadowsocks = "shadowsocks"
14 Shadowsocks2022 = "shadowsocks2022"
15 Hysteria2 = "hysteria2"
16 WireGuard = "wireguard"
17 TUIC = "tuic"
18 ShadowTLS = "shadowtls"
19
20
21class InboundHostSecurity(str, Enum):
22 inbound_default = "inbound_default"
23 none = "none"
24 tls = "tls"
25
26
27InboundHostALPN = Enum(
28 "ProxyHostALPN",
29 {
30 "none": "none",
31 "h2": "h2",
32 "http/1.1": "http/1.1",
33 "h2,http/1.1": "h2,http/1.1",
34 "h3": "h3",
35 "h3,h2": "h3,h2",
36 "h3,h2,http/1.1": "h3,h2,http/1.1",
37 },
38)
39
40
41InboundHostFingerprint = Enum(
42 "ProxyHostFingerprint",
43 {
44 "none": "",
45 "chrome": "chrome",
46 "firefox": "firefox",
47 "safari": "safari",
48 "ios": "ios",
49 "android": "android",
50 "edge": "edge",
51 "360": "360",
52 "qq": "qq",
53 "random": "random",
54 "randomized": "randomized",
55 },
56)
57
58
59class FormatVariables(dict):
60 def __missing__(self, key):
61 return key.join("{}")
62
63
64class FragmentSettings(BaseModel):
65 packets: str = Field(pattern=r"^(:?tlshello|[\d-]{1,32})$")
66 length: str = Field(pattern=r"^[\d-]{1,32}$")
67 interval: str = Field(pattern=r"^[\d-]{1,32}$")
68
69
70class UDPNoiseSettings(BaseModel):
71 type: str = Field(pattern=r"^(:?rand|str|base64)$")
72 packet: str = Field()
73 interval: str = Field(pattern=r"^[\d-]{1,32}$")
74
75
76class InboundHost(BaseModel):
77 remark: str
78 address: str
79 port: int | None = Field(None)
80 sni: str | None = Field(None)
81 host: str | None = Field(None)
82 path: str | None = Field(None)
83 security: InboundHostSecurity = InboundHostSecurity.inbound_default
84 alpn: InboundHostALPN = InboundHostALPN.none
85 fingerprint: InboundHostFingerprint = InboundHostFingerprint.none
86 allowinsecure: bool | None = False
87 is_disabled: bool | None = False
88 mux: bool = Field(False)
89 fragment: FragmentSettings | None = Field(None)
90 udp_noises: list[UDPNoiseSettings] | None = []
91 http_headers: dict[str, list[str]] | None = {}
92 mtu: int | None = None
93 dns_servers: str | None = None
94 allowed_ips: str | None = None
95 weight: int = 1
96 protocol: ProxyTypes | None = None
97 inbound_id: int | None = None
98 model_config = ConfigDict(from_attributes=True)
99
100 @field_validator("remark", "address", "path")
101 @classmethod
102 def validate_fmt_variables(cls, v: str) -> str:
103 if not v:
104 return v
105
106 v.format_map(FormatVariables())
107
108 return v
109
110 @field_validator("alpn", mode="before")
111 @classmethod
112 def validate_alpn(cls, v):
113 if not v:
114 return InboundHostALPN.none
115 return v
116
117
118class InboundHostResponse(InboundHost):
119 id: int
120
121
122class Inbound(BaseModel):
123 id: int
124 tag: str
125 protocol: ProxyTypes
126 config: str
127 node: Node
128 service_ids: list[int]
129 model_config = ConfigDict(from_attributes=True)