mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-13 15:09:12 +00:00
update(ClashRuleProvider): 支持规则集合; 添加ACL4SSR规则集; 配置说明
This commit is contained in:
@@ -449,11 +449,12 @@
|
||||
"name": "Clash Rule Provider",
|
||||
"description": "随时为Clash添加一些额外的规则。",
|
||||
"labels": "工具",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"icon": "Mihomo_Meta_A.png",
|
||||
"author": "wumode",
|
||||
"level": 1,
|
||||
"history": {
|
||||
"v1.1.0": "支持规则集合; 添加ACL4SSR规则集; 配置说明",
|
||||
"v1.0.1": "支持规则搜索, 优化细节",
|
||||
"v1.0.0": "支持: 规则分页; 导入规则; 代理组; 附加出站代理; 按区域分组",
|
||||
"v0.1.0": "新增ClashRuleProvider"
|
||||
|
||||
38
plugins.v2/clashruleprovider/README.md
Normal file
38
plugins.v2/clashruleprovider/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Clash Rule Provider
|
||||
|
||||
**Clash Rule Provider** 生成适用于 [Meta Kernel](https://github.com/MetaCubeX/mihomo/tree/Meta) 定制配置,便于增加、修改和删除规则。
|
||||
|
||||
- 即时通知 Clash 刷新规则集合
|
||||
- 基于 Meta 内核丰富的代理组配置,提供灵活的路由功能
|
||||
- 支持按大洲分组节点
|
||||
- 支持 [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR) 规则集合
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 规则集规则
|
||||
|
||||
用于添加能够在 Clash 中即时生效的规则,Clash Rule Provider 会根据每条规则的**出站**生成相应的**规则集合** `📂<-` + `出站`。
|
||||
|
||||
### 置顶规则
|
||||
|
||||
这是Clash配置文件的`rules`字段中最顶部的规则,相比于其它规则它们拥有更高的优先级。Clash Rule Provider 会自动在此处添加**规则集规则**内的**规则集合**。
|
||||
|
||||
### 代理组
|
||||
|
||||
代理组中配置项的说明请参考 [Mihomo docs](https://wiki.metacubex.one/config/proxy-groups/),
|
||||
这里以两个例子说明如何定制代理组:
|
||||
|
||||
- 访问北邮人的代理组
|
||||
|
||||
北邮人拒绝国内以及所有IPv4连接,可以添加一个 `type` 为 `url-test` 的代理组,在 `url` 中填写北邮人的地址,打开 `include-all-proxies`,其余配置项保持默认。
|
||||
|
||||
然后,在**置顶规则**中添加一条规则: `DOMAIN,xxx.pt,PtProxy` 。
|
||||
|
||||

|
||||
|
||||
|
||||
- 访问ChatGPT的代理组
|
||||
|
||||
在**高级选项**中启用按大洲分组节点。选择Asia以外的代理组,设置`url`: `https://chatgpt.com/` , `expected-status`: `200` 。
|
||||
|
||||

|
||||
@@ -17,11 +17,11 @@ from app import schemas
|
||||
from app.core.config import settings
|
||||
from app.log import logger
|
||||
from app.plugins import _PluginBase
|
||||
from app.schemas.types import EventType, NotificationType
|
||||
from app.schemas.types import NotificationType
|
||||
from app.utils.http import RequestUtils
|
||||
from app.plugins.clashruleprovider.clash_rule_parser import ClashRuleParser
|
||||
from app.plugins.clashruleprovider.clash_rule_parser import Action, RuleType, ClashRule, MatchRule, LogicRule
|
||||
from app.plugins.clashruleprovider.clash_rule_parser import ProxyGroupValidator
|
||||
from app.plugins.clashruleprovider.clash_rule_parser import ProxyGroup, RuleProvider
|
||||
|
||||
|
||||
class ClashRuleProvider(_PluginBase):
|
||||
@@ -32,7 +32,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "Mihomo_Meta_A.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.0.1"
|
||||
plugin_version = "1.1.0"
|
||||
# 插件作者
|
||||
plugin_author = "wumode"
|
||||
# 作者主页
|
||||
@@ -62,19 +62,24 @@ class ClashRuleProvider(_PluginBase):
|
||||
_retry_times = 3
|
||||
_filter_keywords = []
|
||||
_auto_update_subscriptions = True
|
||||
_ruleset_prefix: str = '📂<-'
|
||||
_ruleset_prefix: str = '📂<='
|
||||
_group_by_region: bool = False
|
||||
_refresh_delay: int = 5
|
||||
_discard_rules: bool = False
|
||||
_enable_acl4ssr: bool = False
|
||||
|
||||
# 插件数据
|
||||
_clash_config: Optional[Dict[str, Any]] = None
|
||||
_top_rules: List[str] = []
|
||||
_ruleset_rules: List[str] = []
|
||||
_rule_provider: Dict[str, Any] = {}
|
||||
_extra_rule_providers: Dict[str, Any] = {}
|
||||
_subscription_info = {}
|
||||
_ruleset_names: Dict[str, str] = {}
|
||||
_proxy_groups = []
|
||||
_extra_proxies = []
|
||||
_acl4ssr_providers: Dict[str, Any] = {}
|
||||
_acl4ssr_prefix: str = '🗂️=>'
|
||||
|
||||
# protected variables
|
||||
_clash_rule_parser = None
|
||||
@@ -93,7 +98,9 @@ class ClashRuleProvider(_PluginBase):
|
||||
self._subscription_info = self.get_data("subscription_info") or \
|
||||
{"download": 0, "upload": 0, "total": 0, "expire": 0, "last_update": 0}
|
||||
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 {}
|
||||
if config:
|
||||
self._enabled = config.get("enabled")
|
||||
self._proxy = config.get("proxy")
|
||||
@@ -106,12 +113,15 @@ class ClashRuleProvider(_PluginBase):
|
||||
self._movie_pilot_url = self._movie_pilot_url[:-1]
|
||||
self._cron = config.get("cron_string")
|
||||
self._timeout = config.get("timeout")
|
||||
self._retry_times = config.get("retry_times")
|
||||
self._retry_times = config.get("retry_times") or 3
|
||||
self._filter_keywords = config.get("filter_keywords")
|
||||
self._ruleset_prefix = config.get("ruleset_prefix", "Custom_")
|
||||
self._ruleset_prefix = config.get("ruleset_prefix", "📂<=")
|
||||
self._acl4ssr_prefix = config.get("acl4ssr_prefix", "🗂️=>")
|
||||
self._auto_update_subscriptions = config.get("auto_update_subscriptions")
|
||||
self._group_by_region = config.get("group_by_region")
|
||||
self._refresh_delay = config.get("refresh_delay") or 5
|
||||
self._discard_rules = config.get("discard_rules") or False
|
||||
self._enable_acl4ssr = config.get("enable_acl4ssr") or False
|
||||
self._clash_rule_parser = ClashRuleParser()
|
||||
self._ruleset_rule_parser = ClashRuleParser()
|
||||
if self._enabled:
|
||||
@@ -125,7 +135,13 @@ class ClashRuleProvider(_PluginBase):
|
||||
self._scheduler.start()
|
||||
# 更新订阅
|
||||
self._scheduler.add_job(self.__refresh_subscription, "date",
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=5))
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=2))
|
||||
# 更新acl4ssr
|
||||
if self._enable_acl4ssr:
|
||||
self._scheduler.add_job(self.__refresh_acl4ssr, "date",
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=5))
|
||||
else:
|
||||
self._acl4ssr_providers = {}
|
||||
|
||||
def get_state(self) -> bool:
|
||||
return self._enabled
|
||||
@@ -229,8 +245,32 @@ class ClashRuleProvider(_PluginBase):
|
||||
"endpoint": self.get_rule_providers,
|
||||
"methods": ["GET"],
|
||||
"auth": "bear",
|
||||
"summary": "update rule providers",
|
||||
"description": "update rule providers"
|
||||
"summary": "rule providers",
|
||||
"description": "rule providers"
|
||||
},
|
||||
{
|
||||
"path": "/extra-rule-providers",
|
||||
"endpoint": self.get_extra_rule_providers,
|
||||
"methods": ["GET"],
|
||||
"auth": "bear",
|
||||
"summary": "extra rule providers",
|
||||
"description": "extra rule providers"
|
||||
},
|
||||
{
|
||||
"path": "/extra-rule-provider",
|
||||
"endpoint": self.update_extra_rule_provider,
|
||||
"methods": ["POST"],
|
||||
"auth": "bear",
|
||||
"summary": "update an extra rule provider",
|
||||
"description": "update an rule provider"
|
||||
},
|
||||
{
|
||||
"path": "/extra-rule-provider",
|
||||
"endpoint": self.delete_extra_rule_provider,
|
||||
"methods": ["DELETE"],
|
||||
"auth": "bear",
|
||||
"summary": "add an extra rule provider",
|
||||
"description": "add an rule provider"
|
||||
},
|
||||
{
|
||||
"path": "/extra-proxies",
|
||||
@@ -245,8 +285,8 @@ class ClashRuleProvider(_PluginBase):
|
||||
"endpoint": self.delete_extra_proxy,
|
||||
"methods": ["DELETE"],
|
||||
"auth": "bear",
|
||||
"summary": "delete a extra proxy",
|
||||
"description": "delete a extra proxy"
|
||||
"summary": "delete an extra proxy",
|
||||
"description": "delete an extra proxy"
|
||||
},
|
||||
{
|
||||
"path": "/extra-proxies",
|
||||
@@ -333,31 +373,15 @@ class ClashRuleProvider(_PluginBase):
|
||||
"id": "ClashRuleProvider",
|
||||
"name": "定时更新订阅",
|
||||
"trigger": CronTrigger.from_crontab(self._cron),
|
||||
"func": self.update_subscription_service,
|
||||
"func": self.refresh_subscription_service,
|
||||
"kwargs": {}
|
||||
}]
|
||||
return []
|
||||
|
||||
def __update_config(self):
|
||||
# 保存配置
|
||||
self.update_config(
|
||||
{
|
||||
"enabled": self._enabled,
|
||||
"cron": self._cron,
|
||||
"proxy": self._proxy,
|
||||
"notify": self._notify,
|
||||
"sub_links": self._sub_links,
|
||||
"clash_dashboard_url": self._clash_dashboard_url,
|
||||
"clash_dashboard_secret": self._clash_dashboard_secret,
|
||||
"movie_pilot_url": self._movie_pilot_url,
|
||||
"retry_times": self._retry_times,
|
||||
"timeout": self._timeout,
|
||||
})
|
||||
|
||||
def __save_data(self):
|
||||
self.__insert_ruleset()
|
||||
self._top_rules = self._clash_rule_parser.to_string()
|
||||
self._ruleset_rules = self._ruleset_rule_parser.to_string()
|
||||
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)
|
||||
@@ -366,6 +390,8 @@ class ClashRuleProvider(_PluginBase):
|
||||
self.save_data('rule_provider', self._rule_provider)
|
||||
self.save_data('proxy_groups', self._proxy_groups)
|
||||
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)
|
||||
|
||||
def __parse_config(self):
|
||||
if self._top_rules is None:
|
||||
@@ -457,9 +483,9 @@ class ClashRuleProvider(_PluginBase):
|
||||
self.append_top_rules(rules)
|
||||
return schemas.Response(success=True)
|
||||
|
||||
def reorder_rules(self, params: Dict[str, Any]):
|
||||
def reorder_rules(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return {"success": False, "message": ""}
|
||||
return schemas.Response(success=False, message='')
|
||||
moved_priority = params.get('moved_priority')
|
||||
target_priority = params.get('target_priority')
|
||||
try:
|
||||
@@ -469,39 +495,41 @@ class ClashRuleProvider(_PluginBase):
|
||||
else:
|
||||
self.__reorder_rules(self._clash_rule_parser, moved_priority, target_priority)
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
return {"success": True, "message": None}
|
||||
return schemas.Response(success=False, message=str(e))
|
||||
return schemas.Response(success=True)
|
||||
|
||||
def update_rules(self, params: Dict[str, Any]):
|
||||
def update_rules(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return {"success": False, "message": ""}
|
||||
return schemas.Response(success=False, message='')
|
||||
if params.get('type') == 'ruleset':
|
||||
self.__update_rules(params.get('rules'), self._ruleset_rule_parser)
|
||||
else:
|
||||
self.__update_rules(params.get('rules'), self._clash_rule_parser)
|
||||
return {"success": True, "message": None}
|
||||
return schemas.Response(success=True)
|
||||
|
||||
def update_rule(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
if not self._enabled:
|
||||
return {"success": False, "message": ""}
|
||||
if params.get('type') == 'ruleset':
|
||||
res = self.update_rule_by_priority(params.get('rule_data'), self._ruleset_rule_parser)
|
||||
res = self.update_rule_by_priority(params.get('rule_data'),
|
||||
params.get('priority'),
|
||||
self._ruleset_rule_parser)
|
||||
if res:
|
||||
self.__add_notification_job(f"{self._ruleset_prefix}{params.get('rule_data').get('action')}")
|
||||
else:
|
||||
res = self.update_rule_by_priority(params.get('rule_data'), self._clash_rule_parser)
|
||||
res = self.update_rule_by_priority(params.get('rule_data'), params.get('priority'), self._clash_rule_parser)
|
||||
return {"success": bool(res), "message": None}
|
||||
|
||||
def add_rule(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def add_rule(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return {"success": False, "message": ""}
|
||||
return schemas.Response(success=False, message='')
|
||||
if params.get('type') == 'ruleset':
|
||||
res = self.add_rule_by_priority(params.get('rule_data'), self._ruleset_rule_parser)
|
||||
if res:
|
||||
self.__add_notification_job(f"{self._ruleset_prefix}{params.get('rule_data').get('action')}")
|
||||
else:
|
||||
res = self.add_rule_by_priority(params.get('rule_data'), self._clash_rule_parser)
|
||||
return {"success": bool(res), "message": None}
|
||||
return schemas.Response(success=bool(res), message='')
|
||||
|
||||
def get_subscription(self) -> schemas.Response:
|
||||
if not self._sub_links:
|
||||
@@ -517,11 +545,61 @@ class ClashRuleProvider(_PluginBase):
|
||||
res = self.__refresh_subscription()
|
||||
if not res:
|
||||
return schemas.Response(success=False, message=f"订阅链接 {self._sub_links[0]} 更新失败")
|
||||
return schemas.Response(success=True, message='订阅更新成功败')
|
||||
return schemas.Response(success=True, message='订阅更新成功')
|
||||
|
||||
def get_rule_providers(self) -> schemas.Response:
|
||||
return schemas.Response(success=True, data=self.rule_providers())
|
||||
|
||||
def get_extra_rule_providers(self) -> schemas.Response:
|
||||
extra_rule_providers = []
|
||||
for name, value in self._extra_rule_providers.items():
|
||||
item = {'name': name}
|
||||
item.update(value)
|
||||
extra_rule_providers.append(item)
|
||||
return schemas.Response(success=True, data={'rule_providers': extra_rule_providers})
|
||||
|
||||
def update_extra_rule_provider(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return schemas.Response(success=False, message='')
|
||||
name = params.get('name')
|
||||
new_value = params.get('value')
|
||||
new_name = new_value.get('name')
|
||||
if not name or not new_name:
|
||||
return schemas.Response(success=False, message="Missing param: name")
|
||||
item = {}
|
||||
for key, value in new_value.items():
|
||||
if key == 'name' or value is None:
|
||||
continue
|
||||
if value == '' or value is None:
|
||||
continue
|
||||
item[key] = value
|
||||
try:
|
||||
rule_provider = RuleProvider.parse_obj(item)
|
||||
if rule_provider.type == 'inline' and rule_provider.behavior == 'classical':
|
||||
for rule in rule_provider.payload:
|
||||
clash_rule = ClashRuleParser.parse_rule_line(f"{rule},DIRECT")
|
||||
if not clash_rule:
|
||||
raise ValueError(f"Invalid clash_rule: {rule}")
|
||||
except Exception as e:
|
||||
error_message = f"Failed to parse rule provider: Invalid data={item}, error={repr(e)}"
|
||||
logger.error(error_message)
|
||||
return schemas.Response(success=False, message=str(error_message))
|
||||
if name != new_name:
|
||||
self._extra_rule_providers.pop(name, None)
|
||||
self._extra_rule_providers[new_name] = item
|
||||
self.save_data('extra_rule_providers', self._extra_rule_providers)
|
||||
return schemas.Response(success=True)
|
||||
|
||||
def delete_extra_rule_provider(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return schemas.Response(success=False, message='')
|
||||
name = params.get('name')
|
||||
if not name:
|
||||
return schemas.Response(success=False, message="Missing param: name")
|
||||
self._extra_rule_providers.pop(name, None)
|
||||
self.save_data('extra_rule_providers', self._extra_rule_providers)
|
||||
return schemas.Response(success=True)
|
||||
|
||||
def get_proxy_groups(self) -> schemas.Response:
|
||||
return schemas.Response(success=True, data={'proxy_groups': self._proxy_groups})
|
||||
|
||||
@@ -569,14 +647,14 @@ class ClashRuleProvider(_PluginBase):
|
||||
if not item.get('name') or any(x.get('name') == item.get('name') for x in self._proxy_groups):
|
||||
return schemas.Response(success=False, message=f"The proxy group name {item.get('name')} already exists")
|
||||
try:
|
||||
ProxyGroupValidator.parse_obj(item)
|
||||
ProxyGroup.parse_obj(item)
|
||||
except Exception as e:
|
||||
error_message = f"Failed to parse proxy group: Invalid data={item}, error={repr(e)}"
|
||||
logger.error(error_message)
|
||||
return schemas.Response(success=False, message=str(error_message))
|
||||
new_item = {}
|
||||
for k, v in item.items():
|
||||
if type(v) is str and len(v) == 0:
|
||||
if v == '':
|
||||
continue
|
||||
if v is None:
|
||||
continue
|
||||
@@ -608,9 +686,11 @@ class ClashRuleProvider(_PluginBase):
|
||||
if not self._clash_config:
|
||||
return None
|
||||
rule_providers = {}
|
||||
for key, value in self._clash_config.get("rule-providers", {}):
|
||||
if value.get("path", '').startwith("./CRP/"):
|
||||
continue
|
||||
for key, value in self._clash_config.get('rule-providers', {}).items():
|
||||
rule_providers[key] = value
|
||||
for key, value in self._extra_rule_providers.items():
|
||||
rule_providers[key] = value
|
||||
for key, value in self._acl4ssr_providers.items():
|
||||
rule_providers[key] = value
|
||||
return rule_providers
|
||||
|
||||
@@ -618,7 +698,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
rule_parser.rules = []
|
||||
for rule in rules:
|
||||
clash_rule = ClashRuleParser.parse_rule_dict(rule)
|
||||
rule_parser.insert_rule_at_priority(clash_rule, rule.get("priority"))
|
||||
rule_parser.insert_rule_at_priority(clash_rule, rule.get('priority'))
|
||||
self.__save_data()
|
||||
|
||||
def __reorder_rules(self, rule_parser: ClashRuleParser, moved_priority, target_priority):
|
||||
@@ -665,13 +745,14 @@ class ClashRuleProvider(_PluginBase):
|
||||
self.__save_data()
|
||||
return
|
||||
|
||||
def update_rule_by_priority(self, rule: Dict[str, Any], rule_parser: ClashRuleParser) -> bool:
|
||||
if not isinstance(rule.get("priority"), int):
|
||||
def update_rule_by_priority(self, rule: Dict[str, Any], priority: int, rule_parser: ClashRuleParser) -> bool:
|
||||
if type(rule.get("priority")) is not int or type(priority) is not int:
|
||||
return False
|
||||
clash_rule = ClashRuleParser.parse_rule_dict(rule)
|
||||
if not clash_rule:
|
||||
logger.error(f"Failed to update rule at priority {priority}. Invalid clash rule: {rule}")
|
||||
return False
|
||||
res = rule_parser.update_rule_at_priority(clash_rule, rule.get("priority"))
|
||||
res = rule_parser.update_rule_at_priority(clash_rule, priority)
|
||||
self.__save_data()
|
||||
return res
|
||||
|
||||
@@ -712,7 +793,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
days = seconds_left // 86400
|
||||
return f"{days}天后过期" if days > 0 else "已过期"
|
||||
|
||||
def update_subscription_service(self):
|
||||
def refresh_subscription_service(self):
|
||||
res = self.__refresh_subscription()
|
||||
if res:
|
||||
used = self._subscription_info['download'] + self._subscription_info['upload']
|
||||
@@ -730,19 +811,51 @@ class ClashRuleProvider(_PluginBase):
|
||||
text=f"{message}"
|
||||
)
|
||||
|
||||
def __refresh_acl4ssr(self):
|
||||
logger.info(f"Refreshing ACL4SSR")
|
||||
# 配置参数
|
||||
owner = 'ACL4SSR'
|
||||
repo = 'ACL4SSR'
|
||||
paths = ['Clash/Providers', 'Clash/Providers/Ruleset']
|
||||
api_url = f"https://api.github.com/repos/{owner}/{repo}/contents/%s"
|
||||
branch = 'master'
|
||||
for path in paths:
|
||||
response = RequestUtils().get_res(api_url % path, headers=settings.GITHUB_HEADERS, params={'ref': branch})
|
||||
if not response:
|
||||
return
|
||||
files = response.json()
|
||||
yaml_files = [f for f in files if f["type"] == "file" and f["name"].endswith((".yaml", ".yml"))]
|
||||
self._acl4ssr_providers = {}
|
||||
for f in yaml_files:
|
||||
name = f"{self._acl4ssr_prefix}{f['name'][:f['name'].rfind('.')]}"
|
||||
path = f"./ACL4SSR/{f['name']}"
|
||||
provider = {'type': 'http', 'path': path, 'url': f["download_url"], 'interval': 600,
|
||||
'behavior': 'classical', 'format': 'yaml', 'size-limit': 0}
|
||||
if name not in self._acl4ssr_providers:
|
||||
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]
|
||||
logger.info(f"Refreshing: {url}")
|
||||
ret = RequestUtils(accept_type="text/html",
|
||||
proxies=settings.PROXY if self._proxy else None
|
||||
).get_res(url)
|
||||
ret = None
|
||||
for i in range(0, self._retry_times):
|
||||
ret = RequestUtils(accept_type="text/html",
|
||||
proxies=settings.PROXY if self._proxy else None
|
||||
).get_res(url)
|
||||
if ret:
|
||||
break
|
||||
if not ret:
|
||||
return False
|
||||
try:
|
||||
rs = yaml.load(ret.content, Loader=yaml.FullLoader)
|
||||
if rs.get('rules') is None:
|
||||
rs['rules'] = []
|
||||
if self._discard_rules:
|
||||
rs['rules'] = []
|
||||
self._clash_config = self.__remove_nodes_by_keywords(rs)
|
||||
except Exception as e:
|
||||
logger.error(f"解析配置出错: {e}")
|
||||
@@ -837,16 +950,18 @@ class ClashRuleProvider(_PluginBase):
|
||||
removed_proxies = []
|
||||
for proxy_group in clash_config.get("proxy-groups", []):
|
||||
proxy_group['proxies'] = [x for x in proxy_group.get('proxies') if x not in removed_proxies]
|
||||
# clash_config["proxy-groups"] = [x for x in clash_config.get("proxy-groups", []) if x.get("proxies")]
|
||||
return clash_config
|
||||
|
||||
def clash_config(self) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
整理clash配置,返回配置字典
|
||||
"""
|
||||
if not self._clash_config:
|
||||
return None
|
||||
self.__insert_ruleset()
|
||||
self._top_rules = self._clash_rule_parser.to_string()
|
||||
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)
|
||||
if proxy_groups:
|
||||
@@ -864,41 +979,56 @@ class ClashRuleProvider(_PluginBase):
|
||||
# 添加按大洲代理组
|
||||
if self._group_by_region:
|
||||
if self._proxy_groups_by_region:
|
||||
if clash_config.get("proxy-groups"):
|
||||
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)
|
||||
|
||||
top_rules = []
|
||||
for rule in self._clash_rule_parser.rules:
|
||||
if (not isinstance(rule.action, Action) and
|
||||
not len([x for x in self.clash_outbound(clash_config) if rule.action == x.get("name", '')])):
|
||||
logger.warn(f"出站 {rule.action} 不存在, 跳过 {rule.raw_rule}")
|
||||
continue
|
||||
top_rules.append(rule.raw_rule)
|
||||
clash_config["rules"] = self._top_rules + clash_config.get("rules", [])
|
||||
outbound_names = list(x.get("name") for x in self.clash_outbound(clash_config))
|
||||
|
||||
# 添加 extra rule providers
|
||||
if self._extra_rule_providers:
|
||||
clash_config['rule-providers'].update(self._extra_rule_providers)
|
||||
|
||||
# 通过 ruleset rules 添加 rule-providers
|
||||
self._rule_provider = {}
|
||||
for r in self._clash_rule_parser.rules:
|
||||
if r.rule_type == RuleType.RULE_SET and r.payload.startswith(self._ruleset_prefix):
|
||||
action_str = f"{r.action.value}" if isinstance(r.action, Action) else r.action
|
||||
for rule in self._ruleset_rule_parser.rules:
|
||||
action_str = f"{rule.action.value}" if isinstance(rule.action, Action) else rule.action
|
||||
rule_provider_name = f'{self._ruleset_prefix}{action_str}'
|
||||
if rule_provider_name not in self._rule_provider:
|
||||
path_name = hashlib.sha256(action_str.encode('utf-8')).hexdigest()[:10]
|
||||
self._ruleset_names[path_name] = r.payload
|
||||
self._ruleset_names[path_name] = rule.payload
|
||||
sub_url = (f"{self._movie_pilot_url}/api/v1/plugin/ClashRuleProvider/ruleset?"
|
||||
f"name={path_name}&apikey={settings.API_TOKEN}")
|
||||
self._rule_provider[r.payload] = {"behavior": "classical",
|
||||
"format": "yaml",
|
||||
"interval": 3600,
|
||||
"path": f"./CRP/{path_name}.yaml",
|
||||
"type": "http",
|
||||
"url": sub_url}
|
||||
if clash_config.get("rule-providers"):
|
||||
self._rule_provider[rule_provider_name] = {"behavior": "classical",
|
||||
"format": "yaml",
|
||||
"interval": 3600,
|
||||
"path": f"./CRP/{path_name}.yaml",
|
||||
"type": "http",
|
||||
"url": sub_url}
|
||||
clash_config['rule-providers'].update(self._rule_provider)
|
||||
# 添加规则
|
||||
for rule in self._clash_rule_parser.rules:
|
||||
if not isinstance(rule.action, Action) and rule.action not in outbound_names:
|
||||
logger.warn(f"出站 {rule.action} 不存在, 跳过 {rule.raw_rule}")
|
||||
continue
|
||||
if rule.rule_type == RuleType.RULE_SET:
|
||||
# 添加ACL4SSR Rules
|
||||
if rule.payload in self._acl4ssr_providers:
|
||||
clash_config['rule-providers'][rule.payload] = self._acl4ssr_providers[rule.payload]
|
||||
if rule.payload not in clash_config.get('rule-providers', {}):
|
||||
logger.warn(f"规则集合 {rule.payload} 不存在, 跳过 {rule.raw_rule}")
|
||||
continue
|
||||
top_rules.append(rule.raw_rule)
|
||||
clash_config["rules"] = self._top_rules + clash_config.get("rules", [])
|
||||
if self._rule_provider:
|
||||
clash_config['rule-providers'] = clash_config.get('rule-providers') or {}
|
||||
clash_config['rule-providers'].update(self._rule_provider)
|
||||
else:
|
||||
clash_config['rule-providers'] = self._rule_provider
|
||||
|
||||
key_to_delete = []
|
||||
for key, item in self._ruleset_names.items():
|
||||
if item not in clash_config['rule-providers']:
|
||||
if item not in clash_config.get('rule-providers', {}):
|
||||
key_to_delete.append(key)
|
||||
for key in key_to_delete:
|
||||
del self._ruleset_names[key]
|
||||
|
||||
@@ -3,9 +3,58 @@ from typing import List, Dict, Any, Optional, Union, Callable, Literal
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from pydantic import BaseModel, Field, validator, HttpUrl
|
||||
|
||||
|
||||
class RuleProvider(BaseModel):
|
||||
type: Literal["http", "file", "inline"] = Field(..., description="Provider type")
|
||||
url: Optional[HttpUrl] = Field(None, description="Must be configured if the type is http")
|
||||
path: Optional[str] = Field(None, description="Optional, file path, must be unique.")
|
||||
interval: Optional[int] = Field(None, ge=0, description="The update interval for the provider, in seconds.")
|
||||
proxy: Optional[str] = Field(None, description="Download/update through the specified proxy.")
|
||||
behavior: Optional[Literal["domain", "ipcidr", "classical"]] = Field(None,
|
||||
description="Behavior of the rule provider")
|
||||
format: Literal["yaml", "text", "mrs"] = Field("yaml", description="Format of the rule provider file")
|
||||
size_limit: int = Field(0, ge=0, description="The maximum size of downloadable files in bytes (0 for no limit)")
|
||||
payload: Optional[List[str]] = Field(None, description="Content, only effective when type is inline")
|
||||
|
||||
@validator("url", pre=True, always=True)
|
||||
def check_url_for_http_type(cls, v, values):
|
||||
if values.get("type") == "http" and v is None:
|
||||
raise ValueError("url must be configured if the type is 'http'")
|
||||
return v
|
||||
|
||||
@validator("path", pre=True, always=True)
|
||||
def check_path_for_file_type(cls, v, values):
|
||||
if values.get("type") == "file" and v is None:
|
||||
raise ValueError("path must be configured if the type is 'file'")
|
||||
return v
|
||||
|
||||
@validator("payload", pre=True, always=True)
|
||||
def handle_payload_for_non_inline_type(cls, v, values):
|
||||
# If type is not inline, payload should be ignored (set to None)
|
||||
if values.get("type") != "inline" and v is not None:
|
||||
return None
|
||||
return v
|
||||
|
||||
@validator("payload")
|
||||
def check_payload_type_for_inline(cls, v, values):
|
||||
if values.get("type") == "inline" and v is not None and not isinstance(v, list):
|
||||
raise ValueError("payload must be a list of strings when type is 'inline'")
|
||||
if values.get("type") == "inline" and v is None:
|
||||
raise ValueError("payload must be configured if the type is 'inline'")
|
||||
return v
|
||||
|
||||
@validator("format")
|
||||
def check_format_with_behavior(cls, v, values):
|
||||
behavior = values.get("behavior")
|
||||
if v == "mrs" and behavior not in ["domain", "ipcidr"]:
|
||||
raise ValueError("mrs format only supports 'domain' or 'ipcidr' behavior")
|
||||
return v
|
||||
|
||||
class RuleProviders(BaseModel):
|
||||
__root__: dict[str, RuleProvider]
|
||||
|
||||
class ProxyGroupBase(BaseModel):
|
||||
"""
|
||||
包含所有代理组类型共有的通用字段。
|
||||
@@ -85,11 +134,7 @@ class LoadBalanceGroup(ProxyGroupBase):
|
||||
# --- Discriminated Union ---
|
||||
ProxyGroupUnion = Union[SelectGroup, RelayGroup, FallbackGroup, UrlTestGroup, LoadBalanceGroup]
|
||||
|
||||
class ProxyGroupValidator(BaseModel):
|
||||
"""
|
||||
这是Pydantic V1的验证器。
|
||||
它使用 __root__ 字段来处理可辨识联合。
|
||||
"""
|
||||
class ProxyGroup(BaseModel):
|
||||
__root__: ProxyGroupUnion
|
||||
|
||||
class RuleType(Enum):
|
||||
@@ -212,7 +257,6 @@ class ClashRuleParser:
|
||||
return ClashRuleParser._parse_regular_rule(line)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error parsing rule '{line}': {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
@@ -228,13 +272,16 @@ class ClashRuleParser:
|
||||
conditions_str += f'({condition.get("type")},{condition.get("payload")})'
|
||||
conditions_str = f"({conditions_str})"
|
||||
raw_rule = f"{clash_rule.get('type')},{conditions_str},{clash_rule.get('action')}"
|
||||
return ClashRuleParser._parse_logic_rule(raw_rule)
|
||||
rule = ClashRuleParser._parse_logic_rule(raw_rule)
|
||||
elif clash_rule.get("type") == 'MATCH':
|
||||
raw_rule = f"{clash_rule.get('type')},{clash_rule.get('action')}"
|
||||
return ClashRuleParser._parse_match_rule(raw_rule)
|
||||
rule = ClashRuleParser._parse_match_rule(raw_rule)
|
||||
else:
|
||||
raw_rule = f"{clash_rule.get('type')},{clash_rule.get('payload')},{clash_rule.get('action')}"
|
||||
return ClashRuleParser._parse_regular_rule(raw_rule)
|
||||
rule = ClashRuleParser._parse_regular_rule(raw_rule)
|
||||
if rule and 'priority' in clash_rule:
|
||||
rule.priority = clash_rule['priority']
|
||||
return rule
|
||||
|
||||
@staticmethod
|
||||
def _parse_match_rule(line: str) -> MatchRule:
|
||||
@@ -339,7 +386,7 @@ class ClashRuleParser:
|
||||
)
|
||||
conditions.append(condition)
|
||||
except ValueError:
|
||||
print(f"Unknown rule type in logic condition: {rule_type_str}")
|
||||
continue
|
||||
|
||||
return conditions
|
||||
|
||||
@@ -396,7 +443,7 @@ class ClashRuleParser:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def to_string(self) -> List[str]:
|
||||
def to_list(self) -> List[str]:
|
||||
result = []
|
||||
for rule in self.rules:
|
||||
result.append(rule.raw_rule)
|
||||
@@ -478,12 +525,19 @@ class ClashRuleParser:
|
||||
self.rules.sort(key=lambda r: r.priority)
|
||||
|
||||
def update_rule_at_priority(self, clash_rule: Union[ClashRule, LogicRule], priority: int) -> bool:
|
||||
for index, existing_rule in enumerate(self.rules):
|
||||
if existing_rule.priority == priority:
|
||||
self.rules[index] = clash_rule
|
||||
self.rules[index].priority = priority
|
||||
return True
|
||||
return False
|
||||
if clash_rule.priority == priority:
|
||||
for index, existing_rule in enumerate(self.rules):
|
||||
if existing_rule.priority == priority:
|
||||
self.rules[index] = clash_rule
|
||||
self.rules[index].priority = priority
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
removed = self.remove_rule_at_priority(priority)
|
||||
if not removed:
|
||||
return False
|
||||
self.insert_rule_at_priority(clash_rule, clash_rule.priority)
|
||||
return True
|
||||
|
||||
def remove_rule_at_priority(self, priority: int) -> Optional[Union[ClashRule, LogicRule, MatchRule]]:
|
||||
"""Remove rule at specific priority and adjust remaining priorities"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
.plugin-config[data-v-52f6e9ed] {
|
||||
.plugin-config[data-v-c50374dc] {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -49,18 +49,6 @@ const testResult = reactive({
|
||||
message: ''
|
||||
});
|
||||
|
||||
// Cron 选项
|
||||
const cronOptions = [
|
||||
{text: '每5分钟', value: '5min', cron: '*/5 * * * *'},
|
||||
{text: '每15分钟', value: '15min', cron: '*/15 * * * *'},
|
||||
{text: '每30分钟', value: '30min', cron: '*/30 * * * *'},
|
||||
{text: '每小时', value: '1hour', cron: '0 * * * *'},
|
||||
{text: '每2小时', value: '2hours', cron: '0 */2 * * *'},
|
||||
{text: '每6小时', value: '6hours', cron: '0 */6 * * *'},
|
||||
{text: '每12小时', value: '12hours', cron: '0 */12 * * *'},
|
||||
{text: '每天', value: '1day', cron: '0 0 * * *'},
|
||||
{text: '自定义', value: 'custom', cron: ''},
|
||||
];
|
||||
|
||||
// 默认配置
|
||||
const defaultConfig = {
|
||||
@@ -76,9 +64,12 @@ const defaultConfig = {
|
||||
proxy: false,
|
||||
notify: false,
|
||||
auto_update_subscriptions: true,
|
||||
ruleset_prefix: '📂<-',
|
||||
ruleset_prefix: '📂<=',
|
||||
acl4ssr_prefix: '🗂️=>',
|
||||
group_by_region: false,
|
||||
refresh_delay: 5,
|
||||
discard_rules: false,
|
||||
enable_acl4ssr: false,
|
||||
};
|
||||
|
||||
// 响应式配置对象
|
||||
@@ -95,14 +86,6 @@ onMounted(() => {
|
||||
config[key] = props.initialConfig[key];
|
||||
}
|
||||
});
|
||||
|
||||
// 设置对应的cron选项
|
||||
const cronOption = cronOptions.find(option => option.cron === config.cron_string);
|
||||
if (cronOption) {
|
||||
selectedCronOption.value = cronOption.value;
|
||||
} else {
|
||||
selectedCronOption.value = 'custom';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -129,25 +112,6 @@ function validateSubLinks(links) {
|
||||
return true
|
||||
}
|
||||
|
||||
function validateCronExpression(cronStr) {
|
||||
if (!cronStr) return '请输入Cron表达式'
|
||||
|
||||
// 简单的cron表达式验证
|
||||
const parts = cronStr.trim().split(/\s+/);
|
||||
if (parts.length !== 5) {
|
||||
return 'Cron表达式应包含5个部分 (分 时 日 月 周)'
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 更新cron字符串
|
||||
function updateCronString(optionValue) {
|
||||
const option = cronOptions.find(opt => opt.value === optionValue);
|
||||
if (option && option.cron) {
|
||||
config.cron_string = option.cron;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
async function testConnection() {
|
||||
testing.value = true;
|
||||
@@ -268,7 +232,7 @@ return (_ctx, _cache) => {
|
||||
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");
|
||||
@@ -292,7 +256,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { left: "" }, {
|
||||
default: _withCtx(() => _cache[20] || (_cache[20] = [
|
||||
default: _withCtx(() => _cache[22] || (_cache[22] = [
|
||||
_createTextVNode("mdi-close")
|
||||
])),
|
||||
_: 1
|
||||
@@ -303,7 +267,7 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_card_title, null, {
|
||||
default: _withCtx(() => _cache[19] || (_cache[19] = [
|
||||
default: _withCtx(() => _cache[21] || (_cache[21] = [
|
||||
_createTextVNode("Clash Rule Provider 插件配置")
|
||||
])),
|
||||
_: 1
|
||||
@@ -329,16 +293,16 @@ return (_ctx, _cache) => {
|
||||
ref_key: "form",
|
||||
ref: form,
|
||||
modelValue: isFormValid.value,
|
||||
"onUpdate:modelValue": _cache[17] || (_cache[17] = $event => ((isFormValid).value = $event)),
|
||||
"onUpdate:modelValue": _cache[19] || (_cache[19] = $event => ((isFormValid).value = $event)),
|
||||
onSubmit: _withModifiers(saveConfig, ["prevent"])
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_cache[31] || (_cache[31] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "基本设置", -1)),
|
||||
_cache[33] || (_cache[33] = _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: "4"
|
||||
md: "3"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_switch, {
|
||||
@@ -347,15 +311,14 @@ return (_ctx, _cache) => {
|
||||
label: "启用插件",
|
||||
color: "primary",
|
||||
inset: "",
|
||||
hint: "启用后插件将开始监控和同步",
|
||||
"persistent-hint": ""
|
||||
hint: "启用后插件将开始监控和同步"
|
||||
}, null, 8, ["modelValue"])
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "4"
|
||||
md: "3"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_switch, {
|
||||
@@ -364,15 +327,14 @@ return (_ctx, _cache) => {
|
||||
label: "启用代理",
|
||||
color: "primary",
|
||||
inset: "",
|
||||
hint: "是否使用系统代理进行网络请求",
|
||||
"persistent-hint": ""
|
||||
hint: "是否使用系统代理进行网络请求"
|
||||
}, null, 8, ["modelValue"])
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "4"
|
||||
md: "3"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_switch, {
|
||||
@@ -381,8 +343,23 @@ return (_ctx, _cache) => {
|
||||
label: "启用通知",
|
||||
color: "primary",
|
||||
inset: "",
|
||||
hint: "执行完成后发送通知消息",
|
||||
"persistent-hint": ""
|
||||
hint: "执行完成后发送通知消息"
|
||||
}, 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订阅配置"
|
||||
}, null, 8, ["modelValue"])
|
||||
]),
|
||||
_: 1
|
||||
@@ -390,21 +367,20 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[32] || (_cache[32] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "订阅配置", -1)),
|
||||
_cache[34] || (_cache[34] = _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[3] || (_cache[3] = $event => ((config.sub_links) = $event)),
|
||||
"onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((config.sub_links) = $event)),
|
||||
label: "订阅链接",
|
||||
variant: "outlined",
|
||||
multiple: "",
|
||||
chips: "",
|
||||
"closable-chips": "",
|
||||
hint: "添加一个Clash订阅链接, 按回车确认输入",
|
||||
"persistent-hint": "",
|
||||
rules: [validateSubLinks]
|
||||
}, {
|
||||
chip: _withCtx(({ props, item }) => [
|
||||
@@ -427,14 +403,13 @@ return (_ctx, _cache) => {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_combobox, {
|
||||
modelValue: config.filter_keywords,
|
||||
"onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((config.filter_keywords) = $event)),
|
||||
"onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((config.filter_keywords) = $event)),
|
||||
label: "节点过滤关键词",
|
||||
variant: "outlined",
|
||||
multiple: "",
|
||||
chips: "",
|
||||
"closable-chips": "",
|
||||
hint: "添加用于过滤节点的关键词",
|
||||
"persistent-hint": ""
|
||||
hint: "添加用于过滤节点的关键词"
|
||||
}, {
|
||||
chip: _withCtx(({ props, item }) => [
|
||||
_createVNode(_component_v_chip, _mergeProps(props, {
|
||||
@@ -456,24 +431,23 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[33] || (_cache[33] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "Clash 面板设置", -1)),
|
||||
_cache[35] || (_cache[35] = _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[5] || (_cache[5] = $event => ((config.clash_dashboard_url) = $event)),
|
||||
"onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((config.clash_dashboard_url) = $event)),
|
||||
label: "Clash 面板 URL",
|
||||
variant: "outlined",
|
||||
placeholder: "http://localhost:9090",
|
||||
hint: "Clash 控制面板的访问地址",
|
||||
"persistent-hint": "",
|
||||
rules: [v => !v || isValidUrl(v) || '请输入有效的URL地址']
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "primary" }, {
|
||||
default: _withCtx(() => _cache[21] || (_cache[21] = [
|
||||
default: _withCtx(() => _cache[23] || (_cache[23] = [
|
||||
_createTextVNode("mdi-web")
|
||||
])),
|
||||
_: 1
|
||||
@@ -488,19 +462,18 @@ return (_ctx, _cache) => {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_text_field, {
|
||||
modelValue: config.clash_dashboard_secret,
|
||||
"onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((config.clash_dashboard_secret) = $event)),
|
||||
"onUpdate:modelValue": _cache[7] || (_cache[7] = $event => ((config.clash_dashboard_secret) = $event)),
|
||||
label: "Clash 面板密钥",
|
||||
variant: "outlined",
|
||||
placeholder: "your-clash-secret",
|
||||
hint: "用于访问Clash API的密钥",
|
||||
"persistent-hint": "",
|
||||
"append-inner-icon": showClashSecret.value ? 'mdi-eye-off' : 'mdi-eye',
|
||||
type: showClashSecret.value ? 'text' : 'password',
|
||||
"onClick:appendInner": _cache[7] || (_cache[7] = $event => (showClashSecret.value = !showClashSecret.value))
|
||||
"onClick:appendInner": _cache[8] || (_cache[8] = $event => (showClashSecret.value = !showClashSecret.value))
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "warning" }, {
|
||||
default: _withCtx(() => _cache[22] || (_cache[22] = [
|
||||
default: _withCtx(() => _cache[24] || (_cache[24] = [
|
||||
_createTextVNode("mdi-key")
|
||||
])),
|
||||
_: 1
|
||||
@@ -514,24 +487,23 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[34] || (_cache[34] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "MoviePilot 设置", -1)),
|
||||
_cache[36] || (_cache[36] = _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[8] || (_cache[8] = $event => ((config.movie_pilot_url) = $event)),
|
||||
"onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ((config.movie_pilot_url) = $event)),
|
||||
label: "MoviePilot URL",
|
||||
variant: "outlined",
|
||||
placeholder: "http://localhost:3001",
|
||||
hint: "MoviePilot 服务的访问地址",
|
||||
"persistent-hint": "",
|
||||
rules: [v => !!v || 'MoviePilot URL不能为空', v => isValidUrl(v) || '请输入有效的URL地址']
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "success" }, {
|
||||
default: _withCtx(() => _cache[23] || (_cache[23] = [
|
||||
default: _withCtx(() => _cache[25] || (_cache[25] = [
|
||||
_createTextVNode("mdi-movie")
|
||||
])),
|
||||
_: 1
|
||||
@@ -545,58 +517,21 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[35] || (_cache[35] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "执行设置", -1)),
|
||||
_cache[37] || (_cache[37] = _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_select, {
|
||||
modelValue: selectedCronOption.value,
|
||||
"onUpdate:modelValue": [
|
||||
_cache[9] || (_cache[9] = $event => ((selectedCronOption).value = $event)),
|
||||
updateCronString
|
||||
],
|
||||
_createVNode(_component_v_cron_field, {
|
||||
modelValue: config.cron_string,
|
||||
"onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((config.cron_string) = $event)),
|
||||
label: "执行周期",
|
||||
items: cronOptions,
|
||||
variant: "outlined",
|
||||
"item-title": "text",
|
||||
"item-value": "value",
|
||||
hint: "选择插件执行的时间间隔",
|
||||
"persistent-hint": ""
|
||||
placeholder: "0 4 * * *",
|
||||
hint: "使用标准Cron表达式格式 (分 时 日 月 周)"
|
||||
}, null, 8, ["modelValue"])
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
(selectedCronOption.value === 'custom')
|
||||
? (_openBlock(), _createBlock(_component_v_col, {
|
||||
key: 0,
|
||||
cols: "12"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_text_field, {
|
||||
modelValue: config.cron_string,
|
||||
"onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((config.cron_string) = $event)),
|
||||
label: "自定义 Cron 表达式",
|
||||
variant: "outlined",
|
||||
placeholder: "0 */6 * * *",
|
||||
hint: "使用标准Cron表达式格式 (分 时 日 月 周)",
|
||||
"persistent-hint": "",
|
||||
rules: [validateCronExpression]
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[24] || (_cache[24] = [
|
||||
_createTextVNode("mdi-clock-outline")
|
||||
])),
|
||||
_: 1
|
||||
})
|
||||
]),
|
||||
_: 1
|
||||
}, 8, ["modelValue", "rules"])
|
||||
]),
|
||||
_: 1
|
||||
}))
|
||||
: _createCommentVNode("", true),
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "6"
|
||||
@@ -612,12 +547,11 @@ return (_ctx, _cache) => {
|
||||
min: "1",
|
||||
max: "300",
|
||||
hint: "请求的超时时间",
|
||||
"persistent-hint": "",
|
||||
rules: [v => v > 0 || '超时时间必须大于0']
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "warning" }, {
|
||||
default: _withCtx(() => _cache[25] || (_cache[25] = [
|
||||
default: _withCtx(() => _cache[26] || (_cache[26] = [
|
||||
_createTextVNode("mdi-timer")
|
||||
])),
|
||||
_: 1
|
||||
@@ -643,12 +577,11 @@ return (_ctx, _cache) => {
|
||||
min: "0",
|
||||
max: "10",
|
||||
hint: "失败时的重试次数",
|
||||
"persistent-hint": "",
|
||||
rules: [v => v >= 0 || '重试次数不能为负数']
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[26] || (_cache[26] = [
|
||||
default: _withCtx(() => _cache[27] || (_cache[27] = [
|
||||
_createTextVNode("mdi-refresh")
|
||||
])),
|
||||
_: 1
|
||||
@@ -672,12 +605,12 @@ return (_ctx, _cache) => {
|
||||
_createVNode(_component_v_expansion_panel_title, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { class: "mr-2" }, {
|
||||
default: _withCtx(() => _cache[27] || (_cache[27] = [
|
||||
default: _withCtx(() => _cache[28] || (_cache[28] = [
|
||||
_createTextVNode("mdi-cog")
|
||||
])),
|
||||
_: 1
|
||||
}),
|
||||
_cache[28] || (_cache[28] = _createTextVNode(" 高级选项 "))
|
||||
_cache[29] || (_cache[29] = _createTextVNode(" 高级选项 "))
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
@@ -687,34 +620,48 @@ return (_ctx, _cache) => {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "6"
|
||||
md: "4"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_switch, {
|
||||
modelValue: config.auto_update_subscriptions,
|
||||
"onUpdate:modelValue": _cache[13] || (_cache[13] = $event => ((config.auto_update_subscriptions) = $event)),
|
||||
label: "自动更新订阅",
|
||||
modelValue: config.discard_rules,
|
||||
"onUpdate:modelValue": _cache[13] || (_cache[13] = $event => ((config.discard_rules) = $event)),
|
||||
label: "丢弃订阅规则",
|
||||
color: "primary",
|
||||
inset: "",
|
||||
hint: "定期自动更新Clash订阅配置",
|
||||
"persistent-hint": ""
|
||||
hint: "不保留订阅配置文件的rules字段"
|
||||
}, null, 8, ["modelValue"])
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "6"
|
||||
md: "4"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_switch, {
|
||||
modelValue: config.enable_acl4ssr,
|
||||
"onUpdate:modelValue": _cache[14] || (_cache[14] = $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[14] || (_cache[14] = $event => ((config.group_by_region) = $event)),
|
||||
"onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ((config.group_by_region) = $event)),
|
||||
label: "按大洲分组节点",
|
||||
color: "primary",
|
||||
inset: "",
|
||||
hint: "启用后根据名称,将节点添加到代理组",
|
||||
"persistent-hint": ""
|
||||
hint: "启用后根据名称,将节点添加到代理组"
|
||||
}, null, 8, ["modelValue"])
|
||||
]),
|
||||
_: 1
|
||||
@@ -726,23 +673,22 @@ return (_ctx, _cache) => {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "6"
|
||||
md: "4"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_text_field, {
|
||||
modelValue: config.ruleset_prefix,
|
||||
"onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ((config.ruleset_prefix) = $event)),
|
||||
"onUpdate:modelValue": _cache[16] || (_cache[16] = $event => ((config.ruleset_prefix) = $event)),
|
||||
label: "规则集前缀",
|
||||
variant: "outlined",
|
||||
placeholder: "📂<-",
|
||||
placeholder: "📂<=",
|
||||
rules: [v => !!v || '规则集前缀不能为空'],
|
||||
hint: "为生成的规则集添加前缀",
|
||||
"persistent-hint": ""
|
||||
hint: "为生成的规则集添加前缀"
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[29] || (_cache[29] = [
|
||||
_createTextVNode("mdi-prefix")
|
||||
default: _withCtx(() => _cache[30] || (_cache[30] = [
|
||||
_createTextVNode("mdi-palette")
|
||||
])),
|
||||
_: 1
|
||||
})
|
||||
@@ -754,12 +700,39 @@ return (_ctx, _cache) => {
|
||||
}),
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "6"
|
||||
md: "4"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_text_field, {
|
||||
modelValue: config.acl4ssr_prefix,
|
||||
"onUpdate:modelValue": _cache[17] || (_cache[17] = $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[31] || (_cache[31] = [
|
||||
_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[16] || (_cache[16] = $event => ((config.refresh_delay) = $event)),
|
||||
"onUpdate:modelValue": _cache[18] || (_cache[18] = $event => ((config.refresh_delay) = $event)),
|
||||
modelModifiers: { number: true },
|
||||
label: "刷新延迟",
|
||||
variant: "outlined",
|
||||
@@ -768,12 +741,11 @@ return (_ctx, _cache) => {
|
||||
max: "30",
|
||||
suffix: "秒",
|
||||
hint: "通知Clash刷新规则集的延迟时间",
|
||||
"persistent-hint": "",
|
||||
rules: [v => v >= 0 || '刷新延迟不能为负数']
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[30] || (_cache[30] = [
|
||||
default: _withCtx(() => _cache[32] || (_cache[32] = [
|
||||
_createTextVNode("mdi-clock-outline")
|
||||
])),
|
||||
_: 1
|
||||
@@ -802,6 +774,21 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_createVNode(_component_v_alert, {
|
||||
type: "info",
|
||||
text: "",
|
||||
class: "mb-6",
|
||||
variant: "tonal"
|
||||
}, {
|
||||
default: _withCtx(() => _cache[38] || (_cache[38] = [
|
||||
_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, {
|
||||
@@ -810,12 +797,12 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { left: "" }, {
|
||||
default: _withCtx(() => _cache[36] || (_cache[36] = [
|
||||
default: _withCtx(() => _cache[39] || (_cache[39] = [
|
||||
_createTextVNode("mdi-view-dashboard-edit")
|
||||
])),
|
||||
_: 1
|
||||
}),
|
||||
_cache[37] || (_cache[37] = _createTextVNode(" 规则 "))
|
||||
_cache[40] || (_cache[40] = _createTextVNode(" 规则 "))
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
@@ -823,7 +810,7 @@ return (_ctx, _cache) => {
|
||||
color: "secondary",
|
||||
onClick: resetForm
|
||||
}, {
|
||||
default: _withCtx(() => _cache[38] || (_cache[38] = [
|
||||
default: _withCtx(() => _cache[41] || (_cache[41] = [
|
||||
_createTextVNode("重置")
|
||||
])),
|
||||
_: 1
|
||||
@@ -833,7 +820,7 @@ return (_ctx, _cache) => {
|
||||
onClick: testConnection,
|
||||
loading: testing.value
|
||||
}, {
|
||||
default: _withCtx(() => _cache[39] || (_cache[39] = [
|
||||
default: _withCtx(() => _cache[42] || (_cache[42] = [
|
||||
_createTextVNode("测试连接")
|
||||
])),
|
||||
_: 1
|
||||
@@ -845,7 +832,7 @@ return (_ctx, _cache) => {
|
||||
onClick: saveConfig,
|
||||
loading: saving.value
|
||||
}, {
|
||||
default: _withCtx(() => _cache[40] || (_cache[40] = [
|
||||
default: _withCtx(() => _cache[43] || (_cache[43] = [
|
||||
_createTextVNode(" 保存配置 ")
|
||||
])),
|
||||
_: 1
|
||||
@@ -860,7 +847,7 @@ return (_ctx, _cache) => {
|
||||
variant: "tonal",
|
||||
closable: "",
|
||||
class: "ma-4 mt-0",
|
||||
"onClick:close": _cache[18] || (_cache[18] = $event => (testResult.show = false))
|
||||
"onClick:close": _cache[20] || (_cache[20] = $event => (testResult.show = false))
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createElementVNode("div", _hoisted_2, [
|
||||
@@ -887,6 +874,6 @@ return (_ctx, _cache) => {
|
||||
}
|
||||
|
||||
};
|
||||
const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-52f6e9ed"]]);
|
||||
const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-c50374dc"]]);
|
||||
|
||||
export { ConfigComponent as default };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
||||
|
||||
.plugin-page[data-v-783fd7ef] {
|
||||
.plugin-page[data-v-455476d4] {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 使卡片等宽并适应移动端 */
|
||||
.d-flex.flex-wrap[data-v-783fd7ef] {
|
||||
.d-flex.flex-wrap[data-v-455476d4] {
|
||||
gap: 16px;
|
||||
}
|
||||
.url-display[data-v-783fd7ef] {
|
||||
.url-display[data-v-455476d4] {
|
||||
word-break: break-all;
|
||||
padding: 8px;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
@@ -17,19 +17,19 @@
|
||||
|
||||
/* 移动端堆叠布局 */
|
||||
@media (max-width: 768px) {
|
||||
.d-flex.flex-wrap[data-v-783fd7ef] {
|
||||
.d-flex.flex-wrap[data-v-455476d4] {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add visual distinction between sections */
|
||||
.ruleset-section[data-v-783fd7ef] {
|
||||
.ruleset-section[data-v-455476d4] {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.top-section[data-v-783fd7ef] {
|
||||
.top-section[data-v-455476d4] {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
@@ -37,12 +37,12 @@
|
||||
}
|
||||
|
||||
/* Optional: Add different border colors to further distinguish */
|
||||
.ruleset-section[data-v-783fd7ef] {
|
||||
.ruleset-section[data-v-455476d4] {
|
||||
border-left: 4px solid #2196F3; /* Blue accent */
|
||||
}
|
||||
.top-section[data-v-783fd7ef] {
|
||||
.top-section[data-v-455476d4] {
|
||||
border-left: 4px solid #4CAF50; /* Green accent */
|
||||
}
|
||||
.drag-handle[data-v-783fd7ef] {
|
||||
.drag-handle[data-v-455476d4] {
|
||||
cursor: move;
|
||||
}
|
||||
@@ -2,11 +2,11 @@ const currentImports = {};
|
||||
const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']);
|
||||
let moduleMap = {
|
||||
"./Page":()=>{
|
||||
dynamicLoadingCss(["__federation_expose_Page-_CExF_DI.css"], false, './Page');
|
||||
return __federation_import('./__federation_expose_Page-C2_enJ99.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
|
||||
dynamicLoadingCss(["__federation_expose_Page-Djrrbsow.css"], false, './Page');
|
||||
return __federation_import('./__federation_expose_Page-D_nruYha.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
|
||||
"./Config":()=>{
|
||||
dynamicLoadingCss(["__federation_expose_Config-nL3Pv4Qs.css"], false, './Config');
|
||||
return __federation_import('./__federation_expose_Config-DZF0yyTH.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
|
||||
dynamicLoadingCss(["__federation_expose_Config-BJvXq0hj.css"], false, './Config');
|
||||
return __federation_import('./__federation_expose_Config-Btg4HYx3.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
|
||||
"./Dashboard":()=>{
|
||||
dynamicLoadingCss([], false, './Dashboard');
|
||||
return __federation_import('./__federation_expose_Dashboard-DKtydfsT.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},};
|
||||
|
||||
Reference in New Issue
Block a user