实际上,这些链接会调用以下参考函数去访问获得对应的 yaml 配置文件: ```python import httpx import yaml from pathlib import Path from typing import Optional, Dict, Any from fastapi import HTTPException sub_url = 'https://www.yfjc.xyz/api/v1/client/subscribe?token=b74f2207492053926f7511a8e474048f' OUTPUT = Path(__file__).parent.parent.absolute() / "output" def get_sub(sub_url: str, save_path: str) -> Path: """获取订阅文件""" headers = {'User-Agent': 'clash-verge/v1.7.5'} try: resp = httpx.get(sub_url, headers=headers, follow_redirects=True, timeout=10) resp.raise_for_status() except httpx.HTTPError as e: raise HTTPException(status_code=500, detail=f"订阅获取失败: {str(e)}") save_path = Path(save_path) save_path.parent.mkdir(parents=True, exist_ok=True) with open(save_path, 'w', encoding='utf-8') as f: f.write(resp.text) return save_path def update_config( read_path: Path, config_update: dict, save_as: Optional[Path] = None, ) -> Path: """更新配置文件""" config: Dict[str, Any] = {} if read_path.exists(): with open(read_path, 'r', encoding='utf-8') as f: config = yaml.safe_load(f) or {} config.update(config_update) save_as = save_as or read_path save_as.parent.mkdir(parents=True, exist_ok=True) with open(save_as, 'w', encoding='utf-8') as f: yaml.dump( config, f, Dumper=yaml.SafeDumper, allow_unicode=True, indent=2, sort_keys=False ) return save_as ``` 因此, POST /subscriptions 应该是实现 get_sub 、 save_path 的操作。 ``` POST /subscriptions { "urls": [ "http://sub.sub.sub.subsub123456789.com/answer/land?token=a7cbdde987a58068b82e52d57ee5eecd", "https://www.yfjc.xyz/api/v1/client/subscribe?token=b74f2207492053926f7511a8e474048f", "http://subscr.xpoti.com/v3/subscr?id=90bd6ff1da374a89b4ba763f354f20bf" ] } Response: ``` 具体 get_sub 得到的yaml 文件类似如下,当我调用API接口时, GET /subscriptions 应该可以返回所有 yaml 文件的 json 格式: ```yaml mixed-port: 9360 allow-lan: false bind-address: '*' mode: rule log-level: info external-controller: 127.0.0.1:9361 dns: enable: true ipv6: false default-nameserver: [223.5.5.5, 119.29.29.29] enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16 proxies: - { name: '剩余流量:855.2 GB', server: 163.123.192.140, port: 54000, ports: 54000-54060, mport: 54000-54060, udp: true, skip-cert-verify: true, sni: www.bing.com, type: hysteria2, password: 9ecf3874-29dc-482f-a935-91d1d7c6a82a } - { name: 套餐到期:长期有效, server: 163.123.192.140, port: 54000, ports: 54000-54060, mport: 54000-54060, udp: true, skip-cert-verify: true, sni: www.bing.com, type: hysteria2, password: 9ecf3874-29dc-482f-a935-91d1d7c6a82a } - { name: 🇺🇸美国凤凰城专线1, server: 163.123.192.140, port: 54000, ports: 54000-54060, mport: 54000-54060, udp: true, skip-cert-verify: true, sni: www.bing.com, type: hysteria2, password: 9ecf3874-29dc-482f-a935-91d1d7c6a82a } proxy-groups: - { name: 一分机场, type: select, proxies: [自动选择, 故障转移, '剩余流量:855.2 GB', 套餐到期:长期有效, 🇺🇸美国凤凰城专线1, 🇺🇸美国凤凰城专线2, 🇦🇺澳大利亚悉尼, 🇦🇺澳大利亚悉尼2, 🇳🇱荷兰专线, 🇯🇵日本专线, 🇯🇵日本专线3, 🇯🇵日本专线4, 🇸🇬新加坡专线, 🇸🇬新加坡专线2, 🇺🇸美国洛杉矶专线, 🇺🇸美国洛杉矶专线2, 🇺🇸美国洛杉矶专线3, 🇺🇸美国洛杉矶专线4, 🇰🇷韩国三网专线, 🇰🇷韩国三网专线2, 🇰🇷韩国三网专线3, 🇰🇷韩国专线2, 🇧🇷巴西专线, 🇸🇬亚马逊新加坡, 🇮🇳印度孟买专线2, 🇺🇸美国高速01, 🇺🇸美国高速02, '🇳🇱荷兰Eygelshoven | BT下载-0.1倍率', 🇭🇰香港1号, 🇭🇰香港2号, 🇭🇰香港3号, 🇭🇰香港4号, 🇺🇸美国1号-0.1倍, 🇺🇸美国2号-0.1倍, '🇳🇱荷兰Eygelshoven | BT下载-0.1倍', 🇹🇼台湾, 🇫🇷法国巴黎, 🇫🇷法国巴黎2, 🇫🇷法国马赛, 🇳🇱荷兰阿姆斯特丹, 🇩🇪德国法兰克福2, 🇬🇧英国伦敦, 🇬🇧英国伦敦2, 🇮🇳印度海得拉巴, 🇯🇵日本, 🇯🇵日本2] } - { name: 自动选择, type: url-test, proxies: ['剩余流量:855.2 GB', 套餐到期:长期有效, 🇺🇸美国凤凰城专线1, 🇺🇸美国凤凰城专线2, 🇦🇺澳大利亚悉尼, 🇦🇺澳大利亚悉尼2, 🇳🇱荷兰专线, 🇯🇵日本专线, 🇯🇵日本专线3, 🇯🇵日本专线4, 🇸🇬新加坡专线, 🇸🇬新加坡专线2, 🇺🇸美国洛杉矶专线, 🇺🇸美国洛杉矶专线2, 🇺🇸美国洛杉矶专线3, 🇺🇸美国洛杉矶专线4, 🇰🇷韩国三网专线, 🇰🇷韩国三网专线2, 🇰🇷韩国三网专线3, 🇰🇷韩国专线2, 🇧🇷巴西专线, 🇸🇬亚马逊新加坡, 🇮🇳印度孟买专线2, 🇺🇸美国高速01, 🇺🇸美国高速02, '🇳🇱荷兰Eygelshoven | BT下载-0.1倍率', 🇭🇰香港1号, 🇭🇰香港2号, 🇭🇰香港3号, 🇭🇰香港4号, 🇺🇸美国1号-0.1倍, 🇺🇸美国2号-0.1倍, '🇳🇱荷兰Eygelshoven | BT下载-0.1倍', 🇹🇼台湾, 🇫🇷法国巴黎, 🇫🇷法国巴黎2, 🇫🇷法国马赛, 🇳🇱荷兰阿姆斯特丹, 🇩🇪德国法兰克福2, 🇬🇧英国伦敦, 🇬🇧英国伦敦2, 🇮🇳印度海得拉巴, 🇯🇵日本, 🇯🇵日本2], url: 'http://www.gstatic.com/generate_204', interval: 86400 } - { name: 故障转移, type: fallback, proxies: ['剩余流量:855.2 GB', 套餐到期:长期有效, 🇺🇸美国凤凰城专线1, 🇺🇸美国凤凰城专线2, 🇦🇺澳大利亚悉尼, 🇦🇺澳大利亚悉尼2, 🇳🇱荷兰专线, 🇯🇵日本专线, 🇯🇵日本专线3, 🇯🇵日本专线4, 🇸🇬新加坡专线, 🇸🇬新加坡专线2, 🇺🇸美国洛杉矶专线, 🇺🇸美国洛杉矶专线2, 🇺🇸美国洛杉矶专线3, 🇺🇸美国洛杉矶专线4, 🇰🇷韩国三网专线, 🇰🇷韩国三网专线2, 🇰🇷韩国三网专线3, 🇰🇷韩国专线2, 🇧🇷巴西专线, 🇸🇬亚马逊新加坡, 🇮🇳印度孟买专线2, 🇺🇸美国高速01, 🇺🇸美国高速02, '🇳🇱荷兰Eygelshoven | BT下载-0.1倍率', 🇭🇰香港1号, 🇭🇰香港2号, 🇭🇰香港3号, 🇭🇰香港4号, 🇺🇸美国1号-0.1倍, 🇺🇸美国2号-0.1倍, '🇳🇱荷兰Eygelshoven | BT下载-0.1倍', 🇹🇼台湾, 🇫🇷法国巴黎, 🇫🇷法国巴黎2, 🇫🇷法国马赛, 🇳🇱荷兰阿姆斯特丹, 🇩🇪德国法兰克福2, 🇬🇧英国伦敦, 🇬🇧英国伦敦2, 🇮🇳印度海得拉巴, 🇯🇵日本, 🇯🇵日本2], url: 'http://www.gstatic.com/generate_204', interval: 7200 } rules: - 'DOMAIN-SUFFIX,services.googleapis.cn,一分机场' - 'DOMAIN-SUFFIX,xn--ngstr-lra8j.com,一分机场' - 'DOMAIN,safebrowsing.urlsec.qq.com,DIRECT' ``` PUT /subscriptions 应该是实现 update if not exists GET /subscriptions 应该是实现这些链接对应的 yaml 文件的读取。 GET /subscriptions?update=true 应该是再次调用 get_sub 、 save_path ,然后返回新的 yaml 文件 routers\subscriptions.py 中 SUBSCRIPTIONS_FILE = settings.PATH_CONFIG_DIR / "app.yaml" 并未能恰当的保存链接的元数据,每次请求,都会提示链接存在,但是实际 output\subscriptions 目录下的 yaml 并不存在。并且 app.yaml 中也没有任何元数据信息。 我提供判断链接是否存在的方法:首先要从文件层面读取 app.yaml ,得到记录的元数据再判断。或者写成一个接口函数,每次读写都要有一个完整的过程。 app.yaml 存在元数据后,根据元数据的信息,判断 output\subscriptions 目录下的 yaml 是否存在,最终才能确定链接及其配置文件是否保存。最终才能返回 API 。 其次,每次请求多个 POST /subscriptions "urls" 都会因为某个订阅链接失败而导致下一个链接无法保存,这是不被允许的,错误的链接你也应该通过接口告知。成功的链接也需要返回其元数据。即:urls 有3个订阅链接,你都要返回3个的最终信息。 现在Response返回第2个结果是错的,它没有保存到backend\\output\\subscriptions .yaml 文件中 [ { "url": "http://sub.sub.sub.subsub123456789.com/answer/land?token=a7cbdde987a58068b82e52d57ee5eecd", "file_path": "g:\\code\\upwork\\zhang_crawl_bio\\local_proxy_pool\\backend\\output\\subscriptions\\subscription_d92ef962dad83e2caa45b0082db318b6.yaml", "created_at": "2025-01-26T05:08:24.654698", "updated_at": "2025-01-27T04:23:02.069518", "last_check": "2025-01-27T04:23:02.069518", "status": "inactive", "traffic_used": 0, "traffic_total": 0, "group": null, "file_exists": true, "last_error": null, "retry_count": 0, "next_retry": null }, { "url": "https://www.yfjc.xyz/api/v1/client/subscribe?token=b74f2207492053926f7511a8e474048f", "file_path": "g:\\code\\upwork\\zhang_crawl_bio\\local_proxy_pool\\backend\\output\\subscriptions\\string.yaml", "created_at": "2025-01-26T21:47:39.144325", "updated_at": "2025-01-26T21:47:39.144325", "last_check": null, "status": "active", "traffic_used": 0, "traffic_total": 0, "group": null, "file_exists": false, "last_error": null, "retry_count": 0, "next_retry": null }, { "url": "http://subscr.xpoti.com/v3/subscr?id=90bd6ff1da374a89b4ba763f354f20bf", "file_path": "", "created_at": "2025-01-27T05:00:59.463719", "updated_at": "2025-01-27T05:00:59.463719", "last_check": null, "status": "error", "traffic_used": 0, "traffic_total": 0, "group": null, "file_exists": false, "last_error": "", "retry_count": 0, "next_retry": null } ]