From 1ad19a5b23f1bcf771b65c5643d4f24a4e9bd2e8 Mon Sep 17 00:00:00 2001 From: wumode Date: Sat, 2 May 2026 17:03:31 +0800 Subject: [PATCH] feat(ClashRuleProvider): bump version to 2.1.5 and support xhttp protocol --- package.v2.json | 4 +- plugins.v2/clashruleprovider/__init__.py | 2 +- ...__federation_expose_Dashboard-CABqciWS.js} | 4 +- ... => __federation_expose_Page-BVPPK5SA.css} | 10 +-- ...s => __federation_expose_Page-DfFWx370.js} | 23 ++++-- .../dist/assets/remoteEntry.js | 6 +- .../models/proxy/networkmixin.py | 74 ++++++++++++++++++- 7 files changed, 103 insertions(+), 20 deletions(-) rename plugins.v2/clashruleprovider/dist/assets/{__federation_expose_Dashboard-CybypqLB.js => __federation_expose_Dashboard-CABqciWS.js} (99%) rename plugins.v2/clashruleprovider/dist/assets/{__federation_expose_Page-CJILOVp4.css => __federation_expose_Page-BVPPK5SA.css} (88%) rename plugins.v2/clashruleprovider/dist/assets/{__federation_expose_Page-DU-ODF61.js => __federation_expose_Page-DfFWx370.js} (99%) diff --git a/package.v2.json b/package.v2.json index 9ce92cf..2059ee5 100644 --- a/package.v2.json +++ b/package.v2.json @@ -518,12 +518,14 @@ "name": "Clash Rule Provider", "description": "随时为Clash添加一些额外的规则。", "labels": "工具", - "version": "2.1.3", + "version": "2.1.5", "icon": "Mihomo_Meta_A.png", "author": "wumode", "level": 1, "release": true, "history": { + "v2.1.5": "优化仪表盘连接鉴权;优化订阅更新提示", + "v2.1.4": "支持 xhttp 协议", "v2.1.3": "修复代理删除问题", "v2.1.2": "修复规则集序列化错误", "v2.1.1": "增强数据管理功能", diff --git a/plugins.v2/clashruleprovider/__init__.py b/plugins.v2/clashruleprovider/__init__.py index d7883f2..c6257bd 100644 --- a/plugins.v2/clashruleprovider/__init__.py +++ b/plugins.v2/clashruleprovider/__init__.py @@ -36,7 +36,7 @@ class ClashRuleProvider(_PluginBase): # 插件图标 plugin_icon = "Mihomo_Meta_A.png" # 插件版本 - plugin_version = "2.1.3" + plugin_version = "2.1.5" # 插件作者 plugin_author = "wumode" # 作者主页 diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-CybypqLB.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-CABqciWS.js similarity index 99% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-CybypqLB.js rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-CABqciWS.js index af928de..822bc80 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-CybypqLB.js +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-CABqciWS.js @@ -15163,7 +15163,7 @@ const _sfc_main$2 = /* @__PURE__ */ _defineComponent$2({ }); if (props.allowRefresh && componentConfig.clash_available) { evtSource = new EventSource( - "api/v1/plugin/ClashRuleProvider/clash/ws/traffic?secret=" + componentConfig.secret + "api/v1/plugin/ClashRuleProvider/clash/ws/traffic?secret=" + encodeURIComponent(componentConfig.secret) ); evtSource.addEventListener("traffic", (event) => { const data = JSON.parse(event.data); @@ -15177,7 +15177,7 @@ const _sfc_main$2 = /* @__PURE__ */ _defineComponent$2({ } }); connectionsEvtSource = new EventSource( - "api/v1/plugin/ClashRuleProvider/clash/ws/connections?secret=" + componentConfig.secret + "api/v1/plugin/ClashRuleProvider/clash/ws/connections?secret=" + encodeURIComponent(componentConfig.secret) ); connectionsEvtSource.addEventListener("connections", (event) => { const data = JSON.parse(event.data); diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-CJILOVp4.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-BVPPK5SA.css similarity index 88% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-CJILOVp4.css rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-BVPPK5SA.css index 65f1d9a..64829fc 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-CJILOVp4.css +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-BVPPK5SA.css @@ -11,21 +11,21 @@ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important; } -.subscription-card[data-v-97c0f367] { +.subscription-card[data-v-b5b6e9bb] { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } -.subscription-card[data-v-97c0f367]:hover { +.subscription-card[data-v-b5b6e9bb]:hover { transform: translateY(-4px); box-shadow: 0 4px 25px 0 rgba(0, 0, 0, 0.1); border-color: rgb(var(--v-theme-primary)); } -.card-header[data-v-97c0f367] { +.card-header[data-v-b5b6e9bb] { background: rgba(var(--v-theme-surface-variant), 0.05); } -.bg-surface-variant-lighten[data-v-97c0f367] { +.bg-surface-variant-lighten[data-v-b5b6e9bb] { background: rgba(var(--v-theme-surface-variant), 0.02); } -.stats-grid[data-v-97c0f367] { +.stats-grid[data-v-b5b6e9bb] { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DU-ODF61.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DfFWx370.js similarity index 99% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DU-ODF61.js rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DfFWx370.js index d478c8c..a89bd36 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DU-ODF61.js +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DfFWx370.js @@ -10863,14 +10863,22 @@ const _sfc_main$c = /* @__PURE__ */ _defineComponent$c({ loading.value = true; emit("start-loading"); try { - await props.api.put("plugin/ClashRuleProvider/refresh", { + const result = await props.api.put("plugin/ClashRuleProvider/refresh", { url: props.url }); - emit("show-snackbar", { - show: true, - message: "订阅更新成功", - color: "success" - }); + if (result.success) { + emit("show-snackbar", { + show: true, + message: "订阅更新成功", + color: "success" + }); + } else { + emit("show-snackbar", { + show: true, + message: "订阅更新失败", + color: "error" + }); + } emit("refresh", [ "status", "clash-outbounds", @@ -10887,6 +10895,7 @@ const _sfc_main$c = /* @__PURE__ */ _defineComponent$c({ } } async function toggleSubscription(val) { + if (val === null) return; emit("start-loading"); try { await props.api.post("plugin/ClashRuleProvider/subscription-info", { @@ -11089,7 +11098,7 @@ const _sfc_main$c = /* @__PURE__ */ _defineComponent$c({ } }); -const SubscriptionCard = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-97c0f367"]]); +const SubscriptionCard = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-b5b6e9bb"]]); const {defineComponent:_defineComponent$b} = await importShared('vue'); diff --git a/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js b/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js index 50c6172..4c21e44 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-CJILOVp4.css"], false, './Page'); - return __federation_import('./__federation_expose_Page-DU-ODF61.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, + dynamicLoadingCss(["__federation_expose_Page-BVPPK5SA.css"], false, './Page'); + return __federation_import('./__federation_expose_Page-DfFWx370.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, "./Config":()=>{ dynamicLoadingCss(["__federation_expose_Config-CwbjkOP2.css"], false, './Config'); return __federation_import('./__federation_expose_Config-CY46uj5g.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, "./Dashboard":()=>{ dynamicLoadingCss(["__federation_expose_Dashboard-CFBdUa27.css"], false, './Dashboard'); - return __federation_import('./__federation_expose_Dashboard-CybypqLB.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},}; + return __federation_import('./__federation_expose_Dashboard-CABqciWS.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; diff --git a/plugins.v2/clashruleprovider/models/proxy/networkmixin.py b/plugins.v2/clashruleprovider/models/proxy/networkmixin.py index af1dbef..c498732 100644 --- a/plugins.v2/clashruleprovider/models/proxy/networkmixin.py +++ b/plugins.v2/clashruleprovider/models/proxy/networkmixin.py @@ -27,9 +27,81 @@ class WsOpts(BaseModel): v2ray_http_upgrade_fast_open: Optional[bool] = Field(None, alias='v2ray-http-upgrade-fast-open') +class XhttpReuseSettings(BaseModel): + max_concurrency: Optional[str] = Field(None, alias='max-concurrency') + max_connections: Optional[str] = Field(None, alias='max-connections') + c_max_reuse_times: Optional[str] = Field(None, alias='c-max-reuse-times') + h_max_request_times: Optional[str] = Field(None, alias='h-max-request-times') + h_max_reusable_secs: Optional[str] = Field(None, alias='h-max-reusable-secs') + h_keep_alive_period: Optional[int] = Field(None, alias='h-keep-alive-period') + + +class XhttpDownloadSettings(BaseModel): + # xhttp part + path: Optional[str] = None + host: Optional[str] = None + headers: Optional[Dict[str, str]] = None + no_grpc_header: Optional[bool] = Field(None, alias='no-grpc-header') + x_padding_bytes: Optional[str] = Field(None, alias='x-padding-bytes') + x_padding_obfs_mode: Optional[bool] = Field(None, alias='x-padding-obfs-mode') + x_padding_key: Optional[str] = Field(None, alias='x-padding-key') + x_padding_header: Optional[str] = Field(None, alias='x-padding-header') + x_padding_placement: Optional[str] = Field(None, alias='x-padding-placement') + x_padding_method: Optional[str] = Field(None, alias='x-padding-method') + uplink_http_method: Optional[str] = Field(None, alias='uplink-http-method') + session_placement: Optional[str] = Field(None, alias='session-placement') + session_key: Optional[str] = Field(None, alias='session-key') + seq_placement: Optional[str] = Field(None, alias='seq-placement') + seq_key: Optional[str] = Field(None, alias='seq-key') + uplink_data_placement: Optional[str] = Field(None, alias='uplink-data-placement') + uplink_data_key: Optional[str] = Field(None, alias='uplink-data-key') + uplink_chunk_size: Optional[str] = Field(None, alias='uplink-chunk-size') + sc_max_each_post_bytes: Optional[str] = Field(None, alias='sc-max-each-post-bytes') + sc_min_posts_interval_ms: Optional[str] = Field(None, alias='sc-min-posts-interval-ms') + reuse_settings: Optional[XhttpReuseSettings] = Field(None, alias='reuse-settings') + + # proxy part + server: Optional[str] = None + port: Optional[int] = None + tls: Optional[bool] = None + alpn: Optional[List[str]] = None + skip_cert_verify: Optional[bool] = Field(None, alias='skip-cert-verify') + fingerprint: Optional[str] = None + certificate: Optional[str] = None + private_key: Optional[str] = Field(None, alias='private-key') + servername: Optional[str] = None + client_fingerprint: Optional[str] = Field(None, alias='client-fingerprint') + + +class XhttpOpts(BaseModel): + host: Optional[str] = None + path: str = '/' + mode: Literal["auto", "stream-one", "stream-up", "packet-up"] | None = None + headers: Optional[Dict[str, str]] = None + no_grpc_header: Optional[bool] = Field(None, alias='no-grpc-header') + x_padding_bytes: Optional[str] = Field(None, alias='x-padding-bytes') + x_padding_obfs_mode: Optional[bool] = Field(None, alias='x-padding-obfs-mode') + x_padding_key: Optional[str] = Field(None, alias='x-padding-key') + x_padding_header: Optional[str] = Field(None, alias='x-padding-header') + x_padding_placement: Optional[str] = Field(None, alias='x-padding-placement') + x_padding_method: Optional[str] = Field(None, alias='x-padding-method') + uplink_http_method: Optional[str] = Field(None, alias='uplink-http-method') + session_placement: Optional[str] = Field(None, alias='session-placement') + session_key: Optional[str] = Field(None, alias='session-key') + seq_placement: Optional[str] = Field(None, alias='seq-placement') + seq_key: Optional[str] = Field(None, alias='seq-key') + uplink_data_placement: Optional[str] = Field(None, alias='uplink-data-placement') + uplink_data_key: Optional[str] = Field(None, alias='uplink-data-key') + uplink_chunk_size: Optional[str] = Field(None, alias='uplink-chunk-size') + sc_max_each_post_bytes: Optional[str] = Field(None, alias='sc-max-each-post-bytes') + sc_min_posts_interval_ms: Optional[str] = Field(None, alias='sc-min-posts-interval-ms') + reuse_settings: Optional[XhttpReuseSettings] = Field(None, alias='reuse-settings') + download_settings: Optional[XhttpDownloadSettings] = Field(None, alias='download-settings') + + class NetworkMixin(BaseModel): # Transport settings - network: Optional[Literal['tcp', 'http', 'h2', 'grpc', 'ws', 'kcp']] = None + network: Optional[Literal['tcp', 'http', 'h2', 'grpc', 'ws', 'kcp', 'xhttp']] = None http_opts: Optional[HttpOpts] = Field(None, alias='http-opts') h2_opts: Optional[H2Opts] = Field(None, alias='h2-opts') grpc_opts: Optional[GrpcOpts] = Field(None, alias='grpc-opts')