This commit is contained in:
riglen
2026-04-01 16:19:08 +08:00
parent caa3aa49f2
commit 167154481c
5 changed files with 897 additions and 4 deletions

View File

@@ -4,7 +4,8 @@ from fastapi import FastAPI, HTTPException, Query, Request
from fastapi.responses import Response
from app.config import get_settings
from app.models import RuleConfig, SourceConfig
from app.models import RuleConfig, SourceConfig, SourceSnapshot
from app.services.bundle_cache import build_bundle_cache_key, load_bundle_cache, save_bundle_cache
from app.services.loader import load_app_config
from app.services.profiles import build_bundle_profile, build_thin_profile, dump_yaml
from app.services.rules import load_rule_text
@@ -71,6 +72,15 @@ async def _build_quota_headers(source_items: list[tuple[str, SourceConfig]]) ->
return headers
def _quota_headers_from_snapshots(snapshots: list[SourceSnapshot]) -> dict[str, str]:
if not snapshots:
return {}
quota = snapshots[0].quota
if quota and not quota.is_empty():
return {"Subscription-Userinfo": quota.to_header_value()}
return {}
def _yaml_response(content: str, request: Request, headers: dict[str, str] | None = None, filename: str | None = None) -> Response:
final_headers = {
"Content-Type": "text/yaml; charset=utf-8",
@@ -145,12 +155,32 @@ async def client_profile(client_type: str, request: Request, sources: str | None
@app.api_route(PUBLIC_PREFIX + "/bundle/{client_type}.yaml", methods=["GET", "HEAD"])
async def bundle_profile(client_type: str, request: Request, sources: str | None = Query(default=None)) -> Response:
async def bundle_profile(
client_type: str,
request: Request,
sources: str | None = Query(default=None),
force_refresh: bool = Query(default=False),
) -> Response:
client = app_config.clients.get(client_type)
if client is None:
raise HTTPException(status_code=404, detail="client config not found")
source_items = _resolve_sources(sources)
cache_key = build_bundle_cache_key(client_type=client_type, source_names=[name for name, _ in source_items])
if not force_refresh:
cached = load_bundle_cache(
cache_dir=settings.bundle_cache_dir,
cache_key=cache_key,
ttl_seconds=settings.bundle_cache_ttl_seconds,
)
if cached is not None:
headers = {
"profile-update-interval": str(client.provider_interval),
"X-Sub-Provider-Bundle-Cache": "HIT",
}
headers.update(cached.headers)
return _yaml_response(content=cached.content, request=request, headers=headers, filename=f"bundle-{client_type}.yaml")
try:
snapshots = await build_source_snapshots(source_items)
except Exception as exc: # noqa: BLE001
@@ -164,6 +194,15 @@ async def bundle_profile(client_type: str, request: Request, sources: str | None
snapshots=snapshots,
)
)
headers = {"profile-update-interval": str(client.provider_interval)}
headers.update(await _build_quota_headers(source_items))
headers = {
"profile-update-interval": str(client.provider_interval),
"X-Sub-Provider-Bundle-Cache": "BYPASS" if force_refresh else "MISS",
}
headers.update(_quota_headers_from_snapshots(snapshots))
save_bundle_cache(
cache_dir=settings.bundle_cache_dir,
cache_key=cache_key,
content=content,
headers={key: value for key, value in headers.items() if key != "X-Sub-Provider-Bundle-Cache"},
)
return _yaml_response(content, request, headers=headers, filename=f"bundle-{client_type}.yaml")