readme.md 9.9 KB

实际上,这些链接会调用以下参考函数去访问获得对应的 yaml 配置文件:

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 格式:

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

} ]