From 68a79364fbc348ae5482c80709e2a2c1e94045e4 Mon Sep 17 00:00:00 2001 From: Random Guy <50927468+M03ED@users.noreply.github.com> Date: Mon, 22 Jul 2024 17:32:00 +0330 Subject: [PATCH 1/6] chore(subscription): customizeable config detail for v2ray- json and sing-box --- app/subscription/singbox.py | 146 +++++++++++++-------- app/subscription/v2ray.py | 192 ++++++++++++++-------------- app/templates/singbox/settings.json | 19 +++ app/templates/v2ray/settings.json | 61 +++++++++ config.py | 9 +- 5 files changed, 283 insertions(+), 144 deletions(-) create mode 100644 app/templates/singbox/settings.json create mode 100644 app/templates/v2ray/settings.json diff --git a/app/subscription/singbox.py b/app/subscription/singbox.py index 6035fc594..2ede2c117 100644 --- a/app/subscription/singbox.py +++ b/app/subscription/singbox.py @@ -5,6 +5,7 @@ from config import ( SINGBOX_SUBSCRIPTION_TEMPLATE, + SINGBOX_SETTINGS_TEMPLATE, MUX_TEMPLATE, USER_AGENT_TEMPLATE, GRPC_USER_AGENT_TEMPLATE, @@ -33,6 +34,11 @@ def __init__(self): else: self.grpc_user_agent_data = [] + temp_settings = render_template(SINGBOX_SETTINGS_TEMPLATE) + self.settings = json.loads(temp_settings) + + del temp_user_agent_data, user_agent_data, temp_grpc_user_agent_data, grpc_user_agent_data, temp_settings + def add_outbound(self, outbound_data): self.config["outbounds"].append(outbound_data) @@ -88,75 +94,115 @@ def tls_config(sni=None, fp=None, tls=None, pbk=None, return config + def http_config(self, host='', path='', random_user_agent: bool = False): + config = self.settings.get("httpSettings", { + "idle_timeout": "15s", + "ping_timeout": "15s", + "method": "GET", + "headers": {} + }) + if "headers" not in config: + config["headers"] = {} + + config["host"] = [] + if path: + config["path"] = path + if host: + config["host"] = [host] + if random_user_agent: + config["headers"]["User-Agent"] = choice(self.user_agent_list) + + return config + + def ws_config(self, host='', path='', random_user_agent: bool = False, + max_early_data=None, early_data_header_name=None): + config = self.settings.get("wsSettings", { + "headers": {} + }) + if "headers" not in config: + config["headers"] = {} + + if path: + config["path"] = path + if host: + config["headers"] = {"Host": host} + if random_user_agent: + config["headers"]["User-Agent"] = choice(self.user_agent_list) + if max_early_data is not None: + config["max_early_data"] = max_early_data + if early_data_header_name: + config["early_data_header_name"] = early_data_header_name + + return config + + def grpc_config(self, path='', random_user_agent: bool = False): + config = self.settings.get("grpcSettings", { + "headers": {} + }) + if "headers" not in config: + config["headers"] = {} + + if path: + config["service_name"] = path + if random_user_agent: + config["headers"]["User-Agent"] = choice(self.grpc_user_agent_data) + + return config + + def httpupgrade_config(self, host='', path='', random_user_agent: bool = False): + config = self.settings.get("httpupgradeSettings", { + "headers": {} + }) + if "headers" not in config: + config["headers"] = {} + + config["host"] = host + if path: + config["path"] = path + if random_user_agent: + config["headers"]["User-Agent"] = choice(self.user_agent_list) + + return config + def transport_config(self, transport_type='', host='', path='', - method='', - idle_timeout="15s", - ping_timeout="15s", max_early_data=None, early_data_header_name=None, - permit_without_stream=False, random_user_agent: bool = False): transport_config = {} if transport_type: - transport_config['type'] = transport_type - if transport_type == "http": - transport_config['host'] = [] - if path: - transport_config['path'] = path - if method: - transport_config['method'] = method - if host or random_user_agent: - transport_config['headers'] = {} - if host: - transport_config["host"] = [host] - if random_user_agent: - transport_config['headers']['User-Agent'] = choice(self.user_agent_list) - if idle_timeout: - transport_config['idle_timeout'] = idle_timeout - if ping_timeout: - transport_config['ping_timeout'] = ping_timeout + transport_config = self.http_config( + host=host, + path=path, + random_user_agent=random_user_agent + ) elif transport_type == "ws": - if path: - transport_config['path'] = path - if host or random_user_agent: - transport_config['headers'] = {} - if host: - transport_config['headers'] = {'Host': host} - if random_user_agent: - transport_config['headers']['User-Agent'] = choice(self.user_agent_list) - if max_early_data is not None: - transport_config['max_early_data'] = max_early_data - if early_data_header_name: - transport_config['early_data_header_name'] = early_data_header_name + transport_config = self.ws_config( + host=host, + path=path, + random_user_agent=random_user_agent, + max_early_data=max_early_data, + early_data_header_name=early_data_header_name + ) elif transport_type == "grpc": - if path: - transport_config['service_name'] = path - if idle_timeout: - transport_config['idle_timeout'] = idle_timeout - if ping_timeout: - transport_config['ping_timeout'] = ping_timeout - if permit_without_stream: - transport_config['permit_without_stream'] = permit_without_stream - if random_user_agent: - transport_config['headers'] = {} - transport_config['headers']['User-Agent'] = choice(self.grpc_user_agent_data) + transport_config = self.grpc_config( + path=path, + random_user_agent=random_user_agent) elif transport_type == "httpupgrade": - transport_config['host'] = host - if path: - transport_config['path'] = path - if random_user_agent: - transport_config['headers'] = {} - transport_config['headers']['User-Agent'] = choice(self.user_agent_list) + transport_config = self.httpupgrade_config( + host=host, + path=path, + random_user_agent=random_user_agent) + transport_config['type'] = transport_type return transport_config def make_outbound(self, diff --git a/app/subscription/v2ray.py b/app/subscription/v2ray.py index 3171aa543..c664eb70c 100644 --- a/app/subscription/v2ray.py +++ b/app/subscription/v2ray.py @@ -12,6 +12,7 @@ MUX_TEMPLATE, USER_AGENT_TEMPLATE, V2RAY_SUBSCRIPTION_TEMPLATE, + V2RAY_SETTINGS_TEMPLATE, GRPC_USER_AGENT_TEMPLATE, ) @@ -134,27 +135,27 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): @classmethod def vmess( - cls, - remark: str, - address: str, - port: int, - id: Union[str, UUID], - host="", - net="tcp", - path="", - type="", - tls="none", - sni="", - fp="", - alpn="", - pbk="", - sid="", - spx="", - ais="", - fs="", - multiMode: bool = False, - max_upload_size: int = 1000000, - max_concurrent_uploads: int = 10, + cls, + remark: str, + address: str, + port: int, + id: Union[str, UUID], + host="", + net="tcp", + path="", + type="", + tls="none", + sni="", + fp="", + alpn="", + pbk="", + sid="", + spx="", + ais="", + fs="", + multiMode: bool = False, + max_upload_size: int = 1000000, + max_concurrent_uploads: int = 10, ): payload = { "add": address, @@ -203,10 +204,10 @@ def vmess( payload["maxConcurrentUploads"] = max_concurrent_uploads return ( - "vmess://" - + base64.b64encode( - json.dumps(payload, sort_keys=True).encode("utf-8") - ).decode() + "vmess://" + + base64.b64encode( + json.dumps(payload, sort_keys=True).encode("utf-8") + ).decode() ) @classmethod @@ -287,10 +288,10 @@ def vless(cls, payload["spx"] = spx return ( - "vless://" - + f"{id}@{address}:{port}?" - + urlparse.urlencode(payload) - + f"#{( urlparse.quote(remark))}" + "vless://" + + f"{id}@{address}:{port}?" + + urlparse.urlencode(payload) + + f"#{(urlparse.quote(remark))}" ) @classmethod @@ -370,20 +371,20 @@ def trojan(cls, payload["spx"] = spx return ( - "trojan://" - + f"{urlparse.quote(password, safe=':')}@{address}:{port}?" - + urlparse.urlencode(payload) - + f"#{urlparse.quote(remark)}" + "trojan://" + + f"{urlparse.quote(password, safe=':')}@{address}:{port}?" + + urlparse.urlencode(payload) + + f"#{urlparse.quote(remark)}" ) @classmethod def shadowsocks( - cls, remark: str, address: str, port: int, password: str, method: str + cls, remark: str, address: str, port: int, password: str, method: str ): return ( - "ss://" - + base64.b64encode(f"{method}:{password}".encode()).decode() - + f"@{address}:{port}#{urlparse.quote(remark)}" + "ss://" + + base64.b64encode(f"{method}:{password}".encode()).decode() + + f"@{address}:{port}#{urlparse.quote(remark)}" ) @@ -409,6 +410,11 @@ def __init__(self): else: self.grpc_user_agent_data = [] + temp_settings = render_template(V2RAY_SETTINGS_TEMPLATE) + self.settings = json.loads(temp_settings) + + del temp_user_agent_data, user_agent_data, temp_grpc_user_agent_data, grpc_user_agent_data, temp_settings + def add_config(self, remarks, outbounds): json_template = json.loads(self.template) json_template["remarks"] = remarks @@ -460,9 +466,10 @@ def reality_config(sni=None, fp=None, pbk=None, sid=None, spx=None): return realitySettings def ws_config(self, path=None, host=None, random_user_agent=None): + wsSettings = self.settings.get("wsSettings", {}) - wsSettings = {} - wsSettings["headers"] = {} + if "headers" not in wsSettings: + wsSettings["headers"] = {} if path: wsSettings["path"] = path if host: @@ -473,9 +480,10 @@ def ws_config(self, path=None, host=None, random_user_agent=None): return wsSettings def httpupgrade_config(self, path=None, host=None, random_user_agent=None): + httpupgradeSettings = self.settings.get("httpupgradeSettings", {}) - httpupgradeSettings = {} - httpupgradeSettings["headers"] = {} + if "headers" not in httpupgradeSettings: + httpupgradeSettings["headers"] = {} if path: httpupgradeSettings["path"] = path if host: @@ -490,9 +498,8 @@ def splithttp_config(self, path=None, host=None, random_user_agent=None, max_upload_size: int = 1000000, max_concurrent_uploads: int = 10, ): + splithttpSettings = self.settings.get("splithttpSettings", {}) - splithttpSettings = {} - splithttpSettings["headers"] = {} if path: splithttpSettings["path"] = path if host: @@ -506,14 +513,13 @@ def splithttp_config(self, path=None, host=None, random_user_agent=None, return splithttpSettings def grpc_config(self, path=None, host=None, multiMode=False, random_user_agent=None): - - grpcSettings = { + grpcSettings = self.settings.get("grpcSettings", { "multiMode": multiMode, "idle_timeout": 60, "health_check_timeout": 20, "permit_without_stream": False, "initial_windows_size": 35538 - } + }) if path: grpcSettings["serviceName"] = path @@ -526,40 +532,38 @@ def grpc_config(self, path=None, host=None, multiMode=False, random_user_agent=N return grpcSettings def tcp_http_config(self, path=None, host=None, random_user_agent=None): - tcpSettings = {} - - if any((path, host)): - tcpSettings["header"] = { - "type": "http", - "request": { - "version": "1.1", - "method": "GET", - "headers": { - "Accept-Encoding": ["gzip", "deflate"], - "Connection": ["keep-alive"], - "Pragma": "no-cache", - "User-Agent": [] - }, - } - } + tcpSettings = self.settings.get("tcpSettings", { + "header": {} + }) + if "header" not in tcpSettings: + tcpSettings["header"] = {} - if path: - tcpSettings["header"]["request"]["path"] = [path] + if any((path, host, random_user_agent)): + if "request" not in tcpSettings["header"]: + tcpSettings["header"]["request"] = {} - if host: - tcpSettings["header"]["request"]["headers"]["Host"] = [host] + if any((random_user_agent, host)): + if "headers" not in tcpSettings["header"]["request"]: + tcpSettings["header"]["request"]["headers"] = {} - if random_user_agent: - tcpSettings["header"]["request"]["headers"]["User-Agent"] = [ - choice(self.user_agent_list)] + if path: + tcpSettings["header"]["request"]["path"] = [path] + + if host: + tcpSettings["header"]["request"]["headers"]["Host"] = [host] + + if random_user_agent: + tcpSettings["header"]["request"]["headers"]["User-Agent"] = [ + choice(self.user_agent_list)] return tcpSettings def h2_config(self, path=None, host=None, random_user_agent=None): - - httpSettings = { - "headers": {} - } + httpSettings = self.settings.get("httpSettings", { + "header": {} + }) + if "header" not in httpSettings: + httpSettings["header"] = {} if path: httpSettings["path"] = path @@ -575,14 +579,16 @@ def h2_config(self, path=None, host=None, random_user_agent=None): return httpSettings - @staticmethod - def quic_config(path=None, host=None, header=None): - - quicSettings = { + def quic_config(self, path=None, host=None, header=None): + quicSettings = self.settings.get("quicSettings", { "security": "none", - "header": {"none"}, + "header": { + "type": "none" + }, "key": "" - } + }) + if "header" not in quicSettings: + quicSettings["header"] = {"type": "none"} if path: quicSettings["key"] = path @@ -593,11 +599,11 @@ def quic_config(path=None, host=None, header=None): return quicSettings - @staticmethod - def kcp_config(seed=None, host=None, header=None): - - kcpSettings = { - "header": {}, + def kcp_config(self, seed=None, host=None, header=None): + kcpSettings = self.settings.get("kcpSettings", { + "header": { + "type": "none" + }, "mtu": 1350, "tti": 50, "uplinkCapacity": 12, @@ -605,14 +611,14 @@ def kcp_config(seed=None, host=None, header=None): "congestion": False, "readBufferSize": 2, "writeBufferSize": 2, - } + }) + if "header" not in kcpSettings: + kcpSettings["header"] = {"type": "none"} if seed: kcpSettings["seed"] = seed if header: kcpSettings["header"]["type"] = header - else: - kcpSettings["header"]["type"] = "none" if host: kcpSettings["header"]["domain"] = host @@ -831,9 +837,9 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): } if inbound['protocol'] == 'vmess': - outbound["settings"] = self.vmess_config(address=address, - port=port, - id=settings['id']) + outbound["settings"] = self.vmess_config(address=address, + port=port, + id=settings['id']) elif inbound['protocol'] == 'vless': if net in ('tcp', 'kcp') and headers != 'http' and tls in ('tls', 'reality'): @@ -842,9 +848,9 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): flow = None outbound["settings"] = self.vless_config(address=address, - port=port, - id=settings['id'], - flow=flow) + port=port, + id=settings['id'], + flow=flow) elif inbound['protocol'] == 'trojan': outbound["settings"] = self.trojan_config(address=address, diff --git a/app/templates/singbox/settings.json b/app/templates/singbox/settings.json new file mode 100644 index 000000000..f54438b6b --- /dev/null +++ b/app/templates/singbox/settings.json @@ -0,0 +1,19 @@ +{ + "grpcSettings": { + "idle_timeout": "15s", + "ping_timeout": "15s", + "permit_without_stream": false + }, + "httpSettings": { + "idle_timeout": "15s", + "ping_timeout": "15s", + "method": "GET", + "headers": {} + }, + "wsSettings": { + "headers": {} + }, + "httpupgradeSettings": { + "headers": {} + } +} \ No newline at end of file diff --git a/app/templates/v2ray/settings.json b/app/templates/v2ray/settings.json new file mode 100644 index 000000000..2327ff8b4 --- /dev/null +++ b/app/templates/v2ray/settings.json @@ -0,0 +1,61 @@ +{ + "grpcSettings": { + "idle_timeout": 60, + "health_check_timeout": 20, + "permit_without_stream": false, + "initial_windows_size": 0 + }, + "httpSettings": { + "read_idle_timeout": 10, + "health_check_timeout": 15, + "method": "GET", + "headers": { + "Pragma": [ + "no-cache" + ] + } + }, + "kcpSettings": { + "mtu": 1350, + "tti": 50, + "uplinkCapacity": 5, + "downlinkCapacity": 20, + "congestion": false, + "readBufferSize": 2, + "writeBufferSize": 2, + "header": {} + }, + "tcpSettings": { + "header": { + "type": "http", + "request": { + "headers": { + "Accept-Encoding": [ + "gzip", "deflate" + ], + "Connection": [ + "keep-alive" + ], + "Pragma": "no-cache" + }, + "method": "GET", + "version": "1.1" + } + } + }, + "wsSettings": { + "headers": { + "Pragma": "no-cache" + } + }, + "httpupgradeSettings": { + "headers": { + "Pragma": "no-cache" + } + }, + "splithttpSettings": { + "headers": { + "Pragma": "no-cache" + } + } +} \ No newline at end of file diff --git a/config.py b/config.py index c50794dcf..999e45d75 100755 --- a/config.py +++ b/config.py @@ -44,12 +44,19 @@ JWT_ACCESS_TOKEN_EXPIRE_MINUTES = config("JWT_ACCESS_TOKEN_EXPIRE_MINUTES", cast=int, default=1440) CUSTOM_TEMPLATES_DIRECTORY = config("CUSTOM_TEMPLATES_DIRECTORY", default=None) -CLASH_SUBSCRIPTION_TEMPLATE = config("CLASH_SUBSCRIPTION_TEMPLATE", default="clash/default.yml") SUBSCRIPTION_PAGE_TEMPLATE = config("SUBSCRIPTION_PAGE_TEMPLATE", default="subscription/index.html") HOME_PAGE_TEMPLATE = config("HOME_PAGE_TEMPLATE", default="home/index.html") + +CLASH_SUBSCRIPTION_TEMPLATE = config("CLASH_SUBSCRIPTION_TEMPLATE", default="clash/default.yml") + SINGBOX_SUBSCRIPTION_TEMPLATE = config("SINGBOX_SUBSCRIPTION_TEMPLATE", default="singbox/default.json") +SINGBOX_SETTINGS_TEMPLATE = config("SINGBOX_SETTINGS_TEMPLATE", default="singbox/settings.json") + MUX_TEMPLATE = config("MUX_TEMPLATE", default="mux/default.json") + V2RAY_SUBSCRIPTION_TEMPLATE = config("V2RAY_SUBSCRIPTION_TEMPLATE", default="v2ray/default.json") +V2RAY_SETTINGS_TEMPLATE = config("V2RAY_SETTINGS_TEMPLATE", default="v2ray/settings.json") + USER_AGENT_TEMPLATE = config("USER_AGENT_TEMPLATE", default="user_agent/default.json") GRPC_USER_AGENT_TEMPLATE = config("GRPC_USER_AGENT_TEMPLATE", default="user_agent/grpc.json") From ff03e3afce52f4953aaa7d971e0524903d0f1bfb Mon Sep 17 00:00:00 2001 From: Random Guy <50927468+M03ED@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:00:14 +0330 Subject: [PATCH 2/6] fix(subscription): remove user-agent for sing-box and clash --- app/subscription/clash.py | 21 ++++++++++----------- app/subscription/singbox.py | 36 ++++++++++-------------------------- 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/app/subscription/clash.py b/app/subscription/clash.py index ae3c9cb0b..3c24d1f3e 100644 --- a/app/subscription/clash.py +++ b/app/subscription/clash.py @@ -6,10 +6,9 @@ from app.subscription.funcs import get_grpc_gun from app.templates import render_template from config import ( - CLASH_SUBSCRIPTION_TEMPLATE, - GRPC_USER_AGENT_TEMPLATE, + CLASH_SUBSCRIPTION_TEMPLATE, MUX_TEMPLATE, - USER_AGENT_TEMPLATE + USER_AGENT_TEMPLATE, ) @@ -31,13 +30,7 @@ def __init__(self): else: self.user_agent_list = [] - temp_grpc_user_agent_data = render_template(GRPC_USER_AGENT_TEMPLATE) - grpc_user_agent_data = json.loads(temp_grpc_user_agent_data) - - if 'list' in grpc_user_agent_data and isinstance(grpc_user_agent_data['list'], list): - self.grpc_user_agent_data = grpc_user_agent_data['list'] - else: - self.grpc_user_agent_data = [] + del temp_user_agent_data, user_agent_data def render(self, reverse=False): if reverse: @@ -96,7 +89,6 @@ def make_node(self, network = 'http' remark = self._remark_validation(name) - self.proxy_remarks.append(remark) node = { 'name': remark, 'type': type, @@ -215,15 +207,18 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): node['alterId'] = 0 node['cipher'] = 'auto' self.data['proxies'].append(node) + self.proxy_remarks.append(remark) if inbound['protocol'] == 'trojan': node['password'] = settings['password'] self.data['proxies'].append(node) + self.proxy_remarks.append(remark) if inbound['protocol'] == 'shadowsocks': node['password'] = settings['password'] node['cipher'] = settings['method'] self.data['proxies'].append(node) + self.proxy_remarks.append(remark) class ClashMetaConfiguration(ClashConfiguration): @@ -301,6 +296,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): node['alterId'] = 0 node['cipher'] = 'auto' self.data['proxies'].append(node) + self.proxy_remarks.append(remark) if inbound['protocol'] == 'vless': node['uuid'] = settings['id'] @@ -309,6 +305,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): node['flow'] = settings.get('flow', '') self.data['proxies'].append(node) + self.proxy_remarks.append(remark) if inbound['protocol'] == 'trojan': node['password'] = settings['password'] @@ -317,8 +314,10 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): node['flow'] = settings.get('flow', '') self.data['proxies'].append(node) + self.proxy_remarks.append(remark) if inbound['protocol'] == 'shadowsocks': node['password'] = settings['password'] node['cipher'] = settings['method'] self.data['proxies'].append(node) + self.proxy_remarks.append(remark) diff --git a/app/subscription/singbox.py b/app/subscription/singbox.py index af5e33500..7eb37dc06 100644 --- a/app/subscription/singbox.py +++ b/app/subscription/singbox.py @@ -8,7 +8,6 @@ SINGBOX_SETTINGS_TEMPLATE, MUX_TEMPLATE, USER_AGENT_TEMPLATE, - GRPC_USER_AGENT_TEMPLATE, ) @@ -27,19 +26,10 @@ def __init__(self): else: self.user_agent_list = [] - temp_grpc_user_agent_data = render_template(GRPC_USER_AGENT_TEMPLATE) - grpc_user_agent_data = json.loads(temp_grpc_user_agent_data) - - if 'list' in grpc_user_agent_data and isinstance(grpc_user_agent_data['list'], list): - self.grpc_user_agent_data = grpc_user_agent_data['list'] - else: - self.grpc_user_agent_data = [] - - temp_settings = render_template(SINGBOX_SETTINGS_TEMPLATE) self.settings = json.loads(temp_settings) - del temp_user_agent_data, user_agent_data, temp_grpc_user_agent_data, grpc_user_agent_data, temp_settings + del temp_user_agent_data, user_agent_data, temp_settings def _remark_validation(self, remark): if not remark in self.proxy_remarks: @@ -147,17 +137,11 @@ def ws_config(self, host='', path='', random_user_agent: bool = False, return config - def grpc_config(self, path='', random_user_agent: bool = False): - config = self.settings.get("grpcSettings", { - "headers": {} - }) - if "headers" not in config: - config["headers"] = {} + def grpc_config(self, path=''): + config = self.settings.get("grpcSettings", {}) if path: config["service_name"] = path - if random_user_agent: - config["headers"]["User-Agent"] = choice(self.grpc_user_agent_data) return config @@ -182,7 +166,8 @@ def transport_config(self, path='', max_early_data=None, early_data_header_name=None, - random_user_agent: bool = False): + random_user_agent: bool = False, + ): transport_config = {} @@ -191,7 +176,7 @@ def transport_config(self, transport_config = self.http_config( host=host, path=path, - random_user_agent=random_user_agent + random_user_agent=random_user_agent, ) elif transport_type == "ws": @@ -200,19 +185,18 @@ def transport_config(self, path=path, random_user_agent=random_user_agent, max_early_data=max_early_data, - early_data_header_name=early_data_header_name + early_data_header_name=early_data_header_name, ) elif transport_type == "grpc": - transport_config = self.grpc_config( - path=path, - random_user_agent=random_user_agent) + transport_config = self.grpc_config(path=path) elif transport_type == "httpupgrade": transport_config = self.httpupgrade_config( host=host, path=path, - random_user_agent=random_user_agent) + random_user_agent=random_user_agent, + ) transport_config['type'] = transport_type return transport_config From 1f0d8dd30ab4eea962c5dba9934135f074d5a65c Mon Sep 17 00:00:00 2001 From: Random Guy <50927468+M03ED@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:49:00 +0330 Subject: [PATCH 3/6] docs: v2ray and sing-box template docs --- .env.example | 5 ++++ app/templates/singbox/README.md | 46 +++++++++++++++++++++++++++++++ app/templates/v2ray/README.md | 49 +++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 app/templates/singbox/README.md create mode 100644 app/templates/v2ray/README.md diff --git a/.env.example b/.env.example index 62a37a34c..6ddd2bd56 100644 --- a/.env.example +++ b/.env.example @@ -33,8 +33,13 @@ UVICORN_PORT = 8000 # CLASH_SUBSCRIPTION_TEMPLATE="clash/my-custom-template.yml" # SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html" # HOME_PAGE_TEMPLATE="home/index.html" + # V2RAY_SUBSCRIPTION_TEMPLATE="v2ray/default.json" +# V2RAY_SETTINGS_TEMPLATE="v2ray/settings.json" + # SINGBOX_SUBSCRIPTION_TEMPLATE="singbox/default.json" +# SINGBOX_SETTINGS_TEMPLATE="singbox/settings.json" + # MUX_TEMPLATE="mux/default.json" ## Enable JSON config for compatible clients to use mux, fragment, etc. Default False. diff --git a/app/templates/singbox/README.md b/app/templates/singbox/README.md new file mode 100644 index 000000000..0a98771dd --- /dev/null +++ b/app/templates/singbox/README.md @@ -0,0 +1,46 @@ +# Sing-box Template + +## Usage +- Can be used to send completely prepared config to users depend on your usage. + +## Config Template +- With the config template, you can change things like routing and rules. + +## Settings Template +You can change some values in custom configs depending on the streamSettings type that is not accessible directly from the dashboard. + +For example, you can change these values for gRPC configs (you can change anything that is part of netSettings except those accessible from the dashboard). +```json +{ + "grpcSettings": { + "idle_timeout": "15s", + "ping_timeout": "15s", + "permit_without_stream": false + }, +} +``` +## How To Use +First of all, you need to set a directory for all of your templates (home, subscription page, etc.). +```shell +CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/" +``` +Make sure you put all of your templates in this folder.\ +If you are using Docker, make sure Docker has access to this folder.\ +Then, we need to make a directory for our V2ray template. +```shell +mkdir /var/lib/marzban/templates/sing-box +``` +After that, put your templates (config and settings) in the directory.\ +Now, change these variables with your files' names. +```shell +SINGBOX_SUBSCRIPTION_TEMPLATE="singbox/default.json" +SINGBOX_SETTINGS_TEMPLATE="singbox/settings.json" +``` +Now, restart your Marzban and enjoy. + +If you have already changed your env variables and you want to just update the template files, there is no need to restart Marzban. + +## Docs +you can use sing-box official documentation to find out how to modify template files + +[Sing-Box documentation](https://sing-box.sagernet.org/configuration/) diff --git a/app/templates/v2ray/README.md b/app/templates/v2ray/README.md new file mode 100644 index 000000000..fb2ce0d3f --- /dev/null +++ b/app/templates/v2ray/README.md @@ -0,0 +1,49 @@ +# V2ray Template + +## Usage +- Can be used to send completely prepared config to users and avoid application default values. + +## Config Template +- With the config template, you can change things like routing and rules. + +## Settings Template +You can change some values in custom configs depending on the streamSettings type that is not accessible directly from the dashboard. + +For example, you can change these values for gRPC configs (you can change anything that is part of netSettings except those accessible from the dashboard). +```json +{ + "grpcSettings": { + "idle_timeout": 60, + "health_check_timeout": 20, + "permit_without_stream": false, + "initial_windows_size": 0 + } +} +``` +## How To Use +First of all, you need to set a directory for all of your templates (home, subscription page, etc.). +```shell +CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/" +``` +Make sure you put all of your templates in this folder.\ +If you are using Docker, make sure Docker has access to this folder.\ +Then, we need to make a directory for our V2ray template. +```shell +mkdir /var/lib/marzban/templates/v2ray +``` +After that, put your templates (config and settings) in the directory.\ +Now, change these variables with your files' names. +```shell +V2RAY_SUBSCRIPTION_TEMPLATE="v2ray/default.json" +V2RAY_SETTINGS_TEMPLATE="v2ray/settings.json" +``` +Now, restart your Marzban and enjoy. + +If you have already changed your env variables and you want to just update the template files, there is no need to restart Marzban. + +## Docs +you can use these docs to find out how to modify template files + +[Xray Docs](https://xtls.github.io/en/) \ +[Xray Examples](https://github.com/XTLS/Xray-examples) \ +[Xray Examples](https://github.com/chika0801/Xray-examples) Unofficial From 8f0c9095ea98ccd0d469a04974a9bc99d77d5d18 Mon Sep 17 00:00:00 2001 From: Random Guy <50927468+M03ED@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:52:33 +0330 Subject: [PATCH 4/6] fix: single str port for v2ray-json --- app/subscription/v2ray.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/subscription/v2ray.py b/app/subscription/v2ray.py index f40844c47..447fa7ad9 100644 --- a/app/subscription/v2ray.py +++ b/app/subscription/v2ray.py @@ -822,6 +822,10 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): net = inbound['network'] protocol = inbound['protocol'] port = inbound['port'] + if isinstance(port, str): + ports = port.split(',') + port = int(choice(ports)) + tls = (inbound['tls']) headers = inbound['header_type'] fragment = inbound['fragment_setting'] @@ -880,7 +884,6 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): pass alpn = inbound.get('alpn', None) - outbound["streamSettings"] = self.make_stream_setting( net=net, tls=tls, From 4209ef477443b7909e27ccbbdb2662f3d46c2856 Mon Sep 17 00:00:00 2001 From: Random Guy <50927468+M03ED@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:38:36 +0330 Subject: [PATCH 5/6] docs: add supported network for sing-box and v2ray --- app/templates/singbox/README.md | 11 +++++++++++ app/templates/v2ray/README.md | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/templates/singbox/README.md b/app/templates/singbox/README.md index 0a98771dd..d5b467dab 100644 --- a/app/templates/singbox/README.md +++ b/app/templates/singbox/README.md @@ -19,6 +19,17 @@ For example, you can change these values for gRPC configs (you can change anythi }, } ``` +### supported network type +| network | support | +|--------------------------------|-----:| +| WebSocket | ✅ | +| gRPC | ✅ | +| http | ✅ | +| kcp | ❌ | +| tcp | ❌ | +| httpupgrade | ✅ | +| splithttp | ❌ | + ## How To Use First of all, you need to set a directory for all of your templates (home, subscription page, etc.). ```shell diff --git a/app/templates/v2ray/README.md b/app/templates/v2ray/README.md index fb2ce0d3f..5b0e393d3 100644 --- a/app/templates/v2ray/README.md +++ b/app/templates/v2ray/README.md @@ -20,6 +20,17 @@ For example, you can change these values for gRPC configs (you can change anythi } } ``` +### supported network type +| network | support | +|--------------------------------|-----:| +| WebSocket | ✅ | +| gRPC | ✅ | +| http | ✅ | +| kcp | ✅ | +| tcp | ✅ | +| httpupgrade | ✅ | +| splithttp | ✅ | + ## How To Use First of all, you need to set a directory for all of your templates (home, subscription page, etc.). ```shell From 94a3f28198d317ae9f5f9cb8c7c4ade1da79f5ee Mon Sep 17 00:00:00 2001 From: Random Guy <50927468+M03ED@users.noreply.github.com> Date: Tue, 23 Jul 2024 21:32:10 +0330 Subject: [PATCH 6/6] feat(subscription): add settings template for clash --- app/subscription/clash.py | 225 +++++++++++++++++++----------- app/subscription/singbox.py | 11 +- app/subscription/v2ray.py | 29 ++-- app/templates/clash/README.md | 59 ++++++++ app/templates/clash/settings.yml | 19 +++ app/templates/singbox/README.md | 27 ++-- app/templates/v2ray/README.md | 21 +-- app/templates/v2ray/settings.json | 5 + config.py | 1 + 9 files changed, 269 insertions(+), 128 deletions(-) create mode 100644 app/templates/clash/README.md create mode 100644 app/templates/clash/settings.yml diff --git a/app/subscription/clash.py b/app/subscription/clash.py index 3c24d1f3e..01645d509 100644 --- a/app/subscription/clash.py +++ b/app/subscription/clash.py @@ -1,3 +1,4 @@ +import copy import json from random import choice @@ -6,7 +7,8 @@ from app.subscription.funcs import get_grpc_gun from app.templates import render_template from config import ( - CLASH_SUBSCRIPTION_TEMPLATE, + CLASH_SUBSCRIPTION_TEMPLATE, + CLASH_SETTINGS_TEMPLATE, MUX_TEMPLATE, USER_AGENT_TEMPLATE, ) @@ -22,15 +24,16 @@ def __init__(self): } self.proxy_remarks = [] self.mux_template = render_template(MUX_TEMPLATE) - temp_user_agent_data = render_template(USER_AGENT_TEMPLATE) - user_agent_data = json.loads(temp_user_agent_data) + user_agent_data = json.loads(render_template(USER_AGENT_TEMPLATE)) if 'list' in user_agent_data and isinstance(user_agent_data['list'], list): self.user_agent_list = user_agent_data['list'] else: self.user_agent_list = [] - del temp_user_agent_data, user_agent_data + self.settings = yaml.load(render_template(CLASH_SETTINGS_TEMPLATE), Loader=yaml.SafeLoader) + + del user_agent_data def render(self, reverse=False): if reverse: @@ -44,7 +47,7 @@ def render(self, reverse=False): Loader=yaml.SafeLoader ), sort_keys=False, - allow_unicode=True + allow_unicode=True, ) def __str__(self) -> str: @@ -63,6 +66,82 @@ def _remark_validation(self, remark): return new c += 1 + def http_config( + self, + path="", + host="", + random_user_agent: bool = False, + ): + config = copy.deepcopy(self.settings.get("http-opts", { + 'headers': {} + })) + + if path: + config["path"] = [path] + if host: + config["Host"] = host + if random_user_agent: + if "headers" not in config: + config["headers"] = {} + config["header"]["User-Agent"] = choice(self.user_agent_list) + + return config + + def ws_config( + self, + path="", + host="", + max_early_data=None, + early_data_header_name="", + is_httpupgrade: bool =False, + random_user_agent: bool = False, + ): + config = copy.deepcopy(self.settings.get("ws-opts", {})) + if (host or random_user_agent) and "headers" not in config: + config["headers"] = {} + if path: + config["path"] = path + if host: + config["headers"]["Host"] = host + if random_user_agent: + config["headers"]["User-Agent"] = choice(self.user_agent_list) + if max_early_data: + config["max-early-data"] = max_early_data + config["early-data-header-name"] = early_data_header_name + if is_httpupgrade: + config["v2ray-http-upgrade"] = True + if max_early_data: + config["v2ray-http-upgrade-fast-open"] = True + + return config + + def grpc_config(self, path=""): + config = copy.deepcopy(self.settings.get("grpc-opts", {})) + if path: + config["grpc-service-name"] = path + + return config + + def h2_config(self, path="", host=""): + config = copy.deepcopy(self.settings.get("h2-opts", {})) + if path: + config["path"] = path + if host: + config["host"] = [host] + + return config + + def tcp_config(self, path="", host=""): + config = copy.deepcopy(self.settings.get("tcp-opts", {})) + if path: + config["path"] = [path] + if host: + if "headers" not in config: + config["headers"] = {} + config["headers"]["Host"] = host + + return config + def make_node(self, name: str, type: str, @@ -87,6 +166,11 @@ def make_node(self, type = 'ss' if network == 'tcp' and headers == 'http': network = 'http' + if network == 'httpupgrade': + network = 'ws' + is_httpupgrade = True + else: + is_httpupgrade = False remark = self._remark_validation(name) node = { @@ -95,7 +179,6 @@ def make_node(self, 'server': server, 'port': port, 'network': network, - f'{network}-opts': {}, 'udp': udp } @@ -106,6 +189,7 @@ def make_node(self, early_data_header_name = "Sec-WebSocket-Protocol" else: max_early_data = None + early_data_header_name = "" if type == 'ss': # shadowsocks return node @@ -121,54 +205,36 @@ def make_node(self, if ais: node['skip-cert-verify'] = ais - net_opts = node[f'{network}-opts'] - if network == 'http': - if path: - net_opts['method'] = 'GET' - net_opts['path'] = [path] - if host: - net_opts['method'] = 'GET' - net_opts['Host'] = host - if random_user_agent: - net_opts['header'] = {"User-Agent": choice(self.user_agent_list)} - - if network == 'ws' or network == 'httpupgrade': - if path: - net_opts['path'] = path - if host or random_user_agent: - net_opts['headers'] = {} - if host: - net_opts['headers']["Host"] = host - if random_user_agent: - net_opts['headers']["User-Agent"] = choice(self.user_agent_list) - if max_early_data: - net_opts['max-early-data'] = max_early_data - net_opts['early-data-header-name'] = early_data_header_name - if network == 'httpupgrade': - net_opts['v2ray-http-upgrade'] = True - if max_early_data: - net_opts['v2ray-http-upgrade-fast-open'] = True - - if network == 'grpc' or network == 'gun': - if path: - net_opts['grpc-service-name'] = path - if random_user_agent: - net_opts['header'] = {"User-Agent": choice(self.user_agent_list)} - - if network == 'h2': - if path: - net_opts['path'] = path - if host: - net_opts['host'] = [host] - - if network == 'tcp': - if path: - net_opts['method'] = 'GET' - net_opts['path'] = [path] - if host: - net_opts['method'] = 'GET' - net_opts['headers'] = {"Host": host} + net_opts = self.http_config( + path=path, + host=host, + random_user_agent=random_user_agent, + ) + + elif network == 'ws': + net_opts = self.ws_config( + path=path, + host=host, + max_early_data=max_early_data, + early_data_header_name=early_data_header_name, + is_httpupgrade=is_httpupgrade, + random_user_agent=random_user_agent, + ) + + elif network == 'grpc' or network == 'gun': + net_opts = self.grpc_config(path=path) + + elif network == 'h2': + net_opts = self.h2_config(path=path, host=host) + + elif network == 'tcp': + net_opts = self.tcp_config(path=path, host=host) + + else: + net_opts = {} + + node[f'{network}-opts'] = net_opts mux_json = json.loads(self.mux_template) mux_config = mux_json["clash"] @@ -183,7 +249,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): # not supported by clash if inbound['network'] in ("kcp", "splithttp"): return - + node = self.make_node( name=remark, type=inbound['protocol'], @@ -197,8 +263,8 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): headers=inbound['header_type'], udp=True, alpn=inbound.get('alpn', ''), - ais=inbound.get('ais', ''), - mux_enable=inbound.get('mux_enable', ''), + ais=inbound.get('ais', False), + mux_enable=inbound.get('mux_enable', False), random_user_agent=inbound.get("random_user_agent") ) @@ -206,19 +272,19 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): node['uuid'] = settings['id'] node['alterId'] = 0 node['cipher'] = 'auto' - self.data['proxies'].append(node) - self.proxy_remarks.append(remark) - if inbound['protocol'] == 'trojan': + elif inbound['protocol'] == 'trojan': node['password'] = settings['password'] - self.data['proxies'].append(node) - self.proxy_remarks.append(remark) - if inbound['protocol'] == 'shadowsocks': + elif inbound['protocol'] == 'shadowsocks': node['password'] = settings['password'] node['cipher'] = settings['method'] - self.data['proxies'].append(node) - self.proxy_remarks.append(remark) + + else: + return + + self.data['proxies'].append(node) + self.proxy_remarks.append(remark) class ClashMetaConfiguration(ClashConfiguration): @@ -269,7 +335,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): # not supported by clash-meta if inbound['network'] in ("kcp", "splithttp"): return - + node = self.make_node( name=remark, type=inbound['protocol'], @@ -286,8 +352,8 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): fp=inbound.get('fp', ''), pbk=inbound.get('pbk', ''), sid=inbound.get('sid', ''), - ais=inbound.get('ais', ''), - mux_enable=inbound.get('mux_enable', ''), + ais=inbound.get('ais', False), + mux_enable=inbound.get('mux_enable', False), random_user_agent=inbound.get("random_user_agent") ) @@ -295,29 +361,22 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict): node['uuid'] = settings['id'] node['alterId'] = 0 node['cipher'] = 'auto' - self.data['proxies'].append(node) - self.proxy_remarks.append(remark) - if inbound['protocol'] == 'vless': + elif inbound['protocol'] == 'vless': node['uuid'] = settings['id'] if inbound['network'] in ('tcp', 'kcp') and inbound['header_type'] != 'http' and inbound['tls'] != 'none': node['flow'] = settings.get('flow', '') - self.data['proxies'].append(node) - self.proxy_remarks.append(remark) - - if inbound['protocol'] == 'trojan': + elif inbound['protocol'] == 'trojan': node['password'] = settings['password'] - if inbound['network'] in ('tcp', 'kcp') and inbound['header_type'] != 'http' and inbound['tls']: - node['flow'] = settings.get('flow', '') - - self.data['proxies'].append(node) - self.proxy_remarks.append(remark) - - if inbound['protocol'] == 'shadowsocks': + elif inbound['protocol'] == 'shadowsocks': node['password'] = settings['password'] node['cipher'] = settings['method'] - self.data['proxies'].append(node) - self.proxy_remarks.append(remark) + + else: + return + + self.data['proxies'].append(node) + self.proxy_remarks.append(remark) diff --git a/app/subscription/singbox.py b/app/subscription/singbox.py index 7eb37dc06..1fad0219f 100644 --- a/app/subscription/singbox.py +++ b/app/subscription/singbox.py @@ -15,21 +15,18 @@ class SingBoxConfiguration(str): def __init__(self): self.proxy_remarks = [] - template = render_template(SINGBOX_SUBSCRIPTION_TEMPLATE) - self.config = json.loads(template) + self.config = json.loads(render_template(SINGBOX_SUBSCRIPTION_TEMPLATE)) self.mux_template = render_template(MUX_TEMPLATE) - temp_user_agent_data = render_template(USER_AGENT_TEMPLATE) - user_agent_data = json.loads(temp_user_agent_data) + user_agent_data = json.loads(render_template(USER_AGENT_TEMPLATE)) if 'list' in user_agent_data and isinstance(user_agent_data['list'], list): self.user_agent_list = user_agent_data['list'] else: self.user_agent_list = [] - temp_settings = render_template(SINGBOX_SETTINGS_TEMPLATE) - self.settings = json.loads(temp_settings) + self.settings = json.loads(render_template(SINGBOX_SETTINGS_TEMPLATE)) - del temp_user_agent_data, user_agent_data, temp_settings + del user_agent_data def _remark_validation(self, remark): if not remark in self.proxy_remarks: diff --git a/app/subscription/v2ray.py b/app/subscription/v2ray.py index 447fa7ad9..584975cdc 100644 --- a/app/subscription/v2ray.py +++ b/app/subscription/v2ray.py @@ -397,26 +397,23 @@ def __init__(self): self.config = [] self.template = render_template(V2RAY_SUBSCRIPTION_TEMPLATE) self.mux_template = render_template(MUX_TEMPLATE) - temp_user_agent_data = render_template(USER_AGENT_TEMPLATE) - user_agent_data = json.loads(temp_user_agent_data) + user_agent_data = json.loads(render_template(USER_AGENT_TEMPLATE)) if 'list' in user_agent_data and isinstance(user_agent_data['list'], list): self.user_agent_list = user_agent_data['list'] else: self.user_agent_list = [] - temp_grpc_user_agent_data = render_template(GRPC_USER_AGENT_TEMPLATE) - grpc_user_agent_data = json.loads(temp_grpc_user_agent_data) + grpc_user_agent_data = json.loads(render_template(GRPC_USER_AGENT_TEMPLATE)) if 'list' in grpc_user_agent_data and isinstance(grpc_user_agent_data['list'], list): self.grpc_user_agent_data = grpc_user_agent_data['list'] else: self.grpc_user_agent_data = [] - temp_settings = render_template(V2RAY_SETTINGS_TEMPLATE) - self.settings = json.loads(temp_settings) + self.settings = json.loads(render_template(V2RAY_SETTINGS_TEMPLATE)) - del temp_user_agent_data, user_agent_data, temp_grpc_user_agent_data, grpc_user_agent_data, temp_settings + del user_agent_data, grpc_user_agent_data def add_config(self, remarks, outbounds): json_template = json.loads(self.template) @@ -562,25 +559,25 @@ def tcp_http_config(self, path=None, host=None, random_user_agent=None): return tcpSettings def h2_config(self, path=None, host=None, random_user_agent=None): - httpSettings = self.settings.get("httpSettings", { + h2Settings = self.settings.get("h2Settings", { "header": {} }) - if "header" not in httpSettings: - httpSettings["header"] = {} + if "header" not in h2Settings: + h2Settings["header"] = {} if path: - httpSettings["path"] = path + h2Settings["path"] = path else: - httpSettings["path"] = "" + h2Settings["path"] = "" if host: - httpSettings["host"] = [host] + h2Settings["host"] = [host] else: - httpSettings["host"] = [] + h2Settings["host"] = [] if random_user_agent: - httpSettings["headers"]["User-Agent"] = [ + h2Settings["headers"]["User-Agent"] = [ choice(self.user_agent_list)] - return httpSettings + return h2Settings def quic_config(self, path=None, host=None, header=None): quicSettings = self.settings.get("quicSettings", { diff --git a/app/templates/clash/README.md b/app/templates/clash/README.md new file mode 100644 index 000000000..7de4bc178 --- /dev/null +++ b/app/templates/clash/README.md @@ -0,0 +1,59 @@ +# Clash Template + +## Usage +- Can be used to send completely prepared config to users depend on your usage. + +## Config Template +- With the config template, you can change things like routing and rules. + +## Settings Template +You can change some values in custom configs depending on the streamSettings type that is not accessible directly from the dashboard. + +For example, you can change these values for http configs (you can change anything that is part of netSettings except those accessible from the dashboard). +```yaml +http-opts: + - ip-version: dual + method: "GET" + headers: + Connection: + - keep-alive +``` +### supported network type +| network | support | +|-------------|--------:| +| WebSocket | ✅ | +| gRPC | ✅ | +| http | ✅ | +| h2 | ✅ | +| kcp | ❌ | +| tcp | ✅ | +| httpupgrade | ♻️ | +| splithttp | ❌ | + +♻️ In clash httpupgrade it's part of WebSocket. + +## How To Use +First of all, you need to set a directory for all of your templates (home, subscription page, etc.). +```shell +CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/" +``` +Make sure you put all of your templates in this folder.\ +If you are using Docker, make sure Docker has access to this folder.\ +Then, we need to make a directory for our Clash template. +```shell +mkdir /var/lib/marzban/templates/v2ray +``` +After that, put your templates (config and settings) in the directory.\ +Now, change these variables with your files' names. +```shell +CLASH_SUBSCRIPTION_TEMPLATE = "clash/default.yml") +CLASH_SETTINGS_TEMPLATE = "clash/settings.yml") +``` +Now, restart your Marzban and enjoy. + +If you have already changed your env variables, and you want to just update the template files, there is no need to restart Marzban. + +## Docs +you can use these docs to find out how to modify template files + +[Mihomo Docs](https://wiki.metacubex.one/en/) \ No newline at end of file diff --git a/app/templates/clash/settings.yml b/app/templates/clash/settings.yml new file mode 100644 index 000000000..7a80181e6 --- /dev/null +++ b/app/templates/clash/settings.yml @@ -0,0 +1,19 @@ +http-opts: + ip-version: dual + method: "GET" + headers: + Connection: + - keep-alive + +grpc-opts: + ip-version: dual + +h2-opts: + ip-version: dual + +# ws and httpupgrade are same in clash +ws-opts: + ip-version: dual + +tcp-opts: + ip-version: dual diff --git a/app/templates/singbox/README.md b/app/templates/singbox/README.md index d5b467dab..95006320f 100644 --- a/app/templates/singbox/README.md +++ b/app/templates/singbox/README.md @@ -16,19 +16,22 @@ For example, you can change these values for gRPC configs (you can change anythi "idle_timeout": "15s", "ping_timeout": "15s", "permit_without_stream": false - }, + } } ``` ### supported network type -| network | support | -|--------------------------------|-----:| -| WebSocket | ✅ | -| gRPC | ✅ | -| http | ✅ | -| kcp | ❌ | -| tcp | ❌ | -| httpupgrade | ✅ | -| splithttp | ❌ | +| network | support | +|-------------|--------:| +| WebSocket | ✅ | +| gRPC | ✅ | +| http | ✅ | +| h2 | ♻️ | +| kcp | ❌ | +| tcp | ❌ | +| httpupgrade | ✅ | +| splithttp | ❌ | + +♻️ In Sing-box h2 it's part of http. ## How To Use First of all, you need to set a directory for all of your templates (home, subscription page, etc.). @@ -37,7 +40,7 @@ CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/" ``` Make sure you put all of your templates in this folder.\ If you are using Docker, make sure Docker has access to this folder.\ -Then, we need to make a directory for our V2ray template. +Then, we need to make a directory for our Sing-box template. ```shell mkdir /var/lib/marzban/templates/sing-box ``` @@ -49,7 +52,7 @@ SINGBOX_SETTINGS_TEMPLATE="singbox/settings.json" ``` Now, restart your Marzban and enjoy. -If you have already changed your env variables and you want to just update the template files, there is no need to restart Marzban. +If you have already changed your env variables, and you want to just update the template files, there is no need to restart Marzban. ## Docs you can use sing-box official documentation to find out how to modify template files diff --git a/app/templates/v2ray/README.md b/app/templates/v2ray/README.md index 5b0e393d3..243971e16 100644 --- a/app/templates/v2ray/README.md +++ b/app/templates/v2ray/README.md @@ -21,15 +21,16 @@ For example, you can change these values for gRPC configs (you can change anythi } ``` ### supported network type -| network | support | -|--------------------------------|-----:| -| WebSocket | ✅ | -| gRPC | ✅ | -| http | ✅ | -| kcp | ✅ | -| tcp | ✅ | -| httpupgrade | ✅ | -| splithttp | ✅ | +| network | support | +|-------------|--------:| +| WebSocket | ✅ | +| gRPC | ✅ | +| http | ✅ | +| h2 | ✅ | +| kcp | ✅ | +| tcp | ✅ | +| httpupgrade | ✅ | +| splithttp | ✅ | ## How To Use First of all, you need to set a directory for all of your templates (home, subscription page, etc.). @@ -50,7 +51,7 @@ V2RAY_SETTINGS_TEMPLATE="v2ray/settings.json" ``` Now, restart your Marzban and enjoy. -If you have already changed your env variables and you want to just update the template files, there is no need to restart Marzban. +If you have already changed your env variables, and you want to just update the template files, there is no need to restart Marzban. ## Docs you can use these docs to find out how to modify template files diff --git a/app/templates/v2ray/settings.json b/app/templates/v2ray/settings.json index 2327ff8b4..89e43895f 100644 --- a/app/templates/v2ray/settings.json +++ b/app/templates/v2ray/settings.json @@ -48,6 +48,11 @@ "Pragma": "no-cache" } }, + "h2Settings": { + "headers": { + "Pragma": "no-cache" + } + }, "httpupgradeSettings": { "headers": { "Pragma": "no-cache" diff --git a/config.py b/config.py index 5623705dc..5c83a1eee 100755 --- a/config.py +++ b/config.py @@ -48,6 +48,7 @@ HOME_PAGE_TEMPLATE = config("HOME_PAGE_TEMPLATE", default="home/index.html") CLASH_SUBSCRIPTION_TEMPLATE = config("CLASH_SUBSCRIPTION_TEMPLATE", default="clash/default.yml") +CLASH_SETTINGS_TEMPLATE = config("CLASH_SETTINGS_TEMPLATE", default="clash/settings.yml") SINGBOX_SUBSCRIPTION_TEMPLATE = config("SINGBOX_SUBSCRIPTION_TEMPLATE", default="singbox/default.json") SINGBOX_SETTINGS_TEMPLATE = config("SINGBOX_SETTINGS_TEMPLATE", default="singbox/settings.json")