From 35ca17270232723b329d57af9a914d6de7accb1b Mon Sep 17 00:00:00 2001 From: wumode Date: Sat, 28 Jun 2025 16:13:09 +0800 Subject: [PATCH 1/2] =?UTF-8?q?update(ClashRuleProvider):=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=AE=A1=E7=90=86=E5=A4=9A=E4=B8=AA=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.v2.json | 3 +- plugins.v2/clashruleprovider/__init__.py | 243 ++-- .../clashruleprovider/clash_rule_parser.py | 102 +- .../__federation_expose_Config-BDDunIfC.css | 4 + .../__federation_expose_Config-r5zwvDwv.js | 1029 +++++++++++++++++ .../__federation_expose_Config-sTz3W3yr.js | 920 --------------- .../__federation_expose_Config-xrxN2F1l.css | 5 - ...__federation_expose_Dashboard-BFVr4jq_.js} | 28 +- ..._federation_expose_Dashboard-D6WU_Ejn.css} | 2 +- ... => __federation_expose_Page-DIAzDhN_.css} | 22 +- ...s => __federation_expose_Page-gjHBCfnd.js} | 735 +++++++----- .../dist/assets/remoteEntry.js | 12 +- 12 files changed, 1739 insertions(+), 1366 deletions(-) create mode 100644 plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BDDunIfC.css create mode 100644 plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-r5zwvDwv.js delete mode 100644 plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-sTz3W3yr.js delete mode 100644 plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-xrxN2F1l.css rename plugins.v2/clashruleprovider/dist/assets/{__federation_expose_Dashboard-DEFRy9WP.js => __federation_expose_Dashboard-BFVr4jq_.js} (99%) rename plugins.v2/clashruleprovider/dist/assets/{__federation_expose_Dashboard-IipjE6HA.css => __federation_expose_Dashboard-D6WU_Ejn.css} (51%) rename plugins.v2/clashruleprovider/dist/assets/{__federation_expose_Page-DQjiFgWw.css => __federation_expose_Page-DIAzDhN_.css} (67%) rename plugins.v2/clashruleprovider/dist/assets/{__federation_expose_Page--ZdI8TQS.js => __federation_expose_Page-gjHBCfnd.js} (89%) diff --git a/package.v2.json b/package.v2.json index 83f52bf..ac3314a 100644 --- a/package.v2.json +++ b/package.v2.json @@ -450,11 +450,12 @@ "name": "Clash Rule Provider", "description": "随时为Clash添加一些额外的规则。", "labels": "工具", - "version": "1.1.3", + "version": "1.2.0", "icon": "Mihomo_Meta_A.png", "author": "wumode", "level": 1, "history": { + "v1.2.0": "支持管理多个订阅; 支持导入配置模板和 V2Ray 链接; 优化界面", "v1.1.3": "添加仪表盘组件", "v1.1.1": "支持解析 V2ray 订阅", "v1.1.0": "支持规则集合; 添加ACL4SSR规则集; 配置说明", diff --git a/plugins.v2/clashruleprovider/__init__.py b/plugins.v2/clashruleprovider/__init__.py index 2bb5651..b3c5e7b 100644 --- a/plugins.v2/clashruleprovider/__init__.py +++ b/plugins.v2/clashruleprovider/__init__.py @@ -2,6 +2,8 @@ import json import re from typing import Any, Optional, List, Dict, Tuple, Union import time +from urllib.parse import urlparse + import yaml import hashlib from datetime import datetime, timedelta @@ -13,7 +15,6 @@ from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger import httpx import asyncio -import json from fastapi import HTTPException, Request, status, Body, Response import websockets from sse_starlette.sse import EventSourceResponse @@ -37,7 +38,7 @@ class ClashRuleProvider(_PluginBase): # 插件图标 plugin_icon = "Mihomo_Meta_A.png" # 插件版本 - plugin_version = "1.1.3" + plugin_version = "1.2.0" # 插件作者 plugin_author = "wumode" # 作者主页 @@ -73,9 +74,11 @@ class ClashRuleProvider(_PluginBase): _discard_rules: bool = False _enable_acl4ssr: bool = False _dashboard_components: List[str] = [] + _clash_template_yaml = '' # 插件数据 - _clash_config: Optional[Dict[str, Any]] = None + # 综合多个订阅的配置 + # _clash_config: Optional[Dict[str, Any]] = None _top_rules: List[str] = [] _ruleset_rules: List[str] = [] _rule_provider: Dict[str, Any] = {} @@ -86,27 +89,30 @@ class ClashRuleProvider(_PluginBase): _extra_proxies = [] _acl4ssr_providers: Dict[str, Any] = {} _acl4ssr_prefix: str = '🗂️=>' + # 保存每个订阅文件的原始内容 + _clash_configs: Dict[str, Any] = {} # protected variables _clash_rule_parser = None _ruleset_rule_parser = None _custom_rule_sets = None + _clash_template: Optional[Dict[str, Any]] = None _scheduler: Optional[BackgroundScheduler] = None _countries: Optional[List[Dict[str, str]]] = None _proxy_groups_by_region: List[Dict[str, Any]] = [] def init_plugin(self, config: dict = None): - self._clash_config = self.get_data("clash_config") + # self._clash_config = self.get_data("clash_config") self._ruleset_rules = self.get_data("ruleset_rules") self._top_rules = self.get_data("top_rules") self._proxy_groups = self.get_data("proxy_groups") or [] self._extra_proxies = self.get_data("extra_proxies") or [] - self._subscription_info = self.get_data("subscription_info") or \ - {"download": 0, "upload": 0, "total": 0, "expire": 0, "last_update": 0} + self._subscription_info = self.get_data("subscription_info") or {} self._rule_provider = self.get_data("rule_provider") or {} self._extra_rule_providers = self.get_data("extra_rule_providers") or {} self._ruleset_names = self.get_data("ruleset_names") or {} self._acl4ssr_providers = self.get_data("acl4ssr_providers") or {} + self._clash_configs = self.get_data("clash_configs") or {} if config: self._enabled = config.get("enabled") self._proxy = config.get("proxy") @@ -134,19 +140,30 @@ class ClashRuleProvider(_PluginBase): self._discard_rules = config.get("discard_rules") or False self._enable_acl4ssr = config.get("enable_acl4ssr") or False self._dashboard_components = config.get("dashboard_components") or [] + self._clash_template_yaml = config.get("clash_template") or '' self._clash_rule_parser = ClashRuleParser() self._ruleset_rule_parser = ClashRuleParser() if self._enabled: + if self._clash_template_yaml: + self._clash_template = {} + try: + self._clash_template = yaml.load(self._clash_template_yaml, Loader=yaml.SafeLoader) + except yaml.YAMLError as exc: + logger.error(f"Error loading clash template yaml: {exc}") if self._group_by_region: self._countries = ClashRuleProvider.__load_countries( f"{settings.ROOT_PATH}/app/plugins/clashruleprovider/countries.json") self._proxy_groups_by_region = ClashRuleProvider.__group_by_region(self._countries, - self._clash_config.get('proxies')) + self.all_proxies()) self.__parse_config() + # 清理不存在的 URL + self._subscription_info = {url: self._subscription_info.get(url) + for url in self._sub_links if self._subscription_info.get(url)} + self._clash_configs = {url: self._clash_configs[url] for url in self._sub_links if self._clash_configs.get(url)} self._scheduler = BackgroundScheduler(timezone=settings.TZ) self._scheduler.start() # 更新订阅 - self._scheduler.add_job(self.__refresh_subscription, "date", + self._scheduler.add_job(self.refresh_subscriptions, "date", run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=2)) # 更新acl4ssr if self._enable_acl4ssr: @@ -155,6 +172,7 @@ class ClashRuleProvider(_PluginBase): else: self._acl4ssr_providers = {} + def get_state(self) -> bool: return self._enabled @@ -236,14 +254,6 @@ class ClashRuleProvider(_PluginBase): "summary": "删除一条规则", "description": "删除一条规则" }, - { - "path": "/subscription", - "endpoint": self.get_subscription, - "methods": ["GET"], - "auth": "bear", - "summary": "获取原订阅链接", - "description": "获取原订阅链接" - }, { "path": "/subscription", "endpoint": self.refresh_subscription, @@ -455,7 +465,6 @@ class ClashRuleProvider(_PluginBase): self.__insert_ruleset() self._top_rules = self._clash_rule_parser.to_list() self._ruleset_rules = self._ruleset_rule_parser.to_list() - self.save_data('clash_config', self._clash_config) self.save_data('ruleset_rules', self._ruleset_rules) self.save_data('top_rules', self._top_rules) self.save_data('subscription_info', self._subscription_info) @@ -465,6 +474,7 @@ class ClashRuleProvider(_PluginBase): self.save_data('extra_proxies', self._extra_proxies) self.save_data('extra_rule_providers', self._extra_rule_providers) self.save_data('acl4ssr_providers', self._acl4ssr_providers) + self.save_data('clash_configs', self._clash_configs) def __parse_config(self): if self._top_rules is None: @@ -555,11 +565,12 @@ class ClashRuleProvider(_PluginBase): return Response(content=res, media_type="text/yaml") def get_clash_outbound(self) -> schemas.Response: - outbound = self.clash_outbound(self._clash_config) + outbound = self.clash_outbound() return schemas.Response(success=True, message="", data={"outbound": outbound}) def get_status(self): - rule_size = len(self._clash_config.get("rules", [])) if self._clash_config else 0 + first_config = self._clash_configs.get(self._sub_links[0], {}) if self._sub_links else {} + rule_size = len(first_config.get("rules", [])) return {"success": True, "message": "", "data": {"state": self._enabled, "ruleset_prefix": self._ruleset_prefix, @@ -574,10 +585,14 @@ class ClashRuleProvider(_PluginBase): if not config: return {'success': False, "message": ''} res = yaml.dump(config, allow_unicode=True) - headers = {'Subscription-Userinfo': f'upload={self._subscription_info["upload"]}; ' - f'download={self._subscription_info["download"]}; ' - f'total={self._subscription_info["total"]}; ' - f'expire={self._subscription_info["expire"]}'} + first_url = self._sub_links[0] if self._sub_links else None + if not first_url: + return {'success': False, "message": ''} + sub_info = self._subscription_info.get(first_url, {}) + headers = {'Subscription-Userinfo': f'upload={sub_info.get("upload", 0)}; ' + f'download={sub_info.get("download", 0)}; ' + f'total={sub_info.get("total", 0)}; ' + f'expire={sub_info.get("expire", 0)}'} return Response(headers=headers, content=res, media_type="text/yaml") def get_rules(self, rule_type: str) -> schemas.Response: @@ -658,20 +673,19 @@ class ClashRuleProvider(_PluginBase): res = self.add_rule_by_priority(params.get('rule_data'), self._clash_rule_parser) return schemas.Response(success=bool(res), message='') - def get_subscription(self) -> schemas.Response: - if not self._sub_links: - return schemas.Response(success=False, message=f"Invalid subscription links: {self._sub_links}") - return schemas.Response(success=True, data={"url": self._sub_links[0]}) - def refresh_subscription(self, params: Dict[str, Any]): if not self._enabled: return schemas.Response(success=False, message="") url = params.get('url') if not url: return schemas.Response(success=False, message="missing params") - res = self.__refresh_subscription() - if not res: - return schemas.Response(success=False, message=f"订阅链接 {self._sub_links[0]} 更新失败") + config, info = self.__get_subscription(url) + if not config: + return schemas.Response(success=False, message=f"订阅链接 {url} 更新失败") + self._clash_configs[url] = config + self._subscription_info[url] = info + self.save_data('clash_configs', self._clash_configs) + self.save_data('subscription_info', self._subscription_info) return schemas.Response(success=True, message='订阅更新成功') def get_rule_providers(self) -> schemas.Response: @@ -741,11 +755,19 @@ class ClashRuleProvider(_PluginBase): try: imported_proxies = yaml.load(params["payload"], Loader=yaml.SafeLoader) extra_proxies = imported_proxies.get("proxies", []) - except yaml.YAMLError as err: + except Exception as err: return schemas.Response(success=False, message=f'YAML error: {err}') + elif params.get('type') == 'LINK': + try: + links = params['payload'].strip().splitlines() + extra_proxies = Converter.convert_v2ray(v2ray_link=links) + except Exception as err: + return schemas.Response(success=False, message=f'LINK error: {err}') + if not extra_proxies: + return schemas.Response(success=False, message='无可用节点') for proxy in extra_proxies: name = proxy.get('name') - if not name or any(x.get('name') == name for x in self.clash_outbound(self._clash_config)): + if not name or any(x.get('name') == name for x in self.clash_outbound()): logger.warning(f"The proxy name {proxy['name']} already exists. Skipping...") continue required_fields = {'name', 'type', 'server', 'port'} @@ -798,22 +820,20 @@ class ClashRuleProvider(_PluginBase): self.save_data('proxy_groups', self._proxy_groups) return schemas.Response(success=True, message='') - def clash_outbound(self, clash_config: Dict[str, Any]) -> Optional[List]: - if not clash_config: - return [] - outbound = [{'name': proxy_group.get("name")} for proxy_group in clash_config.get("proxy-groups")] - outbound.extend([{'name': proxy.get("name")} for proxy in clash_config.get("proxies")]) + def clash_outbound(self) -> Optional[List]: + first_config = self._clash_configs.get(self._sub_links[0], {}) if self._sub_links else {} + outbound = [{'name': proxy_group.get("name")} for proxy_group in first_config.get("proxy-groups", [])] + outbound.extend([{'name': proxy.get("name")} for proxy in first_config.get("proxies", [])]) if self._group_by_region: outbound.extend([{'name': proxy_group.get("name")} for proxy_group in self._proxy_groups_by_region]) outbound.extend([{'name': proxy.get("name")} for proxy in self._extra_proxies]) outbound.extend([{'name': proxy_group.get("name")} for proxy_group in self._proxy_groups]) return outbound - def rule_providers(self) -> Optional[Dict[str, Any]]: - if not self._clash_config: - return None + def rule_providers(self) -> Dict[str, Any]: + first_config = self._clash_configs.get(self._sub_links[0], {}) if self._sub_links else {} rule_providers = {} - for key, value in self._clash_config.get('rule-providers', {}).items(): + for key, value in first_config.get('rule-providers', {}).items(): rule_providers[key] = value for key, value in self._extra_rule_providers.items(): rule_providers[key] = value @@ -921,21 +941,34 @@ class ClashRuleProvider(_PluginBase): return f"{days}天后过期" if days > 0 else "已过期" def refresh_subscription_service(self): - res = self.__refresh_subscription() - if res: - used = self._subscription_info['download'] + self._subscription_info['upload'] - remaining = self._subscription_info['total'] - used - message = (f"订阅更新成功\n" - f"已用流量: {ClashRuleProvider.format_bytes(used)}\n" - f"剩余流量: {ClashRuleProvider.format_bytes(remaining)}\n" - f"总量: {ClashRuleProvider.format_bytes(self._subscription_info['total'])}\n" - f"过期时间: {ClashRuleProvider.format_expire_time(self._subscription_info['expire'])}") - else: - message = "订阅更新失败" + res = self.refresh_subscriptions() + messages = [] + for url, result in res.items(): + try: + host_name = urlparse(url).hostname + except ValueError: + host_name = url + message = f"1. 「 {host_name} 」\n" + if result: + sub_info = self._subscription_info.get(url, {}) + if sub_info.get('total') is not None: + used = sub_info.get('download', 0) + sub_info.get('upload', 0) + remaining = sub_info.get('total', 0) - used + info = (f"节点数量: {sub_info.get('proxy_num', 0)}\n" + f"已用流量: {ClashRuleProvider.format_bytes(used)}\n" + f"剩余流量: {ClashRuleProvider.format_bytes(remaining)}\n" + f"总量: {ClashRuleProvider.format_bytes(sub_info.get('total', 0))}\n" + f"过期时间: {ClashRuleProvider.format_expire_time(sub_info.get('expire', 0))}") + else: + info = "" + message += f"订阅更新成功\n{info}" + else: + message += '订阅更新失败' + messages.append(message) if self._notify: self.post_message(title=f"【{self.plugin_name}】", mtype=NotificationType.Plugin, - text=f"{message}" + text='\n'.join(messages) ) def __refresh_acl4ssr(self): @@ -962,11 +995,31 @@ class ClashRuleProvider(_PluginBase): self._acl4ssr_providers[name] = provider self.save_data('acl4ssr_providers', self._acl4ssr_providers) - def __refresh_subscription(self) -> bool: - if not self._sub_links: - logger.error(f"Invalid links: {self._sub_links}") - return False - url = self._sub_links[0] + def refresh_subscriptions(self) -> Dict[str, bool]: + """ + 更新全部订阅链接 + """ + all_proxies = [] + res = {} + for index, url in enumerate(self._sub_links): + config, sub_info = self.__get_subscription(url) + if not config: + res[url] = False + continue + res[url] = True + self._clash_configs[url] = config + all_proxies.extend(config.get("proxies", [])) + self._subscription_info[url] = sub_info + self.save_data('subscription_info', self._subscription_info) + self.save_data('clash_configs', self._clash_configs) + self._proxy_groups_by_region = ClashRuleProvider.__group_by_region(self._countries, + all_proxies) + return res + + def __get_subscription(self, url: str) -> Tuple[Optional[Dict[str, Any]], Optional[Dict[str, Any]]]: + if not url: + logger.error(f"Invalid links: {url}") + return None, None logger.info(f"正在更新: {url}") ret = None for i in range(0, self._retry_times): @@ -976,9 +1029,9 @@ class ClashRuleProvider(_PluginBase): if ret: break if not ret: - return False + return None, None try: - rs = yaml.load(ret.content, Loader=yaml.FullLoader) + rs: Dict[str, Any] = yaml.load(ret.content, Loader=yaml.FullLoader) if type(rs) is str: all_proxies = {'name': "All Proxies", 'type': 'select', 'include-all-proxies': True} proxies = Converter.convert_v2ray(ret.content) @@ -990,25 +1043,27 @@ class ClashRuleProvider(_PluginBase): rs['rules'] = [] if self._discard_rules: rs['rules'] = [] - self._clash_config = self.__remove_nodes_by_keywords(rs) + rs = self.__remove_nodes_by_keywords(rs) except Exception as e: logger.error(f"解析配置出错: {e}") - return False + return None, None + + sub_info = {'last_update': int(time.time()), 'proxy_num': len(rs.get('proxies', []))} if 'Subscription-Userinfo' in ret.headers: matches = re.findall(r'(\w+)=(\d+)', ret.headers['Subscription-Userinfo']) variables = {key: int(value) for key, value in matches} - self._subscription_info['download'] = variables['download'] - self._subscription_info['upload'] = variables['upload'] - self._subscription_info['total'] = variables['total'] - self._subscription_info['expire'] = variables['expire'] - self._subscription_info["last_update"] = int(time.time()) - self._proxy_groups_by_region = ClashRuleProvider.__group_by_region(self._countries, - self._clash_config.get('proxies')) - self.save_data('subscription_info', self._subscription_info) - self.save_data('clash_config', self._clash_config) - return True + sub_info.update({ + 'download': variables['download'], + 'upload': variables['upload'], + 'total': variables['total'], + 'expire': variables['expire'] + }) + return rs, sub_info def notify_clash(self, ruleset: str): + """ + 通知 Clash 刷新规则集 + """ url = f'{self._clash_dashboard_url}/providers/rules/{ruleset}' RequestUtils(content_type="application/json", headers={"authorization": f"Bearer {self._clash_dashboard_secret}"} @@ -1086,15 +1141,42 @@ class ClashRuleProvider(_PluginBase): proxy_group['proxies'] = [x for x in proxy_group.get('proxies', []) if x not in removed_proxies] return clash_config + def all_proxies(self) -> List[Dict[str, Any]]: + """ + 所有出站代理 + """ + all_proxies = [] + for index, url in enumerate(self._sub_links): + config = self._clash_configs.get(url, {}) + all_proxies.extend(config.get("proxies", [])) + return all_proxies + def clash_config(self) -> Optional[Dict[str, Any]]: """ - 整理clash配置,返回配置字典 + 整理 clash 配置,返回配置字典 """ - if not self._clash_config: - return None + # 使用模板或第一个订阅 + first_config = self._clash_configs.get(self._sub_links[0], {}) if self._sub_links else {} + proxies =[] + if not self._clash_template: + clash_config = copy.deepcopy(first_config) + else: + clash_config = copy.deepcopy(self._clash_template) + proxies.extend(self._clash_template.get('proxies')) + clash_config['proxy-groups'] = (first_config.get('proxy-groups', []) + + clash_config.get('proxy-groups', [])) + clash_config['rule-providers'] = first_config.get('rule-providers', {}).update(clash_config.get('rule-providers', {})) + + + for proxy in (self._extra_proxies + self.all_proxies()) : + if any(p.get('name') == proxy.get('name', '') for p in proxies): + logger.warn(f"Proxy named {proxy.get('name')} already exists. Skipping...") + continue + proxies.append(proxy) + if proxies: + clash_config['proxies'] = proxies self.__insert_ruleset() self._top_rules = self._clash_rule_parser.to_list() - clash_config = copy.deepcopy(self._clash_config) clash_config['rule-providers'] = clash_config.get('rule-providers') or {} # 添加代理组 proxy_groups = copy.deepcopy(self._proxy_groups) @@ -1104,11 +1186,6 @@ class ClashRuleProvider(_PluginBase): else: clash_config['proxy-groups'] = proxy_groups - # 添加额外节点 - if clash_config.get('proxies'): - clash_config['proxies'].extend(self._extra_proxies) - else: - clash_config['proxies'] = copy.deepcopy(self._extra_proxies) # 添加按大洲代理组 if self._group_by_region: @@ -1119,7 +1196,7 @@ class ClashRuleProvider(_PluginBase): clash_config['proxy-groups'] = copy.deepcopy(self._proxy_groups_by_region) top_rules = [] - outbound_names = list(x.get("name") for x in self.clash_outbound(clash_config)) + outbound_names = list(x.get("name") for x in self.clash_outbound()) # 添加 extra rule providers if self._extra_rule_providers: diff --git a/plugins.v2/clashruleprovider/clash_rule_parser.py b/plugins.v2/clashruleprovider/clash_rule_parser.py index db91e37..9977e1d 100644 --- a/plugins.v2/clashruleprovider/clash_rule_parser.py +++ b/plugins.v2/clashruleprovider/clash_rule_parser.py @@ -2,7 +2,7 @@ import re from typing import List, Dict, Any, Optional, Union, Callable, Literal from dataclasses import dataclass from enum import Enum -from urllib.parse import urlparse, parse_qs, unquote, parse_qsl +from urllib.parse import urlparse, parse_qs, unquote, parse_qsl, urlencode, urlunparse import json import base64 import binascii @@ -684,12 +684,14 @@ class Converter: raise ValueError(f"invalid truth value {val!r}") @staticmethod - def convert_v2ray(buf: bytes): - decoded = Converter.decode_base64(buf).decode("utf-8") - lines = decoded.strip().splitlines() + def convert_v2ray(v2ray_link: Union[list, bytes]) -> List[Dict[str, Any]]: + if isinstance(v2ray_link, bytes): + decoded = Converter.decode_base64(v2ray_link).decode("utf-8") + lines = decoded.strip().splitlines() + else: + lines = v2ray_link proxies = [] names = {} - for line in lines: line = line.strip() if not line: @@ -800,7 +802,6 @@ class Converter: try: parsed = urlparse(line) query = parse_qs(parsed.query) - uuid = parsed.username or "" server = parsed.hostname or "" port = parsed.port or 443 @@ -811,7 +812,6 @@ class Converter: network = query.get("type", [""])[0] path = query.get("path", [""])[0] host = query.get("host", [""])[0] - name = Converter.unique_name(names, unquote(parsed.fragment or f"{server}:{port}")) proxy = { @@ -831,11 +831,30 @@ class Converter: if network: proxy["network"] = network - if network == "ws": - proxy["ws-opts"] = { - "path": path, - "headers": {"Host": host} - } + if network in ["ws", "httpupgrade"]: + headers = {} + if host: + headers["Host"] = host + ws_opts:Dict[str, Any] = { "path": path, "headers": headers } + try: + parsed_path = urlparse(path) + q = parse_qs(parsed_path.query) + if "ed" in q: + med = int(q["ed"][0]) + ws_opts["max-early-data"] = med + ws_opts["early-data-header-name"] = q.get("eh", ["Sec-WebSocket-Protocol"])[0] + q.pop("ed", None) + new_query = urlencode(q, doseq=True) + parsed = parsed._replace(query=new_query) + path = urlunparse(parsed) + elif "eh" in q: + ws_opts["early-data-header-name"] = q["eh"][0] + ws_opts["path"] = path + except Exception: + pass + if network == "httpupgrade": + ws_opts["v2ray-http-upgrade-fast-open"] = True + proxy["ws-opts"] = ws_opts elif network == "grpc": proxy["grpc-opts"] = { "grpc-service-name": path @@ -850,11 +869,11 @@ class Converter: alpn = query.get("alpn", [""])[0] if alpn: proxy["alpn"] = alpn.split(",") - - return proxy + proxies.append(proxy) except Exception as e: raise ValueError(f"VLESS parse error: {e}") from e + elif scheme == "trojan": try: parsed = urlparse(line) @@ -971,31 +990,56 @@ class Converter: raise ValueError(f"SOCKS5 parse error: {e}") from e elif scheme == "ss": try: + parsed = urlparse(line) # 兼容 ss://base64 或 ss://base64#name - if "#" in body: - body, fragment = body.split("#", 1) - name = Converter.unique_name(names, unquote(fragment)) + if parsed.fragment: + name = Converter.unique_name(names, unquote(parsed.fragment)) else: name = Converter.unique_name(names, "ss") - - if "@" in body: - userinfo, server = body.split("@", 1) - else: - decoded = Converter.decode_base64(body).decode() - userinfo, server = decoded.rsplit("@", 1) - - cipher, password = userinfo.split(":") - server_host, server_port = server.split(":") - + if parsed.port is None: + base64_body = body.split("#")[0] + parsed = urlparse(f"ss://{Converter.decode_base64(base64_body).decode('utf-8')}") + cipher_raw = parsed.username + cipher = cipher_raw + password = parsed.password + if not password: + dc_buf = Converter.decode_base64(cipher_raw).decode('utf-8') + if dc_buf.startswith("ss://"): + dc_buf = dc_buf[len("ss://"):] + dc_buf = Converter.decode_base64(dc_buf).decode('utf-8') + cipher, password = dc_buf.split(":", 1) + server = parsed.hostname + port = parsed.port + query = dict(parse_qsl(parsed.query)) proxy = { "name": name, "type": "ss", - "server": server_host, - "port": server_port, + "server": server, + "port": port, "cipher": cipher, "password": password, "udp": True } + plugin = query.get("plugin") + if plugin and ";" in plugin: + query_string = "pluginName=" + plugin.replace(";", "&") + plugin_info = parse_qs(query_string) + plugin_name = plugin_info.get("pluginName", [""])[0] + + if "obfs" in plugin_name: + proxy["plugin"] = "obfs" + proxy["plugin-opts"] = { + "mode": plugin_info.get("obfs", [""])[0], + "host": plugin_info.get("obfs-host", [""])[0], + } + elif "v2ray-plugin" in plugin_name: + proxy["plugin"] = "v2ray-plugin" + proxy["plugin-opts"] = { + "mode": plugin_info.get("mode", [""])[0], + "host": plugin_info.get("host", [""])[0], + "path": plugin_info.get("path", [""])[0], + "tls": "tls" in plugin, + } proxies.append(proxy) except Exception as e: raise ValueError(f"SS parse error: {e}") from e diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BDDunIfC.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BDDunIfC.css new file mode 100644 index 0000000..477b78f --- /dev/null +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BDDunIfC.css @@ -0,0 +1,4 @@ + +.plugin-config[data-v-107d3846] { + margin: 0 auto; +} diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-r5zwvDwv.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-r5zwvDwv.js new file mode 100644 index 0000000..0ec1de8 --- /dev/null +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-r5zwvDwv.js @@ -0,0 +1,1029 @@ +import { importShared } from './__federation_fn_import-JrT3xvdd.js'; +import { _ as _export_sfc } from './_plugin-vue_export-helper-pcqpp-6-.js'; + +const {createTextVNode:_createTextVNode,resolveComponent:_resolveComponent,withCtx:_withCtx,createVNode:_createVNode,toDisplayString:_toDisplayString,openBlock:_openBlock,createBlock:_createBlock,createCommentVNode:_createCommentVNode,createElementVNode:_createElementVNode,mergeProps:_mergeProps,withModifiers:_withModifiers,Fragment:_Fragment,createElementBlock:_createElementBlock} = await importShared('vue'); + + +const _hoisted_1 = { class: "plugin-config" }; +const _hoisted_2 = { class: "d-flex align-center" }; +const _hoisted_3 = { class: "font-weight-medium" }; +const _hoisted_4 = { class: "text-body-2" }; + +const {ref,reactive,onMounted,computed} = await importShared('vue'); + + +// Props + +const _sfc_main = { + __name: 'Config', + props: { + initialConfig: { + type: Object, + default: () => ({}), + }, + api: { + type: Object, + default: () => { + }, + }, +}, + emits: ['save', 'close'], + setup(__props, { emit: __emit }) { + +const props = __props; + +// 状态变量 +const clashTemplateDialog = ref(false); +const clashTemplateType = ref('YAML'); +const clashTemplateContent = ref(''); +const form = ref(null); +const isFormValid = ref(true); +const error = ref(null); +const saving = ref(false); +const testing = ref(false); +const showClashSecret = ref(false); +const dashboardComponents = ['Clash Info', 'Traffic Stats']; + +// Test result state +const testResult = reactive({ + show: false, + success: false, + title: '', + message: '' +}); + + +// 默认配置 +const defaultConfig = { + enabled: false, + sub_links: [], + filter_keywords: ["公益性", "高延迟", "域名", "官网", "重启", "过期时间", "系统代理"], + clash_dashboard_url: '', + clash_dashboard_secret: '', + movie_pilot_url: '', + cron_string: '0 */6 * * *', + timeout: 10, + retry_times: 3, + proxy: false, + notify: false, + auto_update_subscriptions: true, + ruleset_prefix: '📂<=', + acl4ssr_prefix: '🗂️=>', + group_by_region: false, + refresh_delay: 5, + discard_rules: false, + enable_acl4ssr: false, + dashboard_components: [], + clash_template: '' +}; + +// 响应式配置对象 +const config = reactive({...defaultConfig}); + +// 自定义事件 +const emit = __emit; + +// 初始化 +onMounted(() => { + if (props.initialConfig) { + Object.keys(props.initialConfig).forEach(key => { + if (key in config) { + config[key] = props.initialConfig[key]; + } + }); + } +}); + +// 验证函数 +const isValidUrl = (urlString) => { + if (!urlString) return false; + try { + const url = new URL(urlString); + return url.protocol === 'http:' || url.protocol === 'https:'; + } catch (e) { + return false; + } +}; + +function validateSubLinks(links) { + if (!links || links.length === 0) { + return '至少需要一个订阅链接' + } + + for (const link of links) { + if (!isValidUrl(link)) { + return `无效的订阅链接: ${link}` + } + } + return true +} + +// 测试连接 +async function testConnection() { + testing.value = true; + error.value = null; + testResult.show = false; + + try { + // 验证必需的参数 + if (!config.clash_dashboard_url) { + throw new Error('请先配置 Clash 面板 URL') + } + if (!config.clash_dashboard_secret) { + throw new Error('请先配置 Clash 面板密钥') + } + if (!config.sub_links || config.sub_links.length === 0) { + throw new Error('请先配置至少一个订阅链接') + } + if (!config.movie_pilot_url || config.movie_pilot_url.length === 0) { + throw new Error('请先MoviePilot链接') + } + // 准备API请求参数 + const testParams = { + clash_dashboard_url: config.clash_dashboard_url, + clash_dashboard_secret: config.clash_dashboard_secret, + sub_link: config.sub_links[0] // 使用第一个订阅链接进行测试 + }; + + // 调用API进行连接测试 + const result = await props.api.post('/plugin/ClashRuleProvider/connectivity', testParams); + + // 根据返回结果显示相应消息 + if (result.success) { + testResult.success = true; + testResult.title = '连接测试成功!'; + testResult.message = 'Clash面板和订阅链接连接正常,配置验证通过'; + testResult.show = true; + + // Auto hide after 5 seconds + setTimeout(() => { + testResult.show = false; + }, 5000); + } else { + throw new Error(result.message || '连接测试失败,请检查配置') + } + + } catch (err) { + console.error('连接测试失败:', err); + testResult.success = false; + testResult.title = '连接测试失败'; + testResult.message = err.message; + testResult.show = true; + } finally { + testing.value = false; + } +} + +// 保存配置 +async function saveConfig() { + if (!isFormValid.value) { + error.value = '请修正表单中的错误'; + return + } + + saving.value = true; + error.value = null; + + try { + await new Promise(resolve => setTimeout(resolve, 1000)); + emit('save', {...config}); + } catch (err) { + console.error('保存配置失败:', err); + error.value = err.message || '保存配置失败'; + } finally { + saving.value = false; + } +} + +function extractDomain(url) { + try { + const domain = new URL(url).hostname; + return domain.startsWith('www.') ? domain.substring(4) : domain + } catch { + return url // 如果解析失败,返回原始URL + } +} + +function openClashTemplateDialog() { + clashTemplateContent.value = config.clash_template; + clashTemplateDialog.value = true; +} + +function saveClashTemplate() { + if (!clashTemplateContent.value.trim()) { + alert('请粘贴 YAML 配置内容'); + return; + } + config.clash_template = clashTemplateContent.value; + clashTemplateDialog.value = false; +} + +// 重置表单 +function resetForm() { + Object.keys(defaultConfig).forEach(key => { + config[key] = defaultConfig[key]; + }); + + if (form.value) { + form.value.resetValidation(); + } +} + +// 关闭组件 +function notifyClose() { + emit('close'); +} + +// 通知主应用切换到Page页面 +function notifySwitch() { + emit('switch'); +} + +return (_ctx, _cache) => { + const _component_v_card_title = _resolveComponent("v-card-title"); + const _component_v_icon = _resolveComponent("v-icon"); + const _component_v_btn = _resolveComponent("v-btn"); + const _component_v_card_item = _resolveComponent("v-card-item"); + const _component_v_alert = _resolveComponent("v-alert"); + const _component_v_switch = _resolveComponent("v-switch"); + const _component_v_col = _resolveComponent("v-col"); + const _component_v_row = _resolveComponent("v-row"); + const _component_v_chip = _resolveComponent("v-chip"); + const _component_v_combobox = _resolveComponent("v-combobox"); + const _component_v_text_field = _resolveComponent("v-text-field"); + const _component_v_select = _resolveComponent("v-select"); + const _component_v_cron_field = _resolveComponent("v-cron-field"); + const _component_v_expansion_panel_title = _resolveComponent("v-expansion-panel-title"); + const _component_v_expansion_panel_text = _resolveComponent("v-expansion-panel-text"); + const _component_v_expansion_panel = _resolveComponent("v-expansion-panel"); + const _component_v_expansion_panels = _resolveComponent("v-expansion-panels"); + const _component_v_form = _resolveComponent("v-form"); + const _component_v_card_text = _resolveComponent("v-card-text"); + const _component_v_spacer = _resolveComponent("v-spacer"); + const _component_v_card_actions = _resolveComponent("v-card-actions"); + const _component_v_card = _resolveComponent("v-card"); + const _component_v_textarea = _resolveComponent("v-textarea"); + const _component_v_dialog = _resolveComponent("v-dialog"); + + return (_openBlock(), _createElementBlock(_Fragment, null, [ + _createElementVNode("div", _hoisted_1, [ + _createVNode(_component_v_card, null, { + default: _withCtx(() => [ + _createVNode(_component_v_card_item, null, { + append: _withCtx(() => [ + _createVNode(_component_v_btn, { + icon: "", + color: "primary", + variant: "text", + onClick: notifyClose + }, { + default: _withCtx(() => [ + _createVNode(_component_v_icon, { left: "" }, { + default: _withCtx(() => _cache[27] || (_cache[27] = [ + _createTextVNode("mdi-close") + ])), + _: 1 + }) + ]), + _: 1 + }) + ]), + default: _withCtx(() => [ + _createVNode(_component_v_card_title, null, { + default: _withCtx(() => _cache[26] || (_cache[26] = [ + _createTextVNode("Clash Rule Provider 插件配置") + ])), + _: 1 + }) + ]), + _: 1 + }), + _createVNode(_component_v_card_text, { class: "overflow-y-auto" }, { + default: _withCtx(() => [ + (error.value) + ? (_openBlock(), _createBlock(_component_v_alert, { + key: 0, + type: "error", + class: "mb-4" + }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(error.value), 1) + ]), + _: 1 + })) + : _createCommentVNode("", true), + _createVNode(_component_v_form, { + ref_key: "form", + ref: form, + modelValue: isFormValid.value, + "onUpdate:modelValue": _cache[20] || (_cache[20] = $event => ((isFormValid).value = $event)), + onSubmit: _withModifiers(saveConfig, ["prevent"]) + }, { + default: _withCtx(() => [ + _cache[41] || (_cache[41] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "基本设置", -1)), + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.enabled, + "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((config.enabled) = $event)), + label: "启用插件", + color: "primary", + inset: "", + hint: "启用插件", + density: "compact" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.proxy, + "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((config.proxy) = $event)), + label: "启用代理", + color: "primary", + inset: "", + hint: "是否使用系统代理进行网络请求", + density: "compact" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.notify, + "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((config.notify) = $event)), + label: "启用通知", + color: "primary", + inset: "", + hint: "执行完成后发送通知消息", + density: "compact" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.auto_update_subscriptions, + "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((config.auto_update_subscriptions) = $event)), + label: "自动更新订阅", + color: "primary", + inset: "", + hint: "定期自动更新 Clash 订阅配置", + density: "compact" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }) + ]), + _: 1 + }), + _cache[42] || (_cache[42] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "订阅配置", -1)), + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { cols: "12" }, { + default: _withCtx(() => [ + _createVNode(_component_v_combobox, { + modelValue: config.sub_links, + "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((config.sub_links) = $event)), + label: "订阅链接", + variant: "outlined", + multiple: "", + chips: "", + "closable-chips": "", + hint: "添加 Clash | V2Ray 订阅链接. 为避免冲突, 只有第一个链接的代理组会被保留", + rules: [validateSubLinks] + }, { + chip: _withCtx(({ props, item }) => [ + _createVNode(_component_v_chip, _mergeProps(props, { + closable: "", + size: "small" + }), { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(extractDomain(item.value)), 1) + ]), + _: 2 + }, 1040) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { cols: "12" }, { + default: _withCtx(() => [ + _createVNode(_component_v_combobox, { + modelValue: config.filter_keywords, + "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((config.filter_keywords) = $event)), + label: "节点过滤关键词", + variant: "outlined", + multiple: "", + chips: "", + "closable-chips": "", + hint: "添加用于过滤节点的关键词" + }, { + chip: _withCtx(({ props, item }) => [ + _createVNode(_component_v_chip, _mergeProps(props, { + closable: "", + size: "small", + color: "info" + }), { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(item.value), 1) + ]), + _: 2 + }, 1040) + ]), + _: 1 + }, 8, ["modelValue"]) + ]), + _: 1 + }) + ]), + _: 1 + }), + _cache[43] || (_cache[43] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "Clash 面板设置", -1)), + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { cols: "12" }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.clash_dashboard_url, + "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((config.clash_dashboard_url) = $event)), + label: "Clash 面板 URL", + variant: "outlined", + placeholder: "http://localhost:9090", + hint: "Clash 控制面板的访问地址", + rules: [v => !v || isValidUrl(v) || '请输入有效的URL地址'] + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "primary" }, { + default: _withCtx(() => _cache[28] || (_cache[28] = [ + _createTextVNode("mdi-web") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.clash_dashboard_secret, + "onUpdate:modelValue": _cache[7] || (_cache[7] = $event => ((config.clash_dashboard_secret) = $event)), + label: "Clash 面板密钥", + variant: "outlined", + placeholder: "your-clash-secret", + hint: "用于访问Clash API的密钥", + "append-inner-icon": showClashSecret.value ? 'mdi-eye-off' : 'mdi-eye', + type: showClashSecret.value ? 'text' : 'password', + "onClick:appendInner": _cache[8] || (_cache[8] = $event => (showClashSecret.value = !showClashSecret.value)) + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "warning" }, { + default: _withCtx(() => _cache[29] || (_cache[29] = [ + _createTextVNode("mdi-key") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "append-inner-icon", "type"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_select, { + modelValue: config.dashboard_components, + "onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ((config.dashboard_components) = $event)), + items: dashboardComponents, + label: "仪表盘组件", + "hide-details": "", + variant: "outlined", + multiple: "", + chips: "", + class: "mb-4", + hint: "添加仪表盘组件" + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "info" }, { + default: _withCtx(() => _cache[30] || (_cache[30] = [ + _createTextVNode("mdi-view-dashboard") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue"]) + ]), + _: 1 + }) + ]), + _: 1 + }), + _cache[44] || (_cache[44] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "MoviePilot 设置", -1)), + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { cols: "12" }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.movie_pilot_url, + "onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((config.movie_pilot_url) = $event)), + label: "MoviePilot URL", + variant: "outlined", + placeholder: "http://localhost:3001", + hint: "MoviePilot 服务的访问地址", + rules: [v => !!v || 'MoviePilot URL不能为空', v => isValidUrl(v) || '请输入有效的URL地址'] + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "success" }, { + default: _withCtx(() => _cache[31] || (_cache[31] = [ + _createTextVNode("mdi-movie") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }) + ]), + _: 1 + }), + _cache[45] || (_cache[45] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "执行设置", -1)), + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { cols: "12" }, { + default: _withCtx(() => [ + _createVNode(_component_v_cron_field, { + modelValue: config.cron_string, + "onUpdate:modelValue": _cache[11] || (_cache[11] = $event => ((config.cron_string) = $event)), + label: "执行周期", + placeholder: "0 4 * * *", + hint: "使用标准Cron表达式格式 (分 时 日 月 周)" + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "info" }, { + default: _withCtx(() => _cache[32] || (_cache[32] = [ + _createTextVNode("mdi-clock-time-four-outline") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.timeout, + "onUpdate:modelValue": _cache[12] || (_cache[12] = $event => ((config.timeout) = $event)), + modelModifiers: { number: true }, + label: "超时时间", + variant: "outlined", + type: "number", + min: "1", + max: "300", + suffix: "秒", + hint: "请求的超时时间", + rules: [v => v > 0 || '超时时间必须大于0'] + }, null, 8, ["modelValue", "rules"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.retry_times, + "onUpdate:modelValue": _cache[13] || (_cache[13] = $event => ((config.retry_times) = $event)), + modelModifiers: { number: true }, + label: "重试次数", + variant: "outlined", + type: "number", + min: "0", + max: "10", + hint: "失败时的重试次数", + rules: [v => v >= 0 || '重试次数不能为负数'] + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "info" }, { + default: _withCtx(() => _cache[33] || (_cache[33] = [ + _createTextVNode("mdi-refresh") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }) + ]), + _: 1 + }), + _createVNode(_component_v_expansion_panels, { + variant: "accordion", + class: "mt-4" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_expansion_panel, null, { + default: _withCtx(() => [ + _createVNode(_component_v_expansion_panel_title, null, { + default: _withCtx(() => [ + _createVNode(_component_v_icon, { class: "mr-2" }, { + default: _withCtx(() => _cache[34] || (_cache[34] = [ + _createTextVNode("mdi-cog") + ])), + _: 1 + }), + _cache[35] || (_cache[35] = _createTextVNode(" 高级选项 ")) + ]), + _: 1 + }), + _createVNode(_component_v_expansion_panel_text, null, { + default: _withCtx(() => [ + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.discard_rules, + "onUpdate:modelValue": _cache[14] || (_cache[14] = $event => ((config.discard_rules) = $event)), + label: "丢弃订阅规则", + color: "primary", + inset: "", + hint: "不保留订阅配置文件的rules字段" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.enable_acl4ssr, + "onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ((config.enable_acl4ssr) = $event)), + label: "ACL4SSR规则集", + color: "primary", + inset: "", + hint: "启用ACL4SSR规则集" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.group_by_region, + "onUpdate:modelValue": _cache[16] || (_cache[16] = $event => ((config.group_by_region) = $event)), + label: "按大洲分组节点", + color: "primary", + inset: "", + hint: "启用后根据名称,将节点添加到代理组" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { cols: "3" }, { + default: _withCtx(() => [ + _createVNode(_component_v_btn, { + color: "primary", + onClick: openClashTemplateDialog, + class: "mr-2" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_icon, { left: "" }, { + default: _withCtx(() => _cache[36] || (_cache[36] = [ + _createTextVNode("mdi-import") + ])), + _: 1 + }), + _cache[37] || (_cache[37] = _createTextVNode(" Clash 配置模板 ")) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }), + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { + cols: "12", + md: "4" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.ruleset_prefix, + "onUpdate:modelValue": _cache[17] || (_cache[17] = $event => ((config.ruleset_prefix) = $event)), + label: "规则集前缀", + variant: "outlined", + placeholder: "📂<=", + rules: [v => !!v || '规则集前缀不能为空'], + hint: "为生成的规则集添加前缀" + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "info" }, { + default: _withCtx(() => _cache[38] || (_cache[38] = [ + _createTextVNode("mdi-palette") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "4" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.acl4ssr_prefix, + "onUpdate:modelValue": _cache[18] || (_cache[18] = $event => ((config.acl4ssr_prefix) = $event)), + label: "ACL4SSR 规则集前缀", + variant: "outlined", + placeholder: "🗂️=>", + rules: [v => !!v || '规则集前缀不能为空'], + hint: "ACL4SSR 规则集前缀" + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "primary" }, { + default: _withCtx(() => _cache[39] || (_cache[39] = [ + _createTextVNode("mdi-palette") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "4" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.refresh_delay, + "onUpdate:modelValue": _cache[19] || (_cache[19] = $event => ((config.refresh_delay) = $event)), + modelModifiers: { number: true }, + label: "刷新延迟", + variant: "outlined", + type: "number", + min: "1", + max: "30", + suffix: "秒", + hint: "通知Clash刷新规则集的延迟时间", + rules: [v => v >= 0 || '刷新延迟不能为负数'] + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "info" }, { + default: _withCtx(() => _cache[40] || (_cache[40] = [ + _createTextVNode("mdi-clock-outline") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_alert, { + type: "info", + text: "", + class: "mb-6", + variant: "tonal" + }, { + default: _withCtx(() => _cache[46] || (_cache[46] = [ + _createTextVNode(" 配置说明参考: "), + _createElementVNode("a", { + href: "https://github.com/wumode/MoviePilot-Plugins/tree/main/plugins.v2/clashruleprovider/README.md", + target: "_blank" + }, "README", -1) + ])), + _: 1 + }), + _createVNode(_component_v_card_actions, null, { + default: _withCtx(() => [ + _createVNode(_component_v_btn, { + color: "primary", + onClick: notifySwitch + }, { + default: _withCtx(() => [ + _createVNode(_component_v_icon, { left: "" }, { + default: _withCtx(() => _cache[47] || (_cache[47] = [ + _createTextVNode("mdi-view-dashboard-edit") + ])), + _: 1 + }), + _cache[48] || (_cache[48] = _createTextVNode(" 规则 ")) + ]), + _: 1 + }), + _createVNode(_component_v_btn, { + color: "secondary", + onClick: resetForm + }, { + default: _withCtx(() => _cache[49] || (_cache[49] = [ + _createTextVNode("重置") + ])), + _: 1 + }), + _createVNode(_component_v_btn, { + color: "info", + onClick: testConnection, + loading: testing.value + }, { + default: _withCtx(() => _cache[50] || (_cache[50] = [ + _createTextVNode("测试连接") + ])), + _: 1 + }, 8, ["loading"]), + _createVNode(_component_v_spacer), + _createVNode(_component_v_btn, { + color: "primary", + disabled: !isFormValid.value, + onClick: saveConfig, + loading: saving.value + }, { + default: _withCtx(() => _cache[51] || (_cache[51] = [ + _createTextVNode(" 保存配置 ") + ])), + _: 1 + }, 8, ["disabled", "loading"]) + ]), + _: 1 + }), + (testResult.show) + ? (_openBlock(), _createBlock(_component_v_alert, { + key: 0, + type: testResult.success ? 'success' : 'error', + variant: "tonal", + closable: "", + class: "ma-4 mt-0", + "onClick:close": _cache[21] || (_cache[21] = $event => (testResult.show = false)) + }, { + default: _withCtx(() => [ + _createElementVNode("div", _hoisted_2, [ + _createVNode(_component_v_icon, { class: "mr-2" }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(testResult.success ? 'mdi-check-circle' : 'mdi-alert-circle'), 1) + ]), + _: 1 + }), + _createElementVNode("div", null, [ + _createElementVNode("div", _hoisted_3, _toDisplayString(testResult.title), 1), + _createElementVNode("div", _hoisted_4, _toDisplayString(testResult.message), 1) + ]) + ]) + ]), + _: 1 + }, 8, ["type"])) + : _createCommentVNode("", true) + ]), + _: 1 + }) + ]), + _createVNode(_component_v_dialog, { + modelValue: clashTemplateDialog.value, + "onUpdate:modelValue": _cache[25] || (_cache[25] = $event => ((clashTemplateDialog).value = $event)), + "max-width": "600" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_card, null, { + default: _withCtx(() => [ + _createVNode(_component_v_card_title, null, { + default: _withCtx(() => _cache[52] || (_cache[52] = [ + _createTextVNode("Clash 配置模板") + ])), + _: 1 + }), + _createVNode(_component_v_card_text, { style: {"max-height":"600px","overflow-y":"auto"} }, { + default: _withCtx(() => [ + _createVNode(_component_v_select, { + modelValue: clashTemplateType.value, + "onUpdate:modelValue": _cache[22] || (_cache[22] = $event => ((clashTemplateType).value = $event)), + items: ['YAML'], + label: "配置类型", + class: "mb-4" + }, null, 8, ["modelValue"]), + _createVNode(_component_v_textarea, { + modelValue: clashTemplateContent.value, + "onUpdate:modelValue": _cache[23] || (_cache[23] = $event => ((clashTemplateContent).value = $event)), + label: "配置内容", + "auto-grow": "", + placeholder: "mixed-port: 7890", + class: "mb-4", + required: "", + hint: "规则和出站代理会被添加在配置模板上", + style: {"max-height":"600px","padding-top":"12px","line-height":"1.5"} + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_card_actions, null, { + default: _withCtx(() => [ + _createVNode(_component_v_spacer), + _createVNode(_component_v_btn, { + text: "", + onClick: _cache[24] || (_cache[24] = $event => (clashTemplateDialog.value = false)) + }, { + default: _withCtx(() => _cache[53] || (_cache[53] = [ + _createTextVNode("取消") + ])), + _: 1 + }), + _createVNode(_component_v_btn, { + color: "primary", + onClick: saveClashTemplate + }, { + default: _withCtx(() => _cache[54] || (_cache[54] = [ + _createTextVNode("确定") + ])), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue"]) + ], 64)) +} +} + +}; +const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-107d3846"]]); + +export { ConfigComponent as default }; diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-sTz3W3yr.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-sTz3W3yr.js deleted file mode 100644 index cd1a029..0000000 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-sTz3W3yr.js +++ /dev/null @@ -1,920 +0,0 @@ -import { importShared } from './__federation_fn_import-JrT3xvdd.js'; -import { _ as _export_sfc } from './_plugin-vue_export-helper-pcqpp-6-.js'; - -const {createTextVNode:_createTextVNode,resolveComponent:_resolveComponent,withCtx:_withCtx,createVNode:_createVNode,toDisplayString:_toDisplayString,openBlock:_openBlock,createBlock:_createBlock,createCommentVNode:_createCommentVNode,createElementVNode:_createElementVNode,mergeProps:_mergeProps,withModifiers:_withModifiers,createElementBlock:_createElementBlock} = await importShared('vue'); - - -const _hoisted_1 = { class: "plugin-config" }; -const _hoisted_2 = { class: "d-flex align-center" }; -const _hoisted_3 = { class: "font-weight-medium" }; -const _hoisted_4 = { class: "text-body-2" }; - -const {ref,reactive,onMounted,computed} = await importShared('vue'); - - -// Props - -const _sfc_main = { - __name: 'Config', - props: { - initialConfig: { - type: Object, - default: () => ({}), - }, - api: { - type: Object, - default: () => { - }, - }, -}, - emits: ['save', 'close'], - setup(__props, { emit: __emit }) { - -const props = __props; - -// 状态变量 -const form = ref(null); -const isFormValid = ref(true); -const error = ref(null); -const saving = ref(false); -const testing = ref(false); -const showClashSecret = ref(false); -const selectedCronOption = ref('6hours'); -const dashboardComponents = ['Clash Info', 'Traffic Stats']; - -// Test result state -const testResult = reactive({ - show: false, - success: false, - title: '', - message: '' -}); - - -// 默认配置 -const defaultConfig = { - enabled: false, - sub_links: [], - filter_keywords: ["公益性", "高延迟", "域名", "官网", "重启", "过期时间", "系统代理"], - clash_dashboard_url: '', - clash_dashboard_secret: '', - movie_pilot_url: '', - cron_string: '0 */6 * * *', - timeout: 10, - retry_times: 3, - proxy: false, - notify: false, - auto_update_subscriptions: true, - ruleset_prefix: '📂<=', - acl4ssr_prefix: '🗂️=>', - group_by_region: false, - refresh_delay: 5, - discard_rules: false, - enable_acl4ssr: false, - dashboard_components: [], -}; - -// 响应式配置对象 -const config = reactive({...defaultConfig}); - -// 自定义事件 -const emit = __emit; - -// 初始化 -onMounted(() => { - if (props.initialConfig) { - Object.keys(props.initialConfig).forEach(key => { - if (key in config) { - config[key] = props.initialConfig[key]; - } - }); - } -}); - -// 验证函数 -const isValidUrl = (urlString) => { - if (!urlString) return false; - try { - const url = new URL(urlString); - return url.protocol === 'http:' || url.protocol === 'https:'; - } catch (e) { - return false; - } -}; - -function validateSubLinks(links) { - if (!links || links.length === 0) { - return '至少需要一个订阅链接' - } - - for (const link of links) { - if (!isValidUrl(link)) { - return `无效的订阅链接: ${link}` - } - } - return true -} - -// 测试连接 -async function testConnection() { - testing.value = true; - error.value = null; - testResult.show = false; - - try { - // 验证必需的参数 - if (!config.clash_dashboard_url) { - throw new Error('请先配置 Clash 面板 URL') - } - if (!config.clash_dashboard_secret) { - throw new Error('请先配置 Clash 面板密钥') - } - if (!config.sub_links || config.sub_links.length === 0) { - throw new Error('请先配置至少一个订阅链接') - } - if (!config.movie_pilot_url || config.movie_pilot_url.length === 0) { - throw new Error('请先MoviePilot链接') - } - // 准备API请求参数 - const testParams = { - clash_dashboard_url: config.clash_dashboard_url, - clash_dashboard_secret: config.clash_dashboard_secret, - sub_link: config.sub_links[0] // 使用第一个订阅链接进行测试 - }; - - // 调用API进行连接测试 - const result = await props.api.post('/plugin/ClashRuleProvider/connectivity', testParams); - - // 根据返回结果显示相应消息 - if (result.success) { - testResult.success = true; - testResult.title = '连接测试成功!'; - testResult.message = 'Clash面板和订阅链接连接正常,配置验证通过'; - testResult.show = true; - - // Auto hide after 5 seconds - setTimeout(() => { - testResult.show = false; - }, 5000); - } else { - throw new Error(result.message || '连接测试失败,请检查配置') - } - - } catch (err) { - console.error('连接测试失败:', err); - testResult.success = false; - testResult.title = '连接测试失败'; - testResult.message = err.message; - testResult.show = true; - } finally { - testing.value = false; - } -} - -// 保存配置 -async function saveConfig() { - if (!isFormValid.value) { - error.value = '请修正表单中的错误'; - return - } - - saving.value = true; - error.value = null; - - try { - await new Promise(resolve => setTimeout(resolve, 1000)); - emit('save', {...config}); - } catch (err) { - console.error('保存配置失败:', err); - error.value = err.message || '保存配置失败'; - } finally { - saving.value = false; - } -} - -function extractDomain(url) { - try { - const domain = new URL(url).hostname; - return domain.startsWith('www.') ? domain.substring(4) : domain - } catch { - return url // 如果解析失败,返回原始URL - } -} - -// 重置表单 -function resetForm() { - Object.keys(defaultConfig).forEach(key => { - config[key] = defaultConfig[key]; - }); - selectedCronOption.value = '6hours'; - - if (form.value) { - form.value.resetValidation(); - } -} - -// 关闭组件 -function notifyClose() { - emit('close'); -} - -// 通知主应用切换到Page页面 -function notifySwitch() { - emit('switch'); -} - -return (_ctx, _cache) => { - const _component_v_card_title = _resolveComponent("v-card-title"); - const _component_v_icon = _resolveComponent("v-icon"); - const _component_v_btn = _resolveComponent("v-btn"); - const _component_v_card_item = _resolveComponent("v-card-item"); - const _component_v_alert = _resolveComponent("v-alert"); - const _component_v_switch = _resolveComponent("v-switch"); - const _component_v_col = _resolveComponent("v-col"); - const _component_v_row = _resolveComponent("v-row"); - const _component_v_chip = _resolveComponent("v-chip"); - const _component_v_combobox = _resolveComponent("v-combobox"); - const _component_v_text_field = _resolveComponent("v-text-field"); - const _component_v_select = _resolveComponent("v-select"); - const _component_v_cron_field = _resolveComponent("v-cron-field"); - const _component_v_expansion_panel_title = _resolveComponent("v-expansion-panel-title"); - const _component_v_expansion_panel_text = _resolveComponent("v-expansion-panel-text"); - const _component_v_expansion_panel = _resolveComponent("v-expansion-panel"); - const _component_v_expansion_panels = _resolveComponent("v-expansion-panels"); - const _component_v_form = _resolveComponent("v-form"); - const _component_v_card_text = _resolveComponent("v-card-text"); - const _component_v_spacer = _resolveComponent("v-spacer"); - const _component_v_card_actions = _resolveComponent("v-card-actions"); - const _component_v_card = _resolveComponent("v-card"); - - return (_openBlock(), _createElementBlock("div", _hoisted_1, [ - _createVNode(_component_v_card, null, { - default: _withCtx(() => [ - _createVNode(_component_v_card_item, null, { - append: _withCtx(() => [ - _createVNode(_component_v_btn, { - icon: "", - color: "primary", - variant: "text", - onClick: notifyClose - }, { - default: _withCtx(() => [ - _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[23] || (_cache[23] = [ - _createTextVNode("mdi-close") - ])), - _: 1 - }) - ]), - _: 1 - }) - ]), - default: _withCtx(() => [ - _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[22] || (_cache[22] = [ - _createTextVNode("Clash Rule Provider 插件配置") - ])), - _: 1 - }) - ]), - _: 1 - }), - _createVNode(_component_v_card_text, { class: "overflow-y-auto" }, { - default: _withCtx(() => [ - (error.value) - ? (_openBlock(), _createBlock(_component_v_alert, { - key: 0, - type: "error", - class: "mb-4" - }, { - default: _withCtx(() => [ - _createTextVNode(_toDisplayString(error.value), 1) - ]), - _: 1 - })) - : _createCommentVNode("", true), - _createVNode(_component_v_form, { - ref_key: "form", - ref: form, - modelValue: isFormValid.value, - "onUpdate:modelValue": _cache[20] || (_cache[20] = $event => ((isFormValid).value = $event)), - onSubmit: _withModifiers(saveConfig, ["prevent"]) - }, { - default: _withCtx(() => [ - _cache[35] || (_cache[35] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "基本设置", -1)), - _createVNode(_component_v_row, null, { - default: _withCtx(() => [ - _createVNode(_component_v_col, { - cols: "12", - md: "3" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_switch, { - modelValue: config.enabled, - "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((config.enabled) = $event)), - label: "启用插件", - color: "primary", - inset: "", - hint: "启用后插件将开始监控和同步", - density: "compact" - }, null, 8, ["modelValue"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "3" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_switch, { - modelValue: config.proxy, - "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((config.proxy) = $event)), - label: "启用代理", - color: "primary", - inset: "", - hint: "是否使用系统代理进行网络请求", - density: "compact" - }, null, 8, ["modelValue"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "3" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_switch, { - modelValue: config.notify, - "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((config.notify) = $event)), - label: "启用通知", - color: "primary", - inset: "", - hint: "执行完成后发送通知消息", - density: "compact" - }, null, 8, ["modelValue"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "3" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_switch, { - modelValue: config.auto_update_subscriptions, - "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((config.auto_update_subscriptions) = $event)), - label: "自动更新订阅", - color: "primary", - inset: "", - hint: "定期自动更新Clash订阅配置", - density: "compact" - }, null, 8, ["modelValue"]) - ]), - _: 1 - }) - ]), - _: 1 - }), - _cache[36] || (_cache[36] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "订阅配置", -1)), - _createVNode(_component_v_row, null, { - default: _withCtx(() => [ - _createVNode(_component_v_col, { cols: "12" }, { - default: _withCtx(() => [ - _createVNode(_component_v_combobox, { - modelValue: config.sub_links, - "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((config.sub_links) = $event)), - label: "订阅链接", - variant: "outlined", - multiple: "", - chips: "", - "closable-chips": "", - hint: "添加一个Clash订阅链接, 按回车确认输入", - rules: [validateSubLinks] - }, { - chip: _withCtx(({ props, item }) => [ - _createVNode(_component_v_chip, _mergeProps(props, { - closable: "", - size: "small" - }), { - default: _withCtx(() => [ - _createTextVNode(_toDisplayString(extractDomain(item.value)), 1) - ]), - _: 2 - }, 1040) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { cols: "12" }, { - default: _withCtx(() => [ - _createVNode(_component_v_combobox, { - modelValue: config.filter_keywords, - "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((config.filter_keywords) = $event)), - label: "节点过滤关键词", - variant: "outlined", - multiple: "", - chips: "", - "closable-chips": "", - hint: "添加用于过滤节点的关键词" - }, { - chip: _withCtx(({ props, item }) => [ - _createVNode(_component_v_chip, _mergeProps(props, { - closable: "", - size: "small", - color: "info" - }), { - default: _withCtx(() => [ - _createTextVNode(_toDisplayString(item.value), 1) - ]), - _: 2 - }, 1040) - ]), - _: 1 - }, 8, ["modelValue"]) - ]), - _: 1 - }) - ]), - _: 1 - }), - _cache[37] || (_cache[37] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "Clash 面板设置", -1)), - _createVNode(_component_v_row, null, { - default: _withCtx(() => [ - _createVNode(_component_v_col, { cols: "12" }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.clash_dashboard_url, - "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((config.clash_dashboard_url) = $event)), - label: "Clash 面板 URL", - variant: "outlined", - placeholder: "http://localhost:9090", - hint: "Clash 控制面板的访问地址", - rules: [v => !v || isValidUrl(v) || '请输入有效的URL地址'] - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "primary" }, { - default: _withCtx(() => _cache[24] || (_cache[24] = [ - _createTextVNode("mdi-web") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "6" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.clash_dashboard_secret, - "onUpdate:modelValue": _cache[7] || (_cache[7] = $event => ((config.clash_dashboard_secret) = $event)), - label: "Clash 面板密钥", - variant: "outlined", - placeholder: "your-clash-secret", - hint: "用于访问Clash API的密钥", - "append-inner-icon": showClashSecret.value ? 'mdi-eye-off' : 'mdi-eye', - type: showClashSecret.value ? 'text' : 'password', - "onClick:appendInner": _cache[8] || (_cache[8] = $event => (showClashSecret.value = !showClashSecret.value)) - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "warning" }, { - default: _withCtx(() => _cache[25] || (_cache[25] = [ - _createTextVNode("mdi-key") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "append-inner-icon", "type"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "6" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_select, { - modelValue: config.dashboard_components, - "onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ((config.dashboard_components) = $event)), - items: dashboardComponents, - label: "仪表盘组件", - "hide-details": "", - variant: "outlined", - multiple: "", - chips: "", - class: "mb-4", - hint: "添加仪表盘组件" - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[26] || (_cache[26] = [ - _createTextVNode("mdi-view-dashboard") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue"]) - ]), - _: 1 - }) - ]), - _: 1 - }), - _cache[38] || (_cache[38] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "MoviePilot 设置", -1)), - _createVNode(_component_v_row, null, { - default: _withCtx(() => [ - _createVNode(_component_v_col, { cols: "12" }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.movie_pilot_url, - "onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((config.movie_pilot_url) = $event)), - label: "MoviePilot URL", - variant: "outlined", - placeholder: "http://localhost:3001", - hint: "MoviePilot 服务的访问地址", - rules: [v => !!v || 'MoviePilot URL不能为空', v => isValidUrl(v) || '请输入有效的URL地址'] - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "success" }, { - default: _withCtx(() => _cache[27] || (_cache[27] = [ - _createTextVNode("mdi-movie") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }) - ]), - _: 1 - }), - _cache[39] || (_cache[39] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "执行设置", -1)), - _createVNode(_component_v_row, null, { - default: _withCtx(() => [ - _createVNode(_component_v_col, { cols: "12" }, { - default: _withCtx(() => [ - _createVNode(_component_v_cron_field, { - modelValue: config.cron_string, - "onUpdate:modelValue": _cache[11] || (_cache[11] = $event => ((config.cron_string) = $event)), - label: "执行周期", - placeholder: "0 4 * * *", - hint: "使用标准Cron表达式格式 (分 时 日 月 周)" - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[28] || (_cache[28] = [ - _createTextVNode("mdi-clock-time-four-outline") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "6" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.timeout, - "onUpdate:modelValue": _cache[12] || (_cache[12] = $event => ((config.timeout) = $event)), - modelModifiers: { number: true }, - label: "超时时间 (秒)", - variant: "outlined", - type: "number", - min: "1", - max: "300", - hint: "请求的超时时间", - rules: [v => v > 0 || '超时时间必须大于0'] - }, null, 8, ["modelValue", "rules"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "6" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.retry_times, - "onUpdate:modelValue": _cache[13] || (_cache[13] = $event => ((config.retry_times) = $event)), - modelModifiers: { number: true }, - label: "重试次数", - variant: "outlined", - type: "number", - min: "0", - max: "10", - hint: "失败时的重试次数", - rules: [v => v >= 0 || '重试次数不能为负数'] - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[29] || (_cache[29] = [ - _createTextVNode("mdi-refresh") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }) - ]), - _: 1 - }), - _createVNode(_component_v_expansion_panels, { - variant: "accordion", - class: "mt-4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_expansion_panel, null, { - default: _withCtx(() => [ - _createVNode(_component_v_expansion_panel_title, null, { - default: _withCtx(() => [ - _createVNode(_component_v_icon, { class: "mr-2" }, { - default: _withCtx(() => _cache[30] || (_cache[30] = [ - _createTextVNode("mdi-cog") - ])), - _: 1 - }), - _cache[31] || (_cache[31] = _createTextVNode(" 高级选项 ")) - ]), - _: 1 - }), - _createVNode(_component_v_expansion_panel_text, null, { - default: _withCtx(() => [ - _createVNode(_component_v_row, null, { - default: _withCtx(() => [ - _createVNode(_component_v_col, { - cols: "12", - md: "4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_switch, { - modelValue: config.discard_rules, - "onUpdate:modelValue": _cache[14] || (_cache[14] = $event => ((config.discard_rules) = $event)), - label: "丢弃订阅规则", - color: "primary", - inset: "", - hint: "不保留订阅配置文件的rules字段" - }, null, 8, ["modelValue"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_switch, { - modelValue: config.enable_acl4ssr, - "onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ((config.enable_acl4ssr) = $event)), - label: "ACL4SSR规则集", - color: "primary", - inset: "", - hint: "启用ACL4SSR规则集" - }, null, 8, ["modelValue"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_switch, { - modelValue: config.group_by_region, - "onUpdate:modelValue": _cache[16] || (_cache[16] = $event => ((config.group_by_region) = $event)), - label: "按大洲分组节点", - color: "primary", - inset: "", - hint: "启用后根据名称,将节点添加到代理组" - }, null, 8, ["modelValue"]) - ]), - _: 1 - }) - ]), - _: 1 - }), - _createVNode(_component_v_row, null, { - default: _withCtx(() => [ - _createVNode(_component_v_col, { - cols: "12", - md: "4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.ruleset_prefix, - "onUpdate:modelValue": _cache[17] || (_cache[17] = $event => ((config.ruleset_prefix) = $event)), - label: "规则集前缀", - variant: "outlined", - placeholder: "📂<=", - rules: [v => !!v || '规则集前缀不能为空'], - hint: "为生成的规则集添加前缀" - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[32] || (_cache[32] = [ - _createTextVNode("mdi-palette") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.acl4ssr_prefix, - "onUpdate:modelValue": _cache[18] || (_cache[18] = $event => ((config.acl4ssr_prefix) = $event)), - label: "ACL4SSR 规则集前缀", - variant: "outlined", - placeholder: "🗂️=>", - rules: [v => !!v || '规则集前缀不能为空'], - hint: "ACL4SSR 规则集前缀" - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "primary" }, { - default: _withCtx(() => _cache[33] || (_cache[33] = [ - _createTextVNode("mdi-palette") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.refresh_delay, - "onUpdate:modelValue": _cache[19] || (_cache[19] = $event => ((config.refresh_delay) = $event)), - modelModifiers: { number: true }, - label: "刷新延迟", - variant: "outlined", - type: "number", - min: "1", - max: "30", - suffix: "秒", - hint: "通知Clash刷新规则集的延迟时间", - rules: [v => v >= 0 || '刷新延迟不能为负数'] - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[34] || (_cache[34] = [ - _createTextVNode("mdi-clock-outline") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue"]) - ]), - _: 1 - }), - _createVNode(_component_v_alert, { - type: "info", - text: "", - class: "mb-6", - variant: "tonal" - }, { - default: _withCtx(() => _cache[40] || (_cache[40] = [ - _createTextVNode(" 配置说明参考: "), - _createElementVNode("a", { - href: "https://github.com/wumode/MoviePilot-Plugins/tree/main/plugins.v2/clashruleprovider/README.md", - target: "_blank" - }, "README", -1) - ])), - _: 1 - }), - _createVNode(_component_v_card_actions, null, { - default: _withCtx(() => [ - _createVNode(_component_v_btn, { - color: "primary", - onClick: notifySwitch - }, { - default: _withCtx(() => [ - _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[41] || (_cache[41] = [ - _createTextVNode("mdi-view-dashboard-edit") - ])), - _: 1 - }), - _cache[42] || (_cache[42] = _createTextVNode(" 规则 ")) - ]), - _: 1 - }), - _createVNode(_component_v_btn, { - color: "secondary", - onClick: resetForm - }, { - default: _withCtx(() => _cache[43] || (_cache[43] = [ - _createTextVNode("重置") - ])), - _: 1 - }), - _createVNode(_component_v_btn, { - color: "info", - onClick: testConnection, - loading: testing.value - }, { - default: _withCtx(() => _cache[44] || (_cache[44] = [ - _createTextVNode("测试连接") - ])), - _: 1 - }, 8, ["loading"]), - _createVNode(_component_v_spacer), - _createVNode(_component_v_btn, { - color: "primary", - disabled: !isFormValid.value, - onClick: saveConfig, - loading: saving.value - }, { - default: _withCtx(() => _cache[45] || (_cache[45] = [ - _createTextVNode(" 保存配置 ") - ])), - _: 1 - }, 8, ["disabled", "loading"]) - ]), - _: 1 - }), - (testResult.show) - ? (_openBlock(), _createBlock(_component_v_alert, { - key: 0, - type: testResult.success ? 'success' : 'error', - variant: "tonal", - closable: "", - class: "ma-4 mt-0", - "onClick:close": _cache[21] || (_cache[21] = $event => (testResult.show = false)) - }, { - default: _withCtx(() => [ - _createElementVNode("div", _hoisted_2, [ - _createVNode(_component_v_icon, { class: "mr-2" }, { - default: _withCtx(() => [ - _createTextVNode(_toDisplayString(testResult.success ? 'mdi-check-circle' : 'mdi-alert-circle'), 1) - ]), - _: 1 - }), - _createElementVNode("div", null, [ - _createElementVNode("div", _hoisted_3, _toDisplayString(testResult.title), 1), - _createElementVNode("div", _hoisted_4, _toDisplayString(testResult.message), 1) - ]) - ]) - ]), - _: 1 - }, 8, ["type"])) - : _createCommentVNode("", true) - ]), - _: 1 - }) - ])) -} -} - -}; -const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-bd2044a8"]]); - -export { ConfigComponent as default }; diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-xrxN2F1l.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-xrxN2F1l.css deleted file mode 100644 index d2397e6..0000000 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-xrxN2F1l.css +++ /dev/null @@ -1,5 +0,0 @@ - -.plugin-config[data-v-bd2044a8] { - max-width: 800px; - margin: 0 auto; -} diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-DEFRy9WP.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BFVr4jq_.js similarity index 99% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-DEFRy9WP.js rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BFVr4jq_.js index 6922411..9d41b2d 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-DEFRy9WP.js +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BFVr4jq_.js @@ -15118,6 +15118,9 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ scales: { x: { display: true, + grid: { + display: false + }, ticks: { maxRotation: 0, minRotation: 0, @@ -15135,6 +15138,9 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ }, y: { beginAtZero: true, + grid: { + color: "rgba(121, 114, 235, 0.1)" + }, ticks: { callback: function(value) { return formatBytes(value) + "/s"; @@ -15169,6 +15175,9 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ scales: { x: { display: true, + grid: { + display: false + }, ticks: { maxRotation: 0, minRotation: 0, @@ -15185,6 +15194,9 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ } }, y: { + grid: { + color: "rgba(121, 114, 235, 0.1)" + }, beginAtZero: true, ticks: { callback: function(value) { @@ -15245,7 +15257,8 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ if (refreshTimer) { clearInterval(refreshTimer); } - if (eventSource) eventSource.close(); + if (connectionsEvtSource) connectionsEvtSource.close(); + if (evtSource) evtSource.close(); }); const props = __props; return (_ctx, _cache) => { @@ -15425,7 +15438,10 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ }) ]), _: 1 - })) : (_openBlock(), _createBlock(_component_v_card, { key: 1 }, { + })) : (_openBlock(), _createBlock(_component_v_card, { + key: 1, + class: "h-100" + }, { default: _withCtx(() => [ _createVNode(_component_v_card_item, null, { default: _withCtx(() => [ @@ -15444,13 +15460,13 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ _createElementVNode("div", _hoisted_9, [ clashInfo.value.version.meta ? (_openBlock(), _createBlock(_component_v_img, { key: 0, - src: "https://raw.githubusercontent.com/MetaCubeX/mihomo/refs/heads/Meta/Meta.png", + src: "/api/v1/system/img/0?imgurl=https://raw.githubusercontent.com/MetaCubeX/mihomo/refs/heads/Meta/Meta.png", alt: "Logo", "max-height": "48", contain: "" })) : (_openBlock(), _createBlock(_component_v_img, { key: 1, - src: "https://raw.githubusercontent.com/jxxghp/MoviePilot-Plugins/refs/heads/main/icons/Clash_A.png", + src: "/api/v1/system/img/0?imgurl=https://raw.githubusercontent.com/jxxghp/MoviePilot-Plugins/refs/heads/main/icons/Clash_A.png", alt: "Logo", "max-height": "48", contain: "" @@ -15479,7 +15495,7 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ ]), _createElementVNode("div", _hoisted_14, [ _cache[11] || (_cache[11] = _createElementVNode("span", null, "规则数量", -1)), - _createElementVNode("span", null, _toDisplayString(clashInfo.value.rules.rules.length), 1) + _createElementVNode("span", null, _toDisplayString(clashInfo.value.rules?.rules.length), 1) ]), _createElementVNode("div", _hoisted_15, [ _cache[12] || (_cache[12] = _createElementVNode("span", null, "规则集数量", -1)), @@ -15498,6 +15514,6 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ } }); -const DashboardComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-6c8bed46"]]); +const DashboardComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-9093476d"]]); export { DashboardComponent as default }; diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-IipjE6HA.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-D6WU_Ejn.css similarity index 51% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-IipjE6HA.css rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-D6WU_Ejn.css index 2a5fa51..6a17a70 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-IipjE6HA.css +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-D6WU_Ejn.css @@ -1,4 +1,4 @@ -.dashboard-widget[data-v-6c8bed46] { +.dashboard-widget[data-v-9093476d] { font-family: 'Inter', sans-serif; } diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DQjiFgWw.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DIAzDhN_.css similarity index 67% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DQjiFgWw.css rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DIAzDhN_.css index c4ca2f7..77cd8ff 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DQjiFgWw.css +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DIAzDhN_.css @@ -1,14 +1,13 @@ -.plugin-page[data-v-eae2a1d4] { - max-width: 1200px; +.plugin-page[data-v-fd7b621d] { margin: 0 auto; } /* 使卡片等宽并适应移动端 */ -.d-flex.flex-wrap[data-v-eae2a1d4] { +.d-flex.flex-wrap[data-v-fd7b621d] { gap: 16px; } -.url-display[data-v-eae2a1d4] { +.url-display[data-v-fd7b621d] { word-break: break-all; padding: 8px; background: rgba(0, 0, 0, 0.05); @@ -17,19 +16,19 @@ /* 移动端堆叠布局 */ @media (max-width: 768px) { -.d-flex.flex-wrap[data-v-eae2a1d4] { +.d-flex.flex-wrap[data-v-fd7b621d] { flex-direction: column; } } /* Add visual distinction between sections */ -.ruleset-section[data-v-eae2a1d4] { +.ruleset-section[data-v-fd7b621d] { border: 1px solid #e0e0e0; border-radius: 4px; padding: 16px; background-color: #f5f5f5; } -.top-section[data-v-eae2a1d4] { +.top-section[data-v-fd7b621d] { border: 1px solid #e0e0e0; border-radius: 4px; padding: 16px; @@ -37,12 +36,15 @@ } /* Optional: Add different border colors to further distinguish */ -.ruleset-section[data-v-eae2a1d4] { +.ruleset-section[data-v-fd7b621d] { border-left: 4px solid #2196F3; /* Blue accent */ } -.top-section[data-v-eae2a1d4] { +.top-section[data-v-fd7b621d] { border-left: 4px solid #4CAF50; /* Green accent */ } -.drag-handle[data-v-eae2a1d4] { +.drag-handle[data-v-fd7b621d] { cursor: move; } +.gap-2[data-v-fd7b621d] { + gap: 8px; +} diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page--ZdI8TQS.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-gjHBCfnd.js similarity index 89% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page--ZdI8TQS.js rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-gjHBCfnd.js index ae5d611..6807718 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page--ZdI8TQS.js +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-gjHBCfnd.js @@ -3849,7 +3849,7 @@ var jsYaml = { safeDump: safeDump }; -const {createTextVNode:_createTextVNode,resolveComponent:_resolveComponent,withCtx:_withCtx,createVNode:_createVNode,toDisplayString:_toDisplayString,openBlock:_openBlock,createBlock:_createBlock,createCommentVNode:_createCommentVNode,createElementVNode:_createElementVNode,withModifiers:_withModifiers,normalizeClass:_normalizeClass,mergeProps:_mergeProps,createElementBlock:_createElementBlock} = await importShared('vue'); +const {createTextVNode:_createTextVNode,resolveComponent:_resolveComponent,withCtx:_withCtx,createVNode:_createVNode,toDisplayString:_toDisplayString,openBlock:_openBlock,createBlock:_createBlock,createCommentVNode:_createCommentVNode,createElementVNode:_createElementVNode,withModifiers:_withModifiers,normalizeClass:_normalizeClass,mergeProps:_mergeProps,renderList:_renderList,Fragment:_Fragment,createElementBlock:_createElementBlock} = await importShared('vue'); const _hoisted_1 = { class: "plugin-page" }; @@ -3911,31 +3911,27 @@ const _hoisted_41 = { class: "d-flex justify-space-between text-body-2 border-b const _hoisted_42 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; const _hoisted_43 = { class: "text-white" }; const _hoisted_44 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; -const _hoisted_45 = { class: "text-white" }; +const _hoisted_45 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; const _hoisted_46 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; -const _hoisted_47 = { class: "text-white" }; -const _hoisted_48 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; -const _hoisted_49 = { class: "text-white" }; -const _hoisted_50 = { class: "d-flex justify-space-between text-body-2" }; -const _hoisted_51 = { class: "text-white" }; -const _hoisted_52 = { +const _hoisted_47 = { class: "d-flex justify-space-between text-body-2" }; +const _hoisted_48 = { key: 0, class: "d-flex flex-column align-start ga-2" }; -const _hoisted_53 = { class: "d-flex align-center" }; -const _hoisted_54 = { class: "pl-6 text-wrap text-body-2" }; -const _hoisted_55 = { class: "my-2 d-flex align-center" }; -const _hoisted_56 = { class: "d-flex align-center" }; -const _hoisted_57 = { class: "pl-6 text-wrap text-body-2" }; -const _hoisted_58 = ["href"]; -const _hoisted_59 = { +const _hoisted_49 = { class: "d-flex align-center" }; +const _hoisted_50 = { class: "pl-6 d-flex flex-wrap gap-2 mt-2" }; +const _hoisted_51 = { class: "my-2 d-flex align-center" }; +const _hoisted_52 = { class: "d-flex align-center" }; +const _hoisted_53 = { class: "pl-6 text-wrap text-body-2" }; +const _hoisted_54 = ["href"]; +const _hoisted_55 = { key: 1, class: "text-grey" }; -const _hoisted_60 = { class: "d-flex align-center" }; -const _hoisted_61 = { class: "d-flex justify-space-between mb-2" }; -const _hoisted_62 = { class: "d-flex justify-space-between mb-2" }; -const _hoisted_63 = { class: "d-flex justify-space-between text-caption text-grey" }; +const _hoisted_56 = { class: "mb-2" }; +const _hoisted_57 = { class: "d-flex justify-space-between mb-2" }; +const _hoisted_58 = { class: "d-flex justify-space-between mb-2" }; +const _hoisted_59 = { class: "d-flex justify-space-between text-caption text-grey" }; const {ref,onMounted,computed} = await importShared('vue'); // v-data-table 的 headers 定义 @@ -3995,6 +3991,7 @@ const extraProxiesHeaders = ref([ {title: '操作', key: 'actions', sortable: false}, ]); const activeTab = ref(0); +const activeSubscriptionTab = ref(0); const page = ref(1); const pageRuleset = ref(1); const pageRulProviders = ref(1); @@ -4041,7 +4038,6 @@ const filteredRulesetRules = computed(() => { ); }); - const pageCountRuleset = computed(() => { if (itemsPerPageRuleset.value === -1) { return 1; @@ -4178,8 +4174,7 @@ const extraRuleProviders = ref([]); const status = ref('running'); const rulesetPrefix = ref('Custom_'); const lastUpdated = ref(''); -const updatingSubscription = ref(false); -const subscriptionUrl = ref(''); +const refreshingSubscription = ref(false); const yamlDialog = ref(false); const displayedYaml = ref(''); const searchTopRule = ref(''); @@ -4230,7 +4225,7 @@ const sortedRulesetRules = computed(() => [...rulesetRules.value].sort((a, b) => const showAdditionalParams = computed(() => { return ['IP-CIDR', 'IP-CIDR6', 'IP-ASN', 'GEOIP'].includes(newRule.value.type); }); -const ruleProviders = ref([]); +const ruleProviders = ref({}); const ruleProviderNames = computed(() => Object.keys(ruleProviders.value)); // 规则类型和动作选项 const ruleTypes = computed(() => { @@ -4249,14 +4244,18 @@ const ruleTypes = computed(() => { } return allTypes; }); - +const importExtraProxiesPlaceholderText = computed(() => { + return importExtraProxies.value.type === 'YAML' + ? 'proxies: []' + : 'vless://xxxx'; +}); const proxyGroupTypes = ref(['select', 'url-test', 'fallback', 'load-balance', 'relay']); ref(['Direct', 'Reject', 'RejectDrop', 'Compatible', 'Pass', 'Dns', 'Relay', 'Selector', 'Fallback', 'URLTest', 'LoadBalance', 'Shadowsocks', 'ShadowsocksR', 'Snell', 'Socks5', 'Http', 'Vmess', 'Vless', 'Trojan', 'Hysteria', 'Hysteria2', 'WireGuard', 'Tuic', 'Ssh',]); const strategyTypes = ref(['round-robin', 'consistent-hashing', 'sticky-sessions']); const importRuleTypes = ['YAML']; -const importProxiesTypes = ['YAML']; +const importProxiesTypes = ['YAML', 'LINK']; const ruleProviderTypes = ['http', 'file', 'inline']; const ruleProviderBehaviorTypes = ['domain', 'ipcidr', 'classical']; const ruleProviderFormatTypes = ['yaml', 'text']; @@ -4272,8 +4271,16 @@ const subscriptionInfo = ref({ expire: 0, last_update: 0, used_percentage: 0, - rule_size: 0 + rule_size: 0, + proxy_num: 0, }); + +const subscriptionsInfo = ref({}); + +const clashInfo = ref({ + rule_size: 0, +}); + // 自定义事件,用于通知主应用刷新数据 const emit = __emit; @@ -4444,11 +4451,7 @@ function editRule(priority, type = 'top') { } function editRuleProvider(name) { - console.log("[DEBUG] extraRuleProviders:", extraRuleProviders.value); - console.log("[DEBUG] name:", name); const ruleProvider = extraRuleProviders.value.find(r => r.name === name); - console.log("[DEBUG] ruleProvider found:", ruleProvider); - if (ruleProvider) { editingRuleProviderName.value = name; newRuleProvider.value = { @@ -4497,7 +4500,16 @@ async function importExtraProxiesFun() { type: importExtraProxies.value.type, payload: importExtraProxies.value.payload }; - await props.api.post('/plugin/ClashRuleProvider/extra-proxies', requestData); + const result = await props.api.post('/plugin/ClashRuleProvider/extra-proxies', requestData); + if (!result.success) { + error.value = result.message || '节点导入失败'; + snackbar.value = { + show: true, + message: '节点导入失败: ' + (result.message || '未知错误'), + color: 'error' + }; + return + } importExtraProxiesDialog.value = false; await refreshData(); // 显示成功提示 @@ -4523,7 +4535,16 @@ async function saveProxyGroups() { const requestData = { proxy_group: newProxyGroup.value, }; - await props.api.post('/plugin/ClashRuleProvider/proxy-group', requestData); + const result = await props.api.post('/plugin/ClashRuleProvider/proxy-group', requestData); + if (!result.success) { + error.value = result.message || '导入代理组失败'; + snackbar.value = { + show: true, + message: '导入代理组失败: ' + (result.message || '未知错误'), + color: 'error' + }; + return + } proxyGroupDialog.value = false; await refreshData(); snackbar.value = { @@ -4532,10 +4553,10 @@ async function saveProxyGroups() { color: 'success' }; } catch (err) { - error.value = err.message || '导入规则失败'; + error.value = err.message || '导入代理组失败'; snackbar.value = { show: true, - message: '导入规则失败: ' + (err.message || '未知错误'), + message: '导入代理组失败: ' + (err.message || '未知错误'), color: 'error' }; } @@ -4665,16 +4686,16 @@ async function deleteExtraProxies(name) { } // 更新订阅 -async function updateSubscription() { - if (!subscriptionUrl.value) { +async function updateSubscription(url) { + if (!url) { error.value = '请先输入订阅URL'; return } - updatingSubscription.value = true; + refreshingSubscription.value = true; try { await props.api.put('plugin/ClashRuleProvider/subscription', { - url: subscriptionUrl.value + url: url }); // 显示成功提示 snackbar.value = { @@ -4687,10 +4708,18 @@ async function updateSubscription() { console.error('更新订阅失败:', err); error.value = err.message; } finally { - updatingSubscription.value = false; + refreshingSubscription.value = false; } } +function extractDomain(url) { + try { + const domain = new URL(url).hostname; + return domain.startsWith('www.') ? domain.substring(4) : domain + } catch { + return url + } +} // 获取和刷新数据 async function refreshData() { @@ -4704,7 +4733,6 @@ async function refreshData() { state, response, response_ruleset, - subscription, outboundsResponse, providersResponse, proxyGroupsResponse, @@ -4714,7 +4742,6 @@ async function refreshData() { props.api.get('/plugin/ClashRuleProvider/status'), props.api.get('/plugin/ClashRuleProvider/rules?rule_type=top'), props.api.get('/plugin/ClashRuleProvider/rules?rule_type=ruleset'), - props.api.get('/plugin/ClashRuleProvider/subscription'), props.api.get('/plugin/ClashRuleProvider/clash-outbound'), props.api.get('/plugin/ClashRuleProvider/rule-providers'), props.api.get('/plugin/ClashRuleProvider/proxy-groups'), @@ -4727,22 +4754,36 @@ async function refreshData() { subUrl.value = state?.data?.sub_url || ''; if (state?.data?.subscription_info) { - subscriptionInfo.value = { - ...state.data.subscription_info, - used_percentage: calculatePercentage( - state.data.subscription_info.download, - state.data.subscription_info.total - ), - rule_size: state?.data?.clash?.rule_size - }; + subscriptionsInfo.value = {}; + Object.keys(state.data.subscription_info).forEach(url => { + const newSubInfo = { + download: 0, + upload: 0, + total: 0, + expire: 0, + last_update: 0, + used_percentage: 0, + rule_size: 0, + proxy_num: 0, + }; + Object.keys(state.data.subscription_info[url]).forEach(key => { + if (key in newSubInfo) { + newSubInfo[key] = state.data.subscription_info[url][key]; + } + }); + newSubInfo.used_percentage = calculatePercentage( + state.data.subscription_info[url]?.download || 0, + state.data.subscription_info[url]?.total || 0 + ); + subscriptionsInfo.value[url] = newSubInfo; + }); } - - rulesetPrefix.value = state?.data?.ruleset_prefix || 'Custom_'; + clashInfo.value = state?.data?.clash ?? clashInfo.value; + rulesetPrefix.value = state?.data?.ruleset_prefix || '📂<='; rules.value = response?.data.rules || []; rulesetRules.value = response_ruleset?.data.rules || []; - subscriptionUrl.value = subscription?.data.url; customOutbounds.value = outboundsResponse?.data.outbound || []; - ruleProviders.value = providersResponse?.data || {}; + ruleProviders.value = providersResponse?.data || ruleProviders.value; proxyGroups.value = proxyGroupsResponse?.data.proxy_groups || []; extraProxies.value = extraProxiesResponse?.data.extra_proxies || []; extraRuleProviders.value = extraRuleProvidersResponse?.data.rule_providers || []; @@ -4798,12 +4839,12 @@ return (_ctx, _cache) => { const _component_v_card = _resolveComponent("v-card"); const _component_v_col = _resolveComponent("v-col"); const _component_v_row = _resolveComponent("v-row"); - const _component_v_spacer = _resolveComponent("v-spacer"); const _component_v_expansion_panel_title = _resolveComponent("v-expansion-panel-title"); const _component_v_progress_linear = _resolveComponent("v-progress-linear"); const _component_v_expansion_panel_text = _resolveComponent("v-expansion-panel-text"); const _component_v_expansion_panel = _resolveComponent("v-expansion-panel"); const _component_v_expansion_panels = _resolveComponent("v-expansion-panels"); + const _component_v_spacer = _resolveComponent("v-spacer"); const _component_v_card_actions = _resolveComponent("v-card-actions"); const _component_v_snackbar = _resolveComponent("v-snackbar"); const _component_v_form = _resolveComponent("v-form"); @@ -4825,7 +4866,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[73] || (_cache[73] = [ + default: _withCtx(() => _cache[75] || (_cache[75] = [ _createTextVNode("mdi-close") ])), _: 1 @@ -4836,7 +4877,7 @@ return (_ctx, _cache) => { ]), default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[72] || (_cache[72] = [ + default: _withCtx(() => _cache[74] || (_cache[74] = [ _createTextVNode("Clash Rule Provider") ])), _: 1 @@ -4872,31 +4913,31 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[74] || (_cache[74] = [ + default: _withCtx(() => _cache[76] || (_cache[76] = [ _createTextVNode("规则集规则") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[75] || (_cache[75] = [ + default: _withCtx(() => _cache[77] || (_cache[77] = [ _createTextVNode("置顶规则") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[76] || (_cache[76] = [ + default: _withCtx(() => _cache[78] || (_cache[78] = [ _createTextVNode("代理组") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[77] || (_cache[77] = [ + default: _withCtx(() => _cache[79] || (_cache[79] = [ _createTextVNode("出站代理") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[78] || (_cache[78] = [ + default: _withCtx(() => _cache[80] || (_cache[80] = [ _createTextVNode("规则集合") ])), _: 1 @@ -4914,7 +4955,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_3, [ _createElementVNode("div", _hoisted_4, [ _createElementVNode("div", _hoisted_5, [ - _cache[81] || (_cache[81] = _createElementVNode("div", { class: "text-h6" }, "规则集规则", -1)), + _cache[83] || (_cache[83] = _createElementVNode("div", { class: "text-h6" }, "规则集规则", -1)), _createElementVNode("div", _hoisted_6, [ _createVNode(_component_v_text_field, { modelValue: searchRulesetRule.value, @@ -4933,12 +4974,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[79] || (_cache[79] = [ + default: _withCtx(() => _cache[81] || (_cache[81] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[80] || (_cache[80] = _createTextVNode(" 添加规则 ")) + _cache[82] || (_cache[82] = _createTextVNode(" 添加规则 ")) ]), _: 1 }) @@ -4967,7 +5008,7 @@ return (_ctx, _cache) => { }, [ _createElementVNode("td", null, [ _createVNode(_component_v_icon, { class: "drag-handle" }, { - default: _withCtx(() => _cache[82] || (_cache[82] = [ + default: _withCtx(() => _cache[84] || (_cache[84] = [ _createTextVNode("mdi-drag") ])), _: 1 @@ -4998,7 +5039,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[83] || (_cache[83] = [ + default: _withCtx(() => _cache[85] || (_cache[85] = [ _createTextVNode("mdi-pencil") ])), _: 1 @@ -5015,7 +5056,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[84] || (_cache[84] = [ + default: _withCtx(() => _cache[86] || (_cache[86] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5055,7 +5096,7 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "items", "search", "page", "items-per-page", "items-per-page-options"]), - _cache[85] || (_cache[85] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对规则集中规则的修改可以在Clash中立即生效。 ", -1)) + _cache[87] || (_cache[87] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对规则集中规则的修改可以在Clash中立即生效。 ", -1)) ]) ]), _: 1 @@ -5065,7 +5106,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_11, [ _createElementVNode("div", _hoisted_12, [ _createElementVNode("div", _hoisted_13, [ - _cache[90] || (_cache[90] = _createElementVNode("div", { class: "text-h6" }, "置顶规则", -1)), + _cache[92] || (_cache[92] = _createElementVNode("div", { class: "text-h6" }, "置顶规则", -1)), _createElementVNode("div", _hoisted_14, [ _createVNode(_component_v_text_field, { modelValue: searchTopRule.value, @@ -5085,12 +5126,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[86] || (_cache[86] = [ + default: _withCtx(() => _cache[88] || (_cache[88] = [ _createTextVNode("mdi-import") ])), _: 1 }), - _cache[87] || (_cache[87] = _createTextVNode(" 导入规则 ")) + _cache[89] || (_cache[89] = _createTextVNode(" 导入规则 ")) ]), _: 1 }), @@ -5100,12 +5141,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[88] || (_cache[88] = [ + default: _withCtx(() => _cache[90] || (_cache[90] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[89] || (_cache[89] = _createTextVNode(" 添加规则 ")) + _cache[91] || (_cache[91] = _createTextVNode(" 添加规则 ")) ]), _: 1 }) @@ -5134,7 +5175,7 @@ return (_ctx, _cache) => { }, [ _createElementVNode("td", null, [ _createVNode(_component_v_icon, { class: "drag-handle" }, { - default: _withCtx(() => _cache[91] || (_cache[91] = [ + default: _withCtx(() => _cache[93] || (_cache[93] = [ _createTextVNode("mdi-drag") ])), _: 1 @@ -5165,7 +5206,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[92] || (_cache[92] = [ + default: _withCtx(() => _cache[94] || (_cache[94] = [ _createTextVNode("mdi-pencil") ])), _: 1 @@ -5183,7 +5224,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[93] || (_cache[93] = [ + default: _withCtx(() => _cache[95] || (_cache[95] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5197,7 +5238,7 @@ return (_ctx, _cache) => { activator: "parent", location: "top" }, { - default: _withCtx(() => _cache[94] || (_cache[94] = [ + default: _withCtx(() => _cache[96] || (_cache[96] = [ _createTextVNode(" 根据规则集自动添加 ") ])), _: 1 @@ -5235,8 +5276,8 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "search", "items", "page", "items-per-page"]), - _cache[95] || (_cache[95] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *置顶规则用于管理来自规则集的匹配规则,这些规则会动态更新。 ", -1)), - _cache[96] || (_cache[96] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对置顶规则的修改只有Clash更新配置后才会生效。 ", -1)) + _cache[97] || (_cache[97] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *置顶规则用于管理来自规则集的匹配规则,这些规则会动态更新。 ", -1)), + _cache[98] || (_cache[98] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对置顶规则的修改只有Clash更新配置后才会生效。 ", -1)) ]) ]), _: 1 @@ -5246,7 +5287,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_19, [ _createElementVNode("div", _hoisted_20, [ _createElementVNode("div", _hoisted_21, [ - _cache[99] || (_cache[99] = _createElementVNode("div", { class: "text-h6" }, "代理组", -1)), + _cache[101] || (_cache[101] = _createElementVNode("div", { class: "text-h6" }, "代理组", -1)), _createElementVNode("div", _hoisted_22, [ _createVNode(_component_v_btn, { color: "primary", @@ -5254,12 +5295,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[97] || (_cache[97] = [ + default: _withCtx(() => _cache[99] || (_cache[99] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[98] || (_cache[98] = _createTextVNode(" 添加代理组 ")) + _cache[100] || (_cache[100] = _createTextVNode(" 添加代理组 ")) ]), _: 1 }) @@ -5290,7 +5331,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[100] || (_cache[100] = [ + default: _withCtx(() => _cache[102] || (_cache[102] = [ _createTextVNode("mdi-code-json") ])), _: 1 @@ -5307,7 +5348,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[101] || (_cache[101] = [ + default: _withCtx(() => _cache[103] || (_cache[103] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5347,7 +5388,7 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "items", "page", "items-per-page"]), - _cache[102] || (_cache[102] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) + _cache[104] || (_cache[104] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) ]) ]), _: 1 @@ -5357,7 +5398,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_26, [ _createElementVNode("div", _hoisted_27, [ _createElementVNode("div", _hoisted_28, [ - _cache[105] || (_cache[105] = _createElementVNode("div", { class: "text-h6" }, "出站代理", -1)), + _cache[107] || (_cache[107] = _createElementVNode("div", { class: "text-h6" }, "出站代理", -1)), _createElementVNode("div", _hoisted_29, [ _createVNode(_component_v_btn, { color: "primary", @@ -5365,12 +5406,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[103] || (_cache[103] = [ + default: _withCtx(() => _cache[105] || (_cache[105] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[104] || (_cache[104] = _createTextVNode(" 导入节点 ")) + _cache[106] || (_cache[106] = _createTextVNode(" 导入节点 ")) ]), _: 1 }) @@ -5403,7 +5444,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[106] || (_cache[106] = [ + default: _withCtx(() => _cache[108] || (_cache[108] = [ _createTextVNode("mdi-code-json") ])), _: 1 @@ -5420,7 +5461,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[107] || (_cache[107] = [ + default: _withCtx(() => _cache[109] || (_cache[109] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5460,7 +5501,7 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "items", "page", "items-per-page"]), - _cache[108] || (_cache[108] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) + _cache[110] || (_cache[110] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) ]) ]), _: 1 @@ -5470,7 +5511,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_33, [ _createElementVNode("div", _hoisted_34, [ _createElementVNode("div", _hoisted_35, [ - _cache[111] || (_cache[111] = _createElementVNode("div", { class: "text-h6" }, "规则集合", -1)), + _cache[113] || (_cache[113] = _createElementVNode("div", { class: "text-h6" }, "规则集合", -1)), _createElementVNode("div", _hoisted_36, [ _createVNode(_component_v_text_field, { modelValue: searchRuleProviders.value, @@ -5489,12 +5530,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[109] || (_cache[109] = [ + default: _withCtx(() => _cache[111] || (_cache[111] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[110] || (_cache[110] = _createTextVNode(" 添加规则集合 ")) + _cache[112] || (_cache[112] = _createTextVNode(" 添加规则集合 ")) ]), _: 1 }) @@ -5529,7 +5570,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[112] || (_cache[112] = [ + default: _withCtx(() => _cache[114] || (_cache[114] = [ _createTextVNode("mdi-pencil") ])), _: 1 @@ -5546,7 +5587,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[113] || (_cache[113] = [ + default: _withCtx(() => _cache[115] || (_cache[115] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5606,7 +5647,7 @@ return (_ctx, _cache) => { _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, { class: "text-h6 font-weight-medium" }, { - default: _withCtx(() => _cache[114] || (_cache[114] = [ + default: _withCtx(() => _cache[116] || (_cache[116] = [ _createTextVNode("状态信息") ])), _: 1 @@ -5615,7 +5656,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createElementVNode("div", _hoisted_40, [ _createElementVNode("div", _hoisted_41, [ - _cache[115] || (_cache[115] = _createElementVNode("span", { class: "text-white" }, "状态", -1)), + _cache[117] || (_cache[117] = _createElementVNode("span", null, "状态", -1)), _createVNode(_component_v_chip, { size: "small", color: status.value === 'running' ? 'success' : 'warning' @@ -5627,24 +5668,24 @@ return (_ctx, _cache) => { }, 8, ["color"]) ]), _createElementVNode("div", _hoisted_42, [ - _cache[116] || (_cache[116] = _createElementVNode("span", { class: "text-white" }, "订阅配置规则数", -1)), + _cache[118] || (_cache[118] = _createElementVNode("span", null, "订阅配置规则数", -1)), _createElementVNode("span", _hoisted_43, _toDisplayString(subscriptionInfo.value.rule_size), 1) ]), _createElementVNode("div", _hoisted_44, [ - _cache[117] || (_cache[117] = _createElementVNode("span", { class: "text-white" }, "置顶规则数", -1)), - _createElementVNode("span", _hoisted_45, _toDisplayString(sortedRules.value.length), 1) + _cache[119] || (_cache[119] = _createElementVNode("span", null, "置顶规则数", -1)), + _createElementVNode("span", null, _toDisplayString(sortedRules.value.length), 1) + ]), + _createElementVNode("div", _hoisted_45, [ + _cache[120] || (_cache[120] = _createElementVNode("span", null, "规则集规则数", -1)), + _createElementVNode("span", null, _toDisplayString(sortedRulesetRules.value.length), 1) ]), _createElementVNode("div", _hoisted_46, [ - _cache[118] || (_cache[118] = _createElementVNode("span", { class: "text-white" }, "规则集规则数", -1)), - _createElementVNode("span", _hoisted_47, _toDisplayString(sortedRulesetRules.value.length), 1) + _cache[121] || (_cache[121] = _createElementVNode("span", null, "代理组数", -1)), + _createElementVNode("span", null, _toDisplayString(proxyGroups.value.length), 1) ]), - _createElementVNode("div", _hoisted_48, [ - _cache[119] || (_cache[119] = _createElementVNode("span", { class: "text-white" }, "代理组数", -1)), - _createElementVNode("span", _hoisted_49, _toDisplayString(proxyGroups.value.length), 1) - ]), - _createElementVNode("div", _hoisted_50, [ - _cache[120] || (_cache[120] = _createElementVNode("span", { class: "text-white" }, "最后更新", -1)), - _createElementVNode("span", _hoisted_51, _toDisplayString(lastUpdated.value), 1) + _createElementVNode("div", _hoisted_47, [ + _cache[122] || (_cache[122] = _createElementVNode("span", null, "最后更新", -1)), + _createElementVNode("span", null, _toDisplayString(lastUpdated.value), 1) ]) ]) ]), @@ -5665,7 +5706,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_card_title, { class: "d-flex justify-space-between align-center" }, { default: _withCtx(() => [ - _cache[122] || (_cache[122] = _createElementVNode("span", { class: "text-h6 font-weight-medium" }, "订阅链接", -1)), + _cache[124] || (_cache[124] = _createElementVNode("span", { class: "text-h6 font-weight-medium" }, "订阅链接", -1)), _createVNode(_component_v_tooltip, { location: "top", text: "复制链接" @@ -5681,7 +5722,7 @@ return (_ctx, _cache) => { }), { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[121] || (_cache[121] = [ + default: _withCtx(() => _cache[123] || (_cache[123] = [ _createTextVNode("mdi-content-copy") ])), _: 1 @@ -5698,50 +5739,64 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_card_text, null, { default: _withCtx(() => [ - (subscriptionUrl.value && subUrl.value) - ? (_openBlock(), _createElementBlock("div", _hoisted_52, [ - _createElementVNode("div", _hoisted_53, [ + (subUrl.value) + ? (_openBlock(), _createElementBlock("div", _hoisted_48, [ + _createElementVNode("div", _hoisted_49, [ _createVNode(_component_v_icon, { color: "grey", class: "mr-2" }, { - default: _withCtx(() => _cache[123] || (_cache[123] = [ + default: _withCtx(() => _cache[125] || (_cache[125] = [ _createTextVNode("mdi-link") ])), _: 1 }), - _cache[124] || (_cache[124] = _createElementVNode("span", { class: "text-grey-darken-1" }, "原始链接:", -1)) + _cache[126] || (_cache[126] = _createElementVNode("span", { class: "text-grey-darken-1" }, "原始链接:", -1)) ]), - _createElementVNode("div", _hoisted_54, _toDisplayString(subscriptionUrl.value), 1), - _createElementVNode("div", _hoisted_55, [ + _createElementVNode("div", _hoisted_50, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(subscriptionsInfo.value, (info, url) => { + return (_openBlock(), _createElementBlock("div", { key: url }, [ + _createVNode(_component_v_chip, _mergeProps({ ref_for: true }, props, { + size: "small", + onClick: $event => (copyToClipboard(url)) + }), { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(extractDomain(url)), 1) + ]), + _: 2 + }, 1040, ["onClick"]) + ])) + }), 128)) + ]), + _createElementVNode("div", _hoisted_51, [ _createVNode(_component_v_icon, { color: "blue" }, { - default: _withCtx(() => _cache[125] || (_cache[125] = [ + default: _withCtx(() => _cache[127] || (_cache[127] = [ _createTextVNode("mdi-arrow-down-bold") ])), _: 1 }) ]), - _createElementVNode("div", _hoisted_56, [ + _createElementVNode("div", _hoisted_52, [ _createVNode(_component_v_icon, { color: "primary", class: "mr-2" }, { - default: _withCtx(() => _cache[126] || (_cache[126] = [ + default: _withCtx(() => _cache[128] || (_cache[128] = [ _createTextVNode("mdi-link-variant") ])), _: 1 }), - _cache[127] || (_cache[127] = _createElementVNode("span", { class: "text-grey-darken-1" }, "生成链接:", -1)) + _cache[129] || (_cache[129] = _createElementVNode("span", { class: "text-grey-darken-1" }, "生成链接:", -1)) ]), - _createElementVNode("div", _hoisted_57, [ + _createElementVNode("div", _hoisted_53, [ _createElementVNode("a", { href: subUrl.value, target: "_blank", class: "text-primary" - }, _toDisplayString(subUrl.value), 9, _hoisted_58) + }, _toDisplayString(subUrl.value), 9, _hoisted_54) ]) ])) - : (_openBlock(), _createElementBlock("div", _hoisted_59, "未配置订阅 URL")) + : (_openBlock(), _createElementBlock("div", _hoisted_55, "未配置订阅 URL")) ]), _: 1 }) @@ -5756,7 +5811,7 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_expansion_panels, { modelValue: expansionPanels.value, - "onUpdate:modelValue": _cache[23] || (_cache[23] = $event => ((expansionPanels).value = $event)) + "onUpdate:modelValue": _cache[25] || (_cache[25] = $event => ((expansionPanels).value = $event)) }, { default: _withCtx(() => [ _createVNode(_component_v_expansion_panel, null, { @@ -5764,96 +5819,146 @@ return (_ctx, _cache) => { _createVNode(_component_v_expansion_panel_title, null, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[128] || (_cache[128] = [ + default: _withCtx(() => _cache[130] || (_cache[130] = [ _createTextVNode("mdi-cloud-download") ])), _: 1 }), - _cache[129] || (_cache[129] = _createElementVNode("span", { class: "text-subtitle-1 font-weight-medium" }, "订阅管理", -1)), - _createVNode(_component_v_spacer), - _createElementVNode("div", _hoisted_60, [ - (subscriptionInfo.value.last_update) - ? (_openBlock(), _createBlock(_component_v_chip, { - key: 0, - size: "small", - color: "light-blue", - class: "ml-2" - }, { - default: _withCtx(() => [ - _createTextVNode(" 更新时间:" + _toDisplayString(formatTimestamp(subscriptionInfo.value.last_update)), 1) - ]), - _: 1 - })) - : _createCommentVNode("", true), - (subscriptionInfo.value.expire) - ? (_openBlock(), _createBlock(_component_v_chip, { - key: 1, - size: "small", - color: getExpireColor(subscriptionInfo.value.expire), - class: "ml-2" - }, { - default: _withCtx(() => [ - _createTextVNode(" 到期时间:" + _toDisplayString(formatTimestamp(subscriptionInfo.value.expire)), 1) - ]), - _: 1 - }, 8, ["color"])) - : _createCommentVNode("", true) - ]) + _cache[131] || (_cache[131] = _createElementVNode("span", { class: "text-subtitle-1 font-weight-medium" }, "订阅管理", -1)) ]), _: 1 }), _createVNode(_component_v_expansion_panel_text, null, { default: _withCtx(() => [ - _createVNode(_component_v_card, { - variant: "outlined", - class: "mb-4" + _createVNode(_component_v_tabs, { + modelValue: activeSubscriptionTab.value, + "onUpdate:modelValue": _cache[23] || (_cache[23] = $event => ((activeSubscriptionTab).value = $event)), + grow: "", + class: "mb-2" }, { default: _withCtx(() => [ - _createVNode(_component_v_card_text, null, { - default: _withCtx(() => [ - _createElementVNode("div", _hoisted_61, [ - _cache[130] || (_cache[130] = _createElementVNode("span", null, "已用流量:", -1)), - _createElementVNode("strong", null, _toDisplayString(formatBytes(subscriptionInfo.value.download + subscriptionInfo.value.upload)), 1) + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(subscriptionsInfo.value, (info, url, index) => { + return (_openBlock(), _createBlock(_component_v_tab, { + key: url, + class: "rounded-pill px-4 text-caption font-weight-medium" + }, { + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(extractDomain(url)), 1) ]), - _createElementVNode("div", _hoisted_62, [ - _cache[131] || (_cache[131] = _createElementVNode("span", null, "剩余流量:", -1)), - _createElementVNode("strong", null, _toDisplayString(formatBytes(subscriptionInfo.value.total - subscriptionInfo.value.download)), 1) - ]), - _createVNode(_component_v_progress_linear, { - "model-value": subscriptionInfo.value.used_percentage, - color: getUsageColor(subscriptionInfo.value.used_percentage), - height: "10", - class: "mb-2", - rounded: "", - striped: "" - }, null, 8, ["model-value", "color"]), - _createElementVNode("div", _hoisted_63, [ - _createElementVNode("span", null, "下载:" + _toDisplayString(formatBytes(subscriptionInfo.value.download)), 1), - _createElementVNode("span", null, "上传:" + _toDisplayString(formatBytes(subscriptionInfo.value.upload)), 1), - _createElementVNode("span", null, "总量:" + _toDisplayString(formatBytes(subscriptionInfo.value.total)), 1) - ]) - ]), - _: 1 - }) + _: 2 + }, 1024)) + }), 128)) ]), _: 1 - }), - _createVNode(_component_v_btn, { - color: "primary", - onClick: updateSubscription, - loading: loading.value + }, 8, ["modelValue"]), + _createVNode(_component_v_window, { + modelValue: activeSubscriptionTab.value, + "onUpdate:modelValue": _cache[24] || (_cache[24] = $event => ((activeSubscriptionTab).value = $event)) }, { default: _withCtx(() => [ - _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[132] || (_cache[132] = [ - _createTextVNode("mdi-cloud-sync") - ])), - _: 1 - }), - _cache[133] || (_cache[133] = _createTextVNode(" 更新订阅 ")) + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(subscriptionsInfo.value, (info, url, index) => { + return (_openBlock(), _createBlock(_component_v_window_item, { + key: url, + value: index + }, { + default: _withCtx(() => [ + _createVNode(_component_v_card, { + variant: "outlined", + class: "mb-4 mt-2" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_card_text, null, { + default: _withCtx(() => [ + _createElementVNode("div", _hoisted_56, [ + (info.proxy_num != null) + ? (_openBlock(), _createBlock(_component_v_chip, { + key: 0, + size: "small", + color: "info", + class: "mr-2" + }, { + default: _withCtx(() => [ + _createTextVNode(" 节点数量:" + _toDisplayString(info.proxy_num), 1) + ]), + _: 2 + }, 1024)) + : _createCommentVNode("", true), + (info.last_update) + ? (_openBlock(), _createBlock(_component_v_chip, { + key: 1, + size: "small", + color: "light-blue", + class: "mr-2" + }, { + default: _withCtx(() => [ + _createTextVNode(" 更新时间:" + _toDisplayString(formatTimestamp(info.last_update)), 1) + ]), + _: 2 + }, 1024)) + : _createCommentVNode("", true), + (info.expire) + ? (_openBlock(), _createBlock(_component_v_chip, { + key: 2, + size: "small", + color: getExpireColor(info.expire) + }, { + default: _withCtx(() => [ + _createTextVNode(" 到期时间:" + _toDisplayString(formatTimestamp(info.expire)), 1) + ]), + _: 2 + }, 1032, ["color"])) + : _createCommentVNode("", true) + ]), + _createElementVNode("div", _hoisted_57, [ + _cache[132] || (_cache[132] = _createElementVNode("span", null, "已用流量:", -1)), + _createElementVNode("strong", null, _toDisplayString(formatBytes(info.download + info.upload)), 1) + ]), + _createElementVNode("div", _hoisted_58, [ + _cache[133] || (_cache[133] = _createElementVNode("span", null, "剩余流量:", -1)), + _createElementVNode("strong", null, _toDisplayString(formatBytes(info.total - info.download)), 1) + ]), + _createVNode(_component_v_progress_linear, { + "model-value": info.used_percentage, + color: getUsageColor(info.used_percentage), + height: "10", + class: "mb-2", + rounded: "", + striped: "" + }, null, 8, ["model-value", "color"]), + _createElementVNode("div", _hoisted_59, [ + _createElementVNode("span", null, "下载:" + _toDisplayString(formatBytes(info.download)), 1), + _createElementVNode("span", null, "上传:" + _toDisplayString(formatBytes(info.upload)), 1), + _createElementVNode("span", null, "总量:" + _toDisplayString(formatBytes(info.total)), 1) + ]) + ]), + _: 2 + }, 1024) + ]), + _: 2 + }, 1024), + _createVNode(_component_v_btn, { + color: "primary", + onClick: $event => (updateSubscription(url)), + loading: refreshingSubscription.value + }, { + default: _withCtx(() => [ + _createVNode(_component_v_icon, { left: "" }, { + default: _withCtx(() => _cache[134] || (_cache[134] = [ + _createTextVNode("mdi-cloud-sync") + ])), + _: 1 + }), + _cache[135] || (_cache[135] = _createTextVNode(" 更新订阅 ")) + ]), + _: 2 + }, 1032, ["onClick", "loading"]) + ]), + _: 2 + }, 1032, ["value"])) + }), 128)) ]), _: 1 - }, 8, ["loading"]) + }, 8, ["modelValue"]) ]), _: 1 }) @@ -5876,12 +5981,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[134] || (_cache[134] = [ + default: _withCtx(() => _cache[136] || (_cache[136] = [ _createTextVNode("mdi-refresh") ])), _: 1 }), - _cache[135] || (_cache[135] = _createTextVNode(" 刷新数据 ")) + _cache[137] || (_cache[137] = _createTextVNode(" 刷新数据 ")) ]), _: 1 }, 8, ["loading"]), @@ -5892,12 +5997,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[136] || (_cache[136] = [ + default: _withCtx(() => _cache[138] || (_cache[138] = [ _createTextVNode("mdi-cog") ])), _: 1 }), - _cache[137] || (_cache[137] = _createTextVNode(" 配置 ")) + _cache[139] || (_cache[139] = _createTextVNode(" 配置 ")) ]), _: 1 }) @@ -5906,7 +6011,7 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_snackbar, { modelValue: snackbar.value.show, - "onUpdate:modelValue": _cache[24] || (_cache[24] = $event => ((snackbar.value.show) = $event)), + "onUpdate:modelValue": _cache[26] || (_cache[26] = $event => ((snackbar.value.show) = $event)), color: snackbar.value.color, location: "bottom", class: "mb-2" @@ -5921,7 +6026,7 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_dialog, { modelValue: ruleDialog.value, - "onUpdate:modelValue": _cache[32] || (_cache[32] = $event => ((ruleDialog).value = $event)), + "onUpdate:modelValue": _cache[34] || (_cache[34] = $event => ((ruleDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ @@ -5943,7 +6048,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: newRule.value.type, - "onUpdate:modelValue": _cache[25] || (_cache[25] = $event => ((newRule.value.type) = $event)), + "onUpdate:modelValue": _cache[27] || (_cache[27] = $event => ((newRule.value.type) = $event)), items: ruleTypes.value, label: "规则类型", required: "", @@ -5953,7 +6058,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_text_field, { key: 0, modelValue: newRule.value.payload, - "onUpdate:modelValue": _cache[26] || (_cache[26] = $event => ((newRule.value.payload) = $event)), + "onUpdate:modelValue": _cache[28] || (_cache[28] = $event => ((newRule.value.payload) = $event)), label: "内容", required: "", rules: payloadRules.value, @@ -5962,7 +6067,7 @@ return (_ctx, _cache) => { : (_openBlock(), _createBlock(_component_v_select, { key: 1, modelValue: newRule.value.payload, - "onUpdate:modelValue": _cache[27] || (_cache[27] = $event => ((newRule.value.payload) = $event)), + "onUpdate:modelValue": _cache[29] || (_cache[29] = $event => ((newRule.value.payload) = $event)), items: ruleProviderNames.value, label: "选择规则集", required: "", @@ -5971,7 +6076,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue", "items", "rules"])), _createVNode(_component_v_select, { modelValue: newRule.value.action, - "onUpdate:modelValue": _cache[28] || (_cache[28] = $event => ((newRule.value.action) = $event)), + "onUpdate:modelValue": _cache[30] || (_cache[30] = $event => ((newRule.value.action) = $event)), items: actions.value, label: "出站", required: "", @@ -5981,7 +6086,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_select, { key: 2, modelValue: newRule.value.additional_params, - "onUpdate:modelValue": _cache[29] || (_cache[29] = $event => ((newRule.value.additional_params) = $event)), + "onUpdate:modelValue": _cache[31] || (_cache[31] = $event => ((newRule.value.additional_params) = $event)), label: "附加参数", items: additionalParamOptions.value, clearable: "", @@ -5994,7 +6099,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_text_field, { key: 3, modelValue: newRule.value.priority, - "onUpdate:modelValue": _cache[30] || (_cache[30] = $event => ((newRule.value.priority) = $event)), + "onUpdate:modelValue": _cache[32] || (_cache[32] = $event => ((newRule.value.priority) = $event)), modelModifiers: { number: true }, type: "number", label: "优先级", @@ -6010,9 +6115,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[31] || (_cache[31] = $event => (ruleDialog.value = false)) + onClick: _cache[33] || (_cache[33] = $event => (ruleDialog.value = false)) }, { - default: _withCtx(() => _cache[138] || (_cache[138] = [ + default: _withCtx(() => _cache[140] || (_cache[140] = [ _createTextVNode("取消") ])), _: 1 @@ -6021,7 +6126,7 @@ return (_ctx, _cache) => { color: "primary", type: "submit" }, { - default: _withCtx(() => _cache[139] || (_cache[139] = [ + default: _withCtx(() => _cache[141] || (_cache[141] = [ _createTextVNode("保存") ])), _: 1 @@ -6040,7 +6145,7 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: proxyGroupDialog.value, - "onUpdate:modelValue": _cache[50] || (_cache[50] = $event => ((proxyGroupDialog).value = $event)), + "onUpdate:modelValue": _cache[52] || (_cache[52] = $event => ((proxyGroupDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ @@ -6053,7 +6158,7 @@ return (_ctx, _cache) => { _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[140] || (_cache[140] = [ + default: _withCtx(() => _cache[142] || (_cache[142] = [ _createTextVNode(_toDisplayString('添加代理组')) ])), _: 1 @@ -6069,7 +6174,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.name, - "onUpdate:modelValue": _cache[33] || (_cache[33] = $event => ((newProxyGroup.value.name) = $event)), + "onUpdate:modelValue": _cache[35] || (_cache[35] = $event => ((newProxyGroup.value.name) = $event)), label: "name", required: "", class: "mb-4", @@ -6086,7 +6191,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: newProxyGroup.value.type, - "onUpdate:modelValue": _cache[34] || (_cache[34] = $event => ((newProxyGroup.value.type) = $event)), + "onUpdate:modelValue": _cache[36] || (_cache[36] = $event => ((newProxyGroup.value.type) = $event)), label: "type", items: proxyGroupTypes.value, required: "", @@ -6101,7 +6206,7 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_select, { modelValue: newProxyGroup.value.proxies, - "onUpdate:modelValue": _cache[35] || (_cache[35] = $event => ((newProxyGroup.value.proxies) = $event)), + "onUpdate:modelValue": _cache[37] || (_cache[37] = $event => ((newProxyGroup.value.proxies) = $event)), label: "proxies", items: actions.value, multiple: "", @@ -6111,7 +6216,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue", "items"]), _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.url, - "onUpdate:modelValue": _cache[36] || (_cache[36] = $event => ((newProxyGroup.value.url) = $event)), + "onUpdate:modelValue": _cache[38] || (_cache[38] = $event => ((newProxyGroup.value.url) = $event)), label: "url", class: "mb-4", hint: "健康检查测试地址", @@ -6122,7 +6227,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_text_field, { key: 0, modelValue: newProxyGroup.value.tolerance, - "onUpdate:modelValue": _cache[37] || (_cache[37] = $event => ((newProxyGroup.value.tolerance) = $event)), + "onUpdate:modelValue": _cache[39] || (_cache[39] = $event => ((newProxyGroup.value.tolerance) = $event)), modelModifiers: { number: true }, label: "tolerance (ms)", variant: "outlined", @@ -6137,7 +6242,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_select, { key: 1, modelValue: newProxyGroup.value.strategy, - "onUpdate:modelValue": _cache[38] || (_cache[38] = $event => ((newProxyGroup.value.strategy) = $event)), + "onUpdate:modelValue": _cache[40] || (_cache[40] = $event => ((newProxyGroup.value.strategy) = $event)), label: "strategy", items: strategyTypes.value, class: "mb-4", @@ -6146,28 +6251,28 @@ return (_ctx, _cache) => { : _createCommentVNode("", true), _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.filter, - "onUpdate:modelValue": _cache[39] || (_cache[39] = $event => ((newProxyGroup.value.filter) = $event)), + "onUpdate:modelValue": _cache[41] || (_cache[41] = $event => ((newProxyGroup.value.filter) = $event)), label: "filter", class: "mb-4", hint: "筛选满足关键词或正则表达式的节点" }, null, 8, ["modelValue"]), _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value['exclude-filter'], - "onUpdate:modelValue": _cache[40] || (_cache[40] = $event => ((newProxyGroup.value['exclude-filter']) = $event)), + "onUpdate:modelValue": _cache[42] || (_cache[42] = $event => ((newProxyGroup.value['exclude-filter']) = $event)), label: "exclude-filter", class: "mb-4", hint: "排除满足关键词或正则表达式的节点" }, null, 8, ["modelValue"]), _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value['exclude-type'], - "onUpdate:modelValue": _cache[41] || (_cache[41] = $event => ((newProxyGroup.value['exclude-type']) = $event)), + "onUpdate:modelValue": _cache[43] || (_cache[43] = $event => ((newProxyGroup.value['exclude-type']) = $event)), label: "exclude-type", class: "mb-4", hint: "不支持正则表达式,通过 | 分割" }, null, 8, ["modelValue"]), _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value['expected-status'], - "onUpdate:modelValue": _cache[42] || (_cache[42] = $event => ((newProxyGroup.value['expected-status']) = $event)), + "onUpdate:modelValue": _cache[44] || (_cache[44] = $event => ((newProxyGroup.value['expected-status']) = $event)), label: "expected-status", class: "mb-4", hint: "健康检查时期望的 HTTP 响应状态码" @@ -6181,19 +6286,19 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.interval, - "onUpdate:modelValue": _cache[43] || (_cache[43] = $event => ((newProxyGroup.value.interval) = $event)), + "onUpdate:modelValue": _cache[45] || (_cache[45] = $event => ((newProxyGroup.value.interval) = $event)), modelModifiers: { number: true }, - label: "interval (s)", + label: "interval", variant: "outlined", type: "number", min: "0", + suffix: "秒", hint: "健康检查间隔,如不为 0 则启用定时测试", - "persistent-hint": "", rules: [v => v > -1 || '检查间隔需不小于0'] }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "warning" }, { - default: _withCtx(() => _cache[141] || (_cache[141] = [ + default: _withCtx(() => _cache[143] || (_cache[143] = [ _createTextVNode("mdi-timer") ])), _: 1 @@ -6211,19 +6316,19 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.timeout, - "onUpdate:modelValue": _cache[44] || (_cache[44] = $event => ((newProxyGroup.value.timeout) = $event)), + "onUpdate:modelValue": _cache[46] || (_cache[46] = $event => ((newProxyGroup.value.timeout) = $event)), modelModifiers: { number: true }, - label: "timeout (ms)", + label: "timeout", variant: "outlined", type: "number", min: "1", hint: "请求的超时时间", - "persistent-hint": "", + suffix: "毫秒", rules: [v => v > 0 || '超时时间必须大于0'] }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "warning" }, { - default: _withCtx(() => _cache[142] || (_cache[142] = [ + default: _withCtx(() => _cache[144] || (_cache[144] = [ _createTextVNode("mdi-timer") ])), _: 1 @@ -6246,7 +6351,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['lazy'], - "onUpdate:modelValue": _cache[45] || (_cache[45] = $event => ((newProxyGroup.value['lazy']) = $event)), + "onUpdate:modelValue": _cache[47] || (_cache[47] = $event => ((newProxyGroup.value['lazy']) = $event)), label: "lazy", inset: "", hint: "未选择到当前策略组时,不进行测试", @@ -6262,7 +6367,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['disable-udp'], - "onUpdate:modelValue": _cache[46] || (_cache[46] = $event => ((newProxyGroup.value['disable-udp']) = $event)), + "onUpdate:modelValue": _cache[48] || (_cache[48] = $event => ((newProxyGroup.value['disable-udp']) = $event)), label: "disable-udp", inset: "", hint: "禁用该策略组的UDP", @@ -6283,7 +6388,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['include-all'], - "onUpdate:modelValue": _cache[47] || (_cache[47] = $event => ((newProxyGroup.value['include-all']) = $event)), + "onUpdate:modelValue": _cache[49] || (_cache[49] = $event => ((newProxyGroup.value['include-all']) = $event)), label: "include-all", inset: "", hint: "引入所有出站代理以及代理集合", @@ -6299,7 +6404,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['include-all-proxies'], - "onUpdate:modelValue": _cache[48] || (_cache[48] = $event => ((newProxyGroup.value['include-all-proxies']) = $event)), + "onUpdate:modelValue": _cache[50] || (_cache[50] = $event => ((newProxyGroup.value['include-all-proxies']) = $event)), label: "include-all-proxies", inset: "", hint: "引入所有出站代理", @@ -6320,7 +6425,7 @@ return (_ctx, _cache) => { class: "mb-6", variant: "tonal" }, { - default: _withCtx(() => _cache[143] || (_cache[143] = [ + default: _withCtx(() => _cache[145] || (_cache[145] = [ _createTextVNode(" 参考"), _createElementVNode("a", { href: "https://wiki.metacubex.one/config/proxy-groups/", @@ -6334,9 +6439,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[49] || (_cache[49] = $event => (proxyGroupDialog.value = false)) + onClick: _cache[51] || (_cache[51] = $event => (proxyGroupDialog.value = false)) }, { - default: _withCtx(() => _cache[144] || (_cache[144] = [ + default: _withCtx(() => _cache[146] || (_cache[146] = [ _createTextVNode("取消") ])), _: 1 @@ -6345,7 +6450,7 @@ return (_ctx, _cache) => { color: "primary", type: "submit" }, { - default: _withCtx(() => _cache[145] || (_cache[145] = [ + default: _withCtx(() => _cache[147] || (_cache[147] = [ _createTextVNode("保存") ])), _: 1 @@ -6364,19 +6469,19 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: yamlDialog.value, - "onUpdate:modelValue": _cache[52] || (_cache[52] = $event => ((yamlDialog).value = $event)), - "max-width": "800" + "onUpdate:modelValue": _cache[54] || (_cache[54] = $event => ((yamlDialog).value = $event)), + "max-width": "600" }, { default: _withCtx(() => [ _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, { class: "headline" }, { - default: _withCtx(() => _cache[146] || (_cache[146] = [ + default: _withCtx(() => _cache[148] || (_cache[148] = [ _createTextVNode("YAML配置") ])), _: 1 }), - _createVNode(_component_v_card_text, null, { + _createVNode(_component_v_card_text, { style: {"max-height":"600px","overflow-y":"auto"} }, { default: _withCtx(() => [ _createElementVNode("pre", null, _toDisplayString(displayedYaml.value), 1) ]), @@ -6389,16 +6494,16 @@ return (_ctx, _cache) => { color: "primary", onClick: _ctx.copyYaml }, { - default: _withCtx(() => _cache[147] || (_cache[147] = [ + default: _withCtx(() => _cache[149] || (_cache[149] = [ _createTextVNode("复制") ])), _: 1 }, 8, ["onClick"]), _createVNode(_component_v_btn, { color: "primary", - onClick: _cache[51] || (_cache[51] = $event => (yamlDialog.value = false)) + onClick: _cache[53] || (_cache[53] = $event => (yamlDialog.value = false)) }, { - default: _withCtx(() => _cache[148] || (_cache[148] = [ + default: _withCtx(() => _cache[150] || (_cache[150] = [ _createTextVNode("关闭") ])), _: 1 @@ -6414,23 +6519,23 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: importRuleDialog.value, - "onUpdate:modelValue": _cache[56] || (_cache[56] = $event => ((importRuleDialog).value = $event)), + "onUpdate:modelValue": _cache[58] || (_cache[58] = $event => ((importRuleDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[149] || (_cache[149] = [ + default: _withCtx(() => _cache[151] || (_cache[151] = [ _createTextVNode("导入规则") ])), _: 1 }), - _createVNode(_component_v_card_text, null, { + _createVNode(_component_v_card_text, { style: {"max-height":"600px","overflow-y":"auto"} }, { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: importRules.value.type, - "onUpdate:modelValue": _cache[53] || (_cache[53] = $event => ((importRules.value.type) = $event)), + "onUpdate:modelValue": _cache[55] || (_cache[55] = $event => ((importRules.value.type) = $event)), items: importRuleTypes, label: "内容格式", required: "", @@ -6438,13 +6543,14 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_textarea, { modelValue: importRules.value.payload, - "onUpdate:modelValue": _cache[54] || (_cache[54] = $event => ((importRules.value.payload) = $event)), + "onUpdate:modelValue": _cache[56] || (_cache[56] = $event => ((importRules.value.payload) = $event)), label: "内容", required: "", placeholder: "rules: []", class: "mb-4", rows: "4", - "auto-grow": "" + "auto-grow": "", + style: {"max-height":"600px","overflow-y":"auto"} }, null, 8, ["modelValue"]), _createVNode(_component_v_alert, { type: "info", @@ -6453,7 +6559,7 @@ return (_ctx, _cache) => { class: "mb-4", variant: "tonal" }, { - default: _withCtx(() => _cache[150] || (_cache[150] = [ + default: _withCtx(() => _cache[152] || (_cache[152] = [ _createTextVNode(" 请输入 Clash 规则中的 "), _createElementVNode("strong", null, "rules", -1), _createTextVNode(" 字段,例如:"), @@ -6474,9 +6580,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[55] || (_cache[55] = $event => (importRuleDialog.value = false)) + onClick: _cache[57] || (_cache[57] = $event => (importRuleDialog.value = false)) }, { - default: _withCtx(() => _cache[151] || (_cache[151] = [ + default: _withCtx(() => _cache[153] || (_cache[153] = [ _createTextVNode("取消") ])), _: 1 @@ -6485,7 +6591,7 @@ return (_ctx, _cache) => { color: "primary", onClick: importRule }, { - default: _withCtx(() => _cache[152] || (_cache[152] = [ + default: _withCtx(() => _cache[154] || (_cache[154] = [ _createTextVNode("导入") ])), _: 1 @@ -6501,23 +6607,23 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: importExtraProxiesDialog.value, - "onUpdate:modelValue": _cache[60] || (_cache[60] = $event => ((importExtraProxiesDialog).value = $event)), + "onUpdate:modelValue": _cache[62] || (_cache[62] = $event => ((importExtraProxiesDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[153] || (_cache[153] = [ + default: _withCtx(() => _cache[155] || (_cache[155] = [ _createTextVNode("导入节点") ])), _: 1 }), - _createVNode(_component_v_card_text, null, { + _createVNode(_component_v_card_text, { style: {"max-height":"600px","overflow-y":"auto"} }, { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: importExtraProxies.value.type, - "onUpdate:modelValue": _cache[57] || (_cache[57] = $event => ((importExtraProxies.value.type) = $event)), + "onUpdate:modelValue": _cache[59] || (_cache[59] = $event => ((importExtraProxies.value.type) = $event)), items: importProxiesTypes, label: "内容格式", required: "", @@ -6525,36 +6631,55 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_textarea, { modelValue: importExtraProxies.value.payload, - "onUpdate:modelValue": _cache[58] || (_cache[58] = $event => ((importExtraProxies.value.payload) = $event)), + "onUpdate:modelValue": _cache[60] || (_cache[60] = $event => ((importExtraProxies.value.payload) = $event)), label: "内容", required: "", - placeholder: "proxies: []", + placeholder: importExtraProxiesPlaceholderText.value, class: "mb-4", rows: "4", "auto-grow": "" - }, null, 8, ["modelValue"]), - _createVNode(_component_v_alert, { - type: "info", - dense: "", - text: "", - class: "mb-4", - variant: "tonal" - }, { - default: _withCtx(() => _cache[154] || (_cache[154] = [ - _createTextVNode(" 请输入 Clash 规则中的 "), - _createElementVNode("strong", null, "proxies", -1), - _createTextVNode(" 字段,例如:"), - _createElementVNode("br", null, null, -1), - _createElementVNode("code", null, [ - _createTextVNode("proxies:"), - _createElementVNode("br"), - _createTextVNode("- name: \"ss\""), - _createElementVNode("br"), - _createTextVNode("- type: \"ss\"") - ], -1) - ])), - _: 1 - }) + }, null, 8, ["modelValue", "placeholder"]), + (importExtraProxies.value.type === 'YAML') + ? (_openBlock(), _createBlock(_component_v_alert, { + key: 0, + type: "info", + dense: "", + text: "", + class: "mb-4", + variant: "tonal" + }, { + default: _withCtx(() => _cache[156] || (_cache[156] = [ + _createTextVNode(" 请输入 Clash 规则中的 "), + _createElementVNode("strong", null, "proxies", -1), + _createTextVNode(" 字段,例如:"), + _createElementVNode("br", null, null, -1), + _createElementVNode("pre", { style: {"white-space":"pre-wrap","font-family":"monospace","margin":"0"} }, [ + _createTextVNode(""), + _createElementVNode("code", null, "proxies:\n - name: \"ss node\"\n type: \"ss\"") + ], -1) + ])), + _: 1 + })) + : _createCommentVNode("", true), + (importExtraProxies.value.type === 'LINK') + ? (_openBlock(), _createBlock(_component_v_alert, { + key: 1, + type: "info", + dense: "", + text: "", + class: "mb-4", + variant: "tonal" + }, { + default: _withCtx(() => _cache[157] || (_cache[157] = [ + _createTextVNode(" 请输入 V2Ray 格式的节点链接,例如:"), + _createElementVNode("br", null, null, -1), + _createElementVNode("code", null, "vmess://xxxx", -1), + _createElementVNode("br", null, null, -1), + _createElementVNode("code", null, "ss://xxxx", -1) + ])), + _: 1 + })) + : _createCommentVNode("", true) ]), _: 1 }), @@ -6563,9 +6688,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[59] || (_cache[59] = $event => (importExtraProxiesDialog.value = false)) + onClick: _cache[61] || (_cache[61] = $event => (importExtraProxiesDialog.value = false)) }, { - default: _withCtx(() => _cache[155] || (_cache[155] = [ + default: _withCtx(() => _cache[158] || (_cache[158] = [ _createTextVNode("取消") ])), _: 1 @@ -6574,7 +6699,7 @@ return (_ctx, _cache) => { color: "primary", onClick: importExtraProxiesFun }, { - default: _withCtx(() => _cache[156] || (_cache[156] = [ + default: _withCtx(() => _cache[159] || (_cache[159] = [ _createTextVNode("导入") ])), _: 1 @@ -6590,7 +6715,7 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: ruleProviderDialog.value, - "onUpdate:modelValue": _cache[71] || (_cache[71] = $event => ((ruleProviderDialog).value = $event)), + "onUpdate:modelValue": _cache[73] || (_cache[73] = $event => ((ruleProviderDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ @@ -6612,7 +6737,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newRuleProvider.value.name, - "onUpdate:modelValue": _cache[61] || (_cache[61] = $event => ((newRuleProvider.value.name) = $event)), + "onUpdate:modelValue": _cache[63] || (_cache[63] = $event => ((newRuleProvider.value.name) = $event)), label: "name", required: "", rules: [v => !!v || '名称不能为空'], @@ -6620,7 +6745,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue", "rules"]), _createVNode(_component_v_select, { modelValue: newRuleProvider.value.type, - "onUpdate:modelValue": _cache[62] || (_cache[62] = $event => ((newRuleProvider.value.type) = $event)), + "onUpdate:modelValue": _cache[64] || (_cache[64] = $event => ((newRuleProvider.value.type) = $event)), items: ruleProviderTypes, label: "type", required: "", @@ -6631,7 +6756,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_text_field, { key: 0, modelValue: newRuleProvider.value.url, - "onUpdate:modelValue": _cache[63] || (_cache[63] = $event => ((newRuleProvider.value.url) = $event)), + "onUpdate:modelValue": _cache[65] || (_cache[65] = $event => ((newRuleProvider.value.url) = $event)), label: "url", required: "", rules: [(v) => !!v || 'URL 不能为空', (v) => isValidUrl(v) || '请输入有效的 URL',], @@ -6643,7 +6768,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_text_field, { key: 1, modelValue: newRuleProvider.value.path, - "onUpdate:modelValue": _cache[64] || (_cache[64] = $event => ((newRuleProvider.value.path) = $event)), + "onUpdate:modelValue": _cache[66] || (_cache[66] = $event => ((newRuleProvider.value.path) = $event)), label: "path", required: "", rules: [v => !!v || '当类型为文件时,路径不能为空'], @@ -6653,7 +6778,7 @@ return (_ctx, _cache) => { : _createCommentVNode("", true), _createVNode(_component_v_text_field, { modelValue: newRuleProvider.value.interval, - "onUpdate:modelValue": _cache[65] || (_cache[65] = $event => ((newRuleProvider.value.interval) = $event)), + "onUpdate:modelValue": _cache[67] || (_cache[67] = $event => ((newRuleProvider.value.interval) = $event)), modelModifiers: { number: true }, label: "interval", class: "mb-4", @@ -6665,7 +6790,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue", "rules"]), _createVNode(_component_v_select, { modelValue: newRuleProvider.value.behavior, - "onUpdate:modelValue": _cache[66] || (_cache[66] = $event => ((newRuleProvider.value.behavior) = $event)), + "onUpdate:modelValue": _cache[68] || (_cache[68] = $event => ((newRuleProvider.value.behavior) = $event)), items: ruleProviderBehaviorTypes, label: "behavior", class: "mb-4", @@ -6673,7 +6798,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_select, { modelValue: newRuleProvider.value.format, - "onUpdate:modelValue": _cache[67] || (_cache[67] = $event => ((newRuleProvider.value.format) = $event)), + "onUpdate:modelValue": _cache[69] || (_cache[69] = $event => ((newRuleProvider.value.format) = $event)), items: ruleProviderFormatTypes, label: "format", class: "mb-4", @@ -6681,7 +6806,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_text_field, { modelValue: newRuleProvider.value['size-limit'], - "onUpdate:modelValue": _cache[68] || (_cache[68] = $event => ((newRuleProvider.value['size-limit']) = $event)), + "onUpdate:modelValue": _cache[70] || (_cache[70] = $event => ((newRuleProvider.value['size-limit']) = $event)), modelModifiers: { number: true }, label: "size-limit", class: "mb-4", @@ -6695,7 +6820,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_combobox, { key: 2, modelValue: newRuleProvider.value.payload, - "onUpdate:modelValue": _cache[69] || (_cache[69] = $event => ((newRuleProvider.value.payload) = $event)), + "onUpdate:modelValue": _cache[71] || (_cache[71] = $event => ((newRuleProvider.value.payload) = $event)), multiple: "", chips: "", "closable-chips": "", @@ -6729,9 +6854,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[70] || (_cache[70] = $event => (ruleProviderDialog.value = false)) + onClick: _cache[72] || (_cache[72] = $event => (ruleProviderDialog.value = false)) }, { - default: _withCtx(() => _cache[157] || (_cache[157] = [ + default: _withCtx(() => _cache[160] || (_cache[160] = [ _createTextVNode("取消") ])), _: 1 @@ -6740,7 +6865,7 @@ return (_ctx, _cache) => { color: "primary", type: "submit" }, { - default: _withCtx(() => _cache[158] || (_cache[158] = [ + default: _withCtx(() => _cache[161] || (_cache[161] = [ _createTextVNode("保存") ])), _: 1 @@ -6762,6 +6887,6 @@ return (_ctx, _cache) => { } }; -const PageComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-eae2a1d4"]]); +const PageComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-fd7b621d"]]); export { PageComponent as default }; diff --git a/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js b/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js index f506565..6560f68 100644 --- a/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js +++ b/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js @@ -2,14 +2,14 @@ const currentImports = {}; const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']); let moduleMap = { "./Page":()=>{ - dynamicLoadingCss(["__federation_expose_Page-DQjiFgWw.css"], false, './Page'); - return __federation_import('./__federation_expose_Page--ZdI8TQS.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, + dynamicLoadingCss(["__federation_expose_Page-DIAzDhN_.css"], false, './Page'); + return __federation_import('./__federation_expose_Page-gjHBCfnd.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, "./Config":()=>{ - dynamicLoadingCss(["__federation_expose_Config-xrxN2F1l.css"], false, './Config'); - return __federation_import('./__federation_expose_Config-sTz3W3yr.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, + dynamicLoadingCss(["__federation_expose_Config-BDDunIfC.css"], false, './Config'); + return __federation_import('./__federation_expose_Config-r5zwvDwv.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, "./Dashboard":()=>{ - dynamicLoadingCss(["__federation_expose_Dashboard-IipjE6HA.css"], false, './Dashboard'); - return __federation_import('./__federation_expose_Dashboard-DEFRy9WP.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},}; + dynamicLoadingCss(["__federation_expose_Dashboard-D6WU_Ejn.css"], false, './Dashboard'); + return __federation_import('./__federation_expose_Dashboard-BFVr4jq_.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},}; const seen = {}; const dynamicLoadingCss = (cssFilePaths, dontAppendStylesToHead, exposeItemName) => { const metaUrl = import.meta.url; From e0aa2e91fc9545dc9e0b2ff9ea4a93b63e7ce9a7 Mon Sep 17 00:00:00 2001 From: wumode Date: Sat, 28 Jun 2025 20:05:01 +0800 Subject: [PATCH 2/2] update(ClashRuleProvider): fix bugs --- plugins.v2/clashruleprovider/__init__.py | 39 ++++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/plugins.v2/clashruleprovider/__init__.py b/plugins.v2/clashruleprovider/__init__.py index b3c5e7b..319f334 100644 --- a/plugins.v2/clashruleprovider/__init__.py +++ b/plugins.v2/clashruleprovider/__init__.py @@ -1041,8 +1041,6 @@ class ClashRuleProvider(_PluginBase): logger.info(f"已更新: {url}. 节点数量: {len(rs['proxies'])}") if rs.get('rules') is None: rs['rules'] = [] - if self._discard_rules: - rs['rules'] = [] rs = self.__remove_nodes_by_keywords(rs) except Exception as e: logger.error(f"解析配置出错: {e}") @@ -1151,6 +1149,19 @@ class ClashRuleProvider(_PluginBase): all_proxies.extend(config.get("proxies", [])) return all_proxies + @staticmethod + def extend_with_name_checking(to_list: List[Dict[str, Any]], from_list: List[Dict[str, Any]] + ) -> List[Dict[str, Any]]: + """ + 去除同名元素合并列表 + """ + for item in from_list: + if any(p.get('name') == item.get('name', '') for p in to_list): + logger.warn(f"Item named {item.get('name')} already exists. Skipping...") + continue + to_list.append(item) + return to_list + def clash_config(self) -> Optional[Dict[str, Any]]: """ 整理 clash 配置,返回配置字典 @@ -1160,11 +1171,18 @@ class ClashRuleProvider(_PluginBase): proxies =[] if not self._clash_template: clash_config = copy.deepcopy(first_config) + clash_config['proxy-groups'] = [] + clash_config['rule-providers'] = {} + clash_config['rules'] = [] else: clash_config = copy.deepcopy(self._clash_template) proxies.extend(self._clash_template.get('proxies')) - clash_config['proxy-groups'] = (first_config.get('proxy-groups', []) - + clash_config.get('proxy-groups', [])) + clash_config['proxy-groups'] = ClashRuleProvider.extend_with_name_checking(clash_config.get('proxy-groups', []), + first_config.get('proxy-groups', []), + ) + clash_config['rules'] = clash_config.get('rules', []) + if not self._discard_rules: + clash_config['rules'] += first_config.get('rules', []) clash_config['rule-providers'] = first_config.get('rule-providers', {}).update(clash_config.get('rule-providers', {})) @@ -1181,19 +1199,14 @@ class ClashRuleProvider(_PluginBase): # 添加代理组 proxy_groups = copy.deepcopy(self._proxy_groups) if proxy_groups: - if clash_config.get("proxy-groups"): - clash_config['proxy-groups'].extend(proxy_groups) - else: - clash_config['proxy-groups'] = proxy_groups - + clash_config['proxy-groups'] = ClashRuleProvider.extend_with_name_checking(clash_config['proxy-groups'], + proxy_groups) # 添加按大洲代理组 if self._group_by_region: if self._proxy_groups_by_region: - if clash_config.get('proxy-groups'): - clash_config['proxy-groups'].extend(self._proxy_groups_by_region) - else: - clash_config['proxy-groups'] = copy.deepcopy(self._proxy_groups_by_region) + clash_config['proxy-groups'] = ClashRuleProvider.extend_with_name_checking(clash_config['proxy-groups'], + self._proxy_groups_by_region) top_rules = [] outbound_names = list(x.get("name") for x in self.clash_outbound())