Убрал кнопку Файл
This commit is contained in:
parent
b82bd53a1d
commit
7d181e0da1
152
mihomo_editor.py
152
mihomo_editor.py
|
|
@ -12,7 +12,6 @@ import time
|
||||||
import shutil
|
import shutil
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import yaml
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# --- НАСТРОЙКИ ---
|
# --- НАСТРОЙКИ ---
|
||||||
|
|
@ -98,87 +97,120 @@ def parse_vless(link, custom_name=None):
|
||||||
|
|
||||||
def parse_wireguard(config_text, custom_name=None):
|
def parse_wireguard(config_text, custom_name=None):
|
||||||
try:
|
try:
|
||||||
name = "WireGuard"
|
# Простой парсер INI формата (чтобы не использовать configparser или yaml)
|
||||||
params = {}
|
conf = {"interface": {}, "peer": {}}
|
||||||
current_section = None
|
section = None
|
||||||
|
|
||||||
for line in config_text.splitlines():
|
for line in config_text.splitlines():
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line or line.startswith('#'):
|
if not line or line.startswith('#') or line.startswith(';'): continue
|
||||||
continue
|
|
||||||
if line.startswith('[') and line.endswith(']'):
|
if line.startswith('[') and line.endswith(']'):
|
||||||
current_section = line[1:-1].lower()
|
s_name = line[1:-1].lower()
|
||||||
|
if s_name == 'interface' or s_name == 'peer':
|
||||||
|
section = s_name
|
||||||
|
else:
|
||||||
|
section = None
|
||||||
continue
|
continue
|
||||||
if '=' in line:
|
|
||||||
key, value = map(str.strip, line.split('=', 1))
|
|
||||||
if current_section:
|
|
||||||
if current_section not in params:
|
|
||||||
params[current_section] = {}
|
|
||||||
params[current_section][key] = value
|
|
||||||
|
|
||||||
if 'interface' not in params or 'peer' not in params:
|
if section and '=' in line:
|
||||||
return None, "Invalid WireGuard config: missing [Interface] or [Peer] section."
|
key, val = line.split('=', 1)
|
||||||
|
conf[section][key.strip().lower()] = val.strip()
|
||||||
|
|
||||||
interface = params.get('interface', {})
|
iface = conf['interface']
|
||||||
peer = params.get('peer', {})
|
peer = conf['peer']
|
||||||
|
|
||||||
server, port_str = peer.get('Endpoint', ':').rsplit(':', 1)
|
if not iface or not peer:
|
||||||
|
return None, "Invalid WireGuard config: missing Interface or Peer"
|
||||||
|
|
||||||
|
# Endpoint parsing
|
||||||
|
endpoint = peer.get('endpoint', '')
|
||||||
|
if not endpoint: return None, "No Endpoint found"
|
||||||
|
|
||||||
|
# Обработка IPv6 в Endpoint [::1]:port
|
||||||
|
if ']:' in endpoint:
|
||||||
|
server = endpoint.split(']:')[0][1:]
|
||||||
|
port = endpoint.split(']:')[1]
|
||||||
|
elif ':' in endpoint:
|
||||||
|
server, port = endpoint.rsplit(':', 1)
|
||||||
|
else:
|
||||||
|
return None, "Invalid Endpoint format"
|
||||||
|
|
||||||
|
# Name logic
|
||||||
|
name = "WireGuard"
|
||||||
if custom_name:
|
if custom_name:
|
||||||
name = custom_name
|
name = custom_name
|
||||||
else:
|
else:
|
||||||
name_match = re.search(r'#\s*(.+)', config_text.splitlines()[0])
|
# Пытаемся вытащить имя из первой строки комментария, если есть
|
||||||
if name_match:
|
first_line = config_text.splitlines()[0].strip()
|
||||||
name = name_match.group(1).strip()
|
if first_line.startswith('#') and len(first_line) > 2:
|
||||||
elif server:
|
name = first_line[1:].strip()
|
||||||
name = f"WG_{server}"
|
|
||||||
else:
|
else:
|
||||||
name = "WireGuard"
|
name = f"WG_{server}"
|
||||||
|
|
||||||
# Преобразуем порт в int, если возможно, иначе оставляем как есть
|
# IP Parsing (Mihomo requires 'ip', usually stripped of CIDR)
|
||||||
try:
|
# Address = 10.0.0.2/32, fd00::2/64
|
||||||
port = int(port_str)
|
address_raw = iface.get('address', '')
|
||||||
except (ValueError, TypeError):
|
if not address_raw: return None, "No Address found"
|
||||||
port = port_str
|
|
||||||
|
|
||||||
ip_address = interface.get("Address", "").split("/")[0]
|
# Берем первый адрес до запятой и убираем маску
|
||||||
|
ip_addr = address_raw.split(',')[0].strip().split('/')[0]
|
||||||
|
|
||||||
proxy_dict = {
|
# Строим YAML вручную (без модуля yaml)
|
||||||
'name': name,
|
y = []
|
||||||
'type': 'wireguard',
|
y.append(f'- name: "{name}"')
|
||||||
'server': server,
|
y.append(f' type: wireguard')
|
||||||
'port': port,
|
y.append(f' server: {server}')
|
||||||
'private-key': interface.get("PrivateKey"),
|
y.append(f' port: {port}')
|
||||||
'public-key': peer.get("PublicKey"),
|
y.append(f' ip: {ip_addr}')
|
||||||
'ip': ip_address
|
|
||||||
}
|
|
||||||
|
|
||||||
if interface.get('DNS'):
|
pk = iface.get('privatekey')
|
||||||
proxy_dict['dns'] = [d.strip() for d in interface.get('DNS').split(',')]
|
if pk: y.append(f' private-key: {pk}')
|
||||||
|
|
||||||
if peer.get('AllowedIPs'):
|
pubk = peer.get('publickey')
|
||||||
proxy_dict['allowed-ips'] = [ip.strip() for ip in peer.get('AllowedIPs').split(',')]
|
if pubk: y.append(f' public-key: {pubk}')
|
||||||
|
|
||||||
if peer.get('PresharedKey'):
|
psk = peer.get('presharedkey')
|
||||||
proxy_dict['preshared-key'] = peer.get("PresharedKey")
|
if psk: y.append(f' preshared-key: {psk}')
|
||||||
|
|
||||||
if peer.get('PersistentKeepalive'):
|
# DNS (JSON array is valid YAML flow style)
|
||||||
proxy_dict['keep-alive'] = int(peer.get("PersistentKeepalive"))
|
dns_raw = iface.get('dns')
|
||||||
|
if dns_raw:
|
||||||
|
dns_list = [d.strip() for d in dns_raw.split(',')]
|
||||||
|
y.append(f' dns: {json.dumps(dns_list)}')
|
||||||
|
|
||||||
amnezia_keys = ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4']
|
mtu = iface.get('mtu')
|
||||||
amnezia_opts = {key: interface.get(key) for key in amnezia_keys if interface.get(key) is not None}
|
if mtu: y.append(f' mtu: {mtu}')
|
||||||
|
|
||||||
if amnezia_opts:
|
y.append(' udp: true')
|
||||||
proxy_dict['amnezia-wg-option'] = {k: v for k, v in amnezia_opts.items()}
|
|
||||||
|
|
||||||
# Создаем YAML, который будет добавлен в `proxies:`
|
# AmneziaWG specific options (Mihomo structure)
|
||||||
# Используем NoQuoteDumper для вывода без кавычек, где это возможно
|
# Ключи обычно: Jc, Jmin, Jmax, S1, S2, H1, H2, H3, H4
|
||||||
yaml_string = yaml.dump([proxy_dict], default_flow_style=False, sort_keys=False, allow_unicode=True, indent=2)
|
amnezia_keys = ['jc', 'jmin', 'jmax', 's1', 's2', 'h1', 'h2', 'h3', 'h4']
|
||||||
|
amn_opts = {}
|
||||||
|
for k in amnezia_keys:
|
||||||
|
if k in iface:
|
||||||
|
val = iface[k]
|
||||||
|
# Пытаемся преобразовать в число, если это число
|
||||||
|
if val.isdigit(): val = int(val)
|
||||||
|
amn_opts[k] = val
|
||||||
|
|
||||||
# Удаляем `- ` с первой строки, т.к. мы добавляем только один прокси за раз
|
if amn_opts:
|
||||||
# и обертка в список нужна только для yaml.dump
|
y.append(' amnezia-wg-option:')
|
||||||
final_yaml = yaml_string.replace('- ', ' ', 1).replace(' name:', '- name:')
|
for k, v in amn_opts.items():
|
||||||
|
y.append(f' {k}: {v}')
|
||||||
|
|
||||||
return {"yaml": final_yaml.strip(), "name": name}, None
|
# AllowedIPs (опционально для клиента, но иногда полезно для routing rules)
|
||||||
|
allowed = peer.get('allowedips')
|
||||||
|
if allowed:
|
||||||
|
al_list = [x.strip() for x in allowed.split(',')]
|
||||||
|
y.append(f' allowed-ips: {json.dumps(al_list)}')
|
||||||
|
|
||||||
|
ka = peer.get('persistentkeepalive')
|
||||||
|
if ka:
|
||||||
|
y.append(f' keep-alive: {ka}')
|
||||||
|
|
||||||
|
return {"yaml": "\n".join(y), "name": name}, None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return None, str(e)
|
return None, str(e)
|
||||||
|
|
@ -257,7 +289,7 @@ HTML_TEMPLATE = """<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||||
<title>Mihomo Editor v18.4</title>
|
<title>Mihomo Editor v18.5</title>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.7/ace.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.7/ace.js"></script>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
|
@ -402,7 +434,7 @@ button:hover{filter:brightness(1.1)}
|
||||||
<div class="hdr">
|
<div class="hdr">
|
||||||
<div style="display:flex;align-items:center;gap:10px">
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
<h2 style="margin:0;color:#4caf50">Mihomo Studio</h2>
|
<h2 style="margin:0;color:#4caf50">Mihomo Studio</h2>
|
||||||
<span style="color:var(--txt-sec);font-size:12px">v18.4 Auto-Panel</span>
|
<span style="color:var(--txt-sec);font-size:12px">v18.5 Auto-Panel</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="last-load">Loaded: __TIME__</div>
|
<div id="last-load">Loaded: __TIME__</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue