ZHIWEI666 commited on
Commit
378dbc5
·
verified ·
1 Parent(s): b2bb548

Upload 2 files

Browse files
Files changed (2) hide show
  1. github_zip_service.py +163 -155
  2. router_proxy.py +1 -1
github_zip_service.py CHANGED
@@ -1,155 +1,163 @@
1
- """授权后的GitHub ZIP交付服务;认证/实时所有权仍由路由负责。"""
2
- import asyncio
3
- import os
4
- from github_archive import cached_archive
5
- from github_source import github_item_source
6
- from skill_archive_source import source_header, installation_revision, source_key
7
- from proxy_resources import ProxyResources
8
- from workflow_stream_response import OwnedProxyResponse
9
- from zip_stream import zip_response_length, stream_zip_bytes
10
- from owned_thread import run_owned_thread
11
-
12
-
13
- async def prepare_github_zip(item, item_id, *, store, token_selector, resolve_commit,
14
- send, client_factory, transport_factory, error_response,
15
- cache_max_bytes, direct_max_bytes, signing_key=None):
16
- # 按服务端资源解析仓库,普通作者只使用本人绑定凭据或匿名GitHub。
17
- try:
18
- source = github_item_source(item)
19
- repo_url = item.get("link", "")
20
- owner, repo = source["owner"], source["repo"]
21
- active_token = await run_owned_thread(token_selector, item)
22
- skill_revision = installation_revision(item, source_key(source), active_token, signing_key=signing_key) if source['skill'] else None
23
- except ValueError:
24
- return error_response(content={"error": "资源仓库或凭据暂时无法核实"}, status_code=503)
25
-
26
- # ===== ZIP 缓存优先机制(鉴权后、GitHub请求前) =====
27
- cache_resources = None
28
- cache_handed_off = False
29
- try:
30
- versions_db = await run_owned_thread(store.load_data, "versions.json", default_data={})
31
- item_version = versions_db.get(item_id)
32
- archive = cached_archive(item, active_token, item_version, repo_id=store.DATASET_REPO_ID)
33
- hf_cache_token = os.environ.get("HF_TOKEN")
34
- if archive and hf_cache_token:
35
- hf_headers = {"Authorization": f"Bearer {hf_cache_token}"}
36
- cache_filename = archive["path"]
37
- cache_url = f"https://huggingface.co/datasets/{archive['repo_id']}/resolve/{archive['hf_commit']}/{cache_filename}"
38
- cache_client = client_factory(
39
- follow_redirects=False, trust_env=False, transport=transport_factory(),
40
- )
41
- cache_resources = ProxyResources(cache_client)
42
- head_resp = cache_resources.track(await send(
43
- cache_client, "HEAD", cache_url, headers=hf_headers, timeout=10))
44
- if head_resp.status_code == 200:
45
- await cache_resources.close_response(head_resp)
46
- stream_resp = cache_resources.track(await send(
47
- cache_client, "GET", cache_url, headers=hf_headers, timeout=120))
48
- if stream_resp.status_code == 200:
49
- # identity响应应与已上传归档大小一致;编码响应仍保持原始透传。
50
- encoding = (stream_resp.headers.get('content-encoding') or 'identity').strip().lower()
51
- expected_size = archive['size'] if encoding == 'identity' else None
52
- cache_length = zip_response_length(stream_resp.headers, max_bytes=cache_max_bytes,
53
- expected_size=expected_size)
54
- cache_headers = {}
55
- if source["skill"]:
56
- cache_headers["X-Ranking-Skill-Source"] = source_header(source, ())
57
- cache_headers['X-Ranking-Skill-Revision'] = skill_revision
58
- content_length = stream_resp.headers.get('content-length')
59
- if content_length:
60
- cache_headers["Content-Length"] = content_length
61
- content_encoding = stream_resp.headers.get('content-encoding')
62
- if content_encoding:
63
- cache_headers["Content-Encoding"] = content_encoding
64
- if item_version.get('hash'):
65
- cache_headers["X-Installed-Version"] = archive["commit"]
66
-
67
- async def cache_stream_generator():
68
- try:
69
- async for chunk in stream_zip_bytes(stream_resp, max_bytes=cache_max_bytes,
70
- length=cache_length, expected_size=expected_size):
71
- yield chunk
72
- except Exception as error:
73
- print(f"[ZIP缓存] 响应流中断: error={type(error).__name__}")
74
- raise
75
- finally:
76
- await cache_resources.close()
77
-
78
- prepared = OwnedProxyResponse(cache_stream_generator(), close_callback=cache_resources.close,
79
- media_type="application/zip", headers=cache_headers)
80
- cache_handed_off = True
81
- return prepared
82
- print(f"[ZIP缓存] GET失败,回退GitHub: status={stream_resp.status_code}")
83
- except asyncio.CancelledError:
84
- raise
85
- except Exception as error:
86
- print(f"[ZIP缓存] 查询失败,回退GitHub: error={type(error).__name__}")
87
- finally:
88
- if cache_resources is not None and not cache_handed_off:
89
- await cache_resources.close()
90
-
91
- # 先取得完整commit,再按它下载;不在下载后另取“最新版本”冒充实际内容。
92
- live_commit = await resolve_commit(repo_url, active_token)
93
- if not live_commit:
94
- return error_response(content={"error": "资源版本暂时无法核实,请稍后重试"}, status_code=502)
95
- github_zip_api = f"https://api.github.com/repos/{owner}/{repo}/zipball/{live_commit}"
96
-
97
- headers = {
98
- "Accept": "application/vnd.github.v3+json",
99
- "User-Agent": "ComfyUI-Ranking-SaaS"
100
- }
101
- if active_token:
102
- headers["Authorization"] = f"Bearer {active_token}"
103
-
104
- # 异步请求固定GitHub commit,原始块保持长度与Content-Encoding契约。
105
- client = client_factory(
106
- follow_redirects=False, trust_env=False, transport=transport_factory(),
107
- )
108
- resources = ProxyResources(client)
109
- handed_off = False
110
- try:
111
- try:
112
- response = resources.track(await send(
113
- client, "GET", github_zip_api, headers=headers, timeout=120))
114
- except Exception as error:
115
- print(f"[proxy_github_zip] 请求失败: error={type(error).__name__}")
116
- return error_response(content={"error": "代理下载时发生网络异常,请稍后重试"}, status_code=502)
117
- if response.status_code != 200:
118
- status_map = {404: "仓库不存在或无权访问", 401: "Token 无效或已过期", 403: "访问被拒绝(可能触发速率限制)"}
119
- err_msg = status_map.get(response.status_code, f"GitHub API 返回 HTTP {response.status_code}")
120
- return error_response(content={"error": f"GITHUB_DOWNLOAD_FAILED: {err_msg}"}, status_code=502)
121
- declared_length = zip_response_length(response.headers, max_bytes=direct_max_bytes)
122
- resp_headers = {}
123
- if source["skill"]:
124
- resp_headers["X-Ranking-Skill-Source"] = source_header(source, ())
125
- resp_headers['X-Ranking-Skill-Revision'] = skill_revision
126
- content_length = response.headers.get('content-length', '')
127
- if content_length:
128
- resp_headers["Content-Length"] = content_length
129
- content_encoding = response.headers.get('content-encoding')
130
- if content_encoding:
131
- resp_headers["Content-Encoding"] = content_encoding
132
- resp_headers["X-Installed-Version"] = live_commit
133
-
134
- async def stream_generator():
135
- try:
136
- async for chunk in stream_zip_bytes(response, max_bytes=direct_max_bytes, length=declared_length):
137
- yield chunk
138
- except Exception as error:
139
- print(f"[proxy_github_zip] 响应流中断: error={type(error).__name__}")
140
- raise
141
- finally:
142
- await resources.close()
143
-
144
- prepared = OwnedProxyResponse(stream_generator(), close_callback=resources.close,
145
- media_type="application/zip", headers=resp_headers)
146
- handed_off = True
147
- return prepared
148
- except asyncio.CancelledError:
149
- raise
150
- except Exception as error:
151
- print(f"[proxy_github_zip] 响应准备失败: error={type(error).__name__}")
152
- return error_response(content={"error": "代理下载时发生网络异常,请稍后重试"}, status_code=502)
153
- finally:
154
- if not handed_off:
155
- await resources.close()
 
 
 
 
 
 
 
 
 
1
+ """授权后的GitHub ZIP交付服务;认证/实时所有权仍由路由负责。"""
2
+ import asyncio
3
+ import os
4
+ from github_archive import cached_archive
5
+ from github_source import github_item_source, repository_parts
6
+ from skill_archive_source import source_header, installation_revision, source_key
7
+ from proxy_resources import ProxyResources
8
+ from workflow_stream_response import OwnedProxyResponse
9
+ from zip_stream import zip_response_length, stream_zip_bytes
10
+ from owned_thread import run_owned_thread
11
+
12
+
13
+ async def prepare_github_zip(item, item_id, *, expected_repository, store, token_selector, resolve_commit,
14
+ send, client_factory, transport_factory, error_response,
15
+ cache_max_bytes, direct_max_bytes, signing_key=None):
16
+ # 按服务端资源解析仓库,普通作者只使用本人绑定凭据或匿名GitHub。
17
+ try:
18
+ source = github_item_source(item)
19
+ repo_url = item.get("link", "")
20
+ owner, repo = source["owner"], source["repo"]
21
+ try:
22
+ expected_owner, expected_repo = repository_parts(expected_repository)
23
+ except ValueError:
24
+ expected_owner = expected_repo = None
25
+ if (expected_owner is None or expected_owner.casefold() != owner.casefold()
26
+ or expected_repo.casefold() != repo.casefold()):
27
+ return error_response(content={"error": "资源来源已变更,请刷新资源后重新安装。",
28
+ "error_code": "source_changed"}, status_code=409)
29
+ active_token = await run_owned_thread(token_selector, item)
30
+ skill_revision = installation_revision(item, source_key(source), active_token, signing_key=signing_key) if source['skill'] else None
31
+ except ValueError:
32
+ return error_response(content={"error": "资源仓库或凭据暂时无法核实"}, status_code=503)
33
+
34
+ # ===== ZIP 缓存优先机制(鉴权后、GitHub请求前) =====
35
+ cache_resources = None
36
+ cache_handed_off = False
37
+ try:
38
+ versions_db = await run_owned_thread(store.load_data, "versions.json", default_data={})
39
+ item_version = versions_db.get(item_id)
40
+ archive = cached_archive(item, active_token, item_version, repo_id=store.DATASET_REPO_ID)
41
+ hf_cache_token = os.environ.get("HF_TOKEN")
42
+ if archive and hf_cache_token:
43
+ hf_headers = {"Authorization": f"Bearer {hf_cache_token}"}
44
+ cache_filename = archive["path"]
45
+ cache_url = f"https://huggingface.co/datasets/{archive['repo_id']}/resolve/{archive['hf_commit']}/{cache_filename}"
46
+ cache_client = client_factory(
47
+ follow_redirects=False, trust_env=False, transport=transport_factory(),
48
+ )
49
+ cache_resources = ProxyResources(cache_client)
50
+ head_resp = cache_resources.track(await send(
51
+ cache_client, "HEAD", cache_url, headers=hf_headers, timeout=10))
52
+ if head_resp.status_code == 200:
53
+ await cache_resources.close_response(head_resp)
54
+ stream_resp = cache_resources.track(await send(
55
+ cache_client, "GET", cache_url, headers=hf_headers, timeout=120))
56
+ if stream_resp.status_code == 200:
57
+ # identity响应应与已上传归档大小一致;编码响应仍保持原始透传。
58
+ encoding = (stream_resp.headers.get('content-encoding') or 'identity').strip().lower()
59
+ expected_size = archive['size'] if encoding == 'identity' else None
60
+ cache_length = zip_response_length(stream_resp.headers, max_bytes=cache_max_bytes,
61
+ expected_size=expected_size)
62
+ cache_headers = {}
63
+ if source["skill"]:
64
+ cache_headers["X-Ranking-Skill-Source"] = source_header(source, ())
65
+ cache_headers['X-Ranking-Skill-Revision'] = skill_revision
66
+ content_length = stream_resp.headers.get('content-length')
67
+ if content_length:
68
+ cache_headers["Content-Length"] = content_length
69
+ content_encoding = stream_resp.headers.get('content-encoding')
70
+ if content_encoding:
71
+ cache_headers["Content-Encoding"] = content_encoding
72
+ if item_version.get('hash'):
73
+ cache_headers["X-Installed-Version"] = archive["commit"]
74
+
75
+ async def cache_stream_generator():
76
+ try:
77
+ async for chunk in stream_zip_bytes(stream_resp, max_bytes=cache_max_bytes,
78
+ length=cache_length, expected_size=expected_size):
79
+ yield chunk
80
+ except Exception as error:
81
+ print(f"[ZIP缓存] 响应流中断: error={type(error).__name__}")
82
+ raise
83
+ finally:
84
+ await cache_resources.close()
85
+
86
+ prepared = OwnedProxyResponse(cache_stream_generator(), close_callback=cache_resources.close,
87
+ media_type="application/zip", headers=cache_headers)
88
+ cache_handed_off = True
89
+ return prepared
90
+ print(f"[ZIP缓存] GET失败,回退GitHub: status={stream_resp.status_code}")
91
+ except asyncio.CancelledError:
92
+ raise
93
+ except Exception as error:
94
+ print(f"[ZIP缓存] 查询失败,回退GitHub: error={type(error).__name__}")
95
+ finally:
96
+ if cache_resources is not None and not cache_handed_off:
97
+ await cache_resources.close()
98
+
99
+ # 先取得完整commit,再按它下载;不在下载后另取“最新版本”冒充实际内容。
100
+ live_commit = await resolve_commit(repo_url, active_token)
101
+ if not live_commit:
102
+ return error_response(content={"error": "资源版本暂时无法核实,请稍后重试"}, status_code=502)
103
+ github_zip_api = f"https://api.github.com/repos/{owner}/{repo}/zipball/{live_commit}"
104
+
105
+ headers = {
106
+ "Accept": "application/vnd.github.v3+json",
107
+ "User-Agent": "ComfyUI-Ranking-SaaS"
108
+ }
109
+ if active_token:
110
+ headers["Authorization"] = f"Bearer {active_token}"
111
+
112
+ # 异步请求固定GitHub commit,原始块保持长度与Content-Encoding契约。
113
+ client = client_factory(
114
+ follow_redirects=False, trust_env=False, transport=transport_factory(),
115
+ )
116
+ resources = ProxyResources(client)
117
+ handed_off = False
118
+ try:
119
+ try:
120
+ response = resources.track(await send(
121
+ client, "GET", github_zip_api, headers=headers, timeout=120))
122
+ except Exception as error:
123
+ print(f"[proxy_github_zip] 请求失败: error={type(error).__name__}")
124
+ return error_response(content={"error": "代理下载时发生网络异常,请稍后重试"}, status_code=502)
125
+ if response.status_code != 200:
126
+ status_map = {404: "仓库不存在或无权访问", 401: "Token 无效或已过期", 403: "访问被拒绝(可能触发速率限制)"}
127
+ err_msg = status_map.get(response.status_code, f"GitHub API 返回 HTTP {response.status_code}")
128
+ return error_response(content={"error": f"GITHUB_DOWNLOAD_FAILED: {err_msg}"}, status_code=502)
129
+ declared_length = zip_response_length(response.headers, max_bytes=direct_max_bytes)
130
+ resp_headers = {}
131
+ if source["skill"]:
132
+ resp_headers["X-Ranking-Skill-Source"] = source_header(source, ())
133
+ resp_headers['X-Ranking-Skill-Revision'] = skill_revision
134
+ content_length = response.headers.get('content-length', '')
135
+ if content_length:
136
+ resp_headers["Content-Length"] = content_length
137
+ content_encoding = response.headers.get('content-encoding')
138
+ if content_encoding:
139
+ resp_headers["Content-Encoding"] = content_encoding
140
+ resp_headers["X-Installed-Version"] = live_commit
141
+
142
+ async def stream_generator():
143
+ try:
144
+ async for chunk in stream_zip_bytes(response, max_bytes=direct_max_bytes, length=declared_length):
145
+ yield chunk
146
+ except Exception as error:
147
+ print(f"[proxy_github_zip] 响应流中断: error={type(error).__name__}")
148
+ raise
149
+ finally:
150
+ await resources.close()
151
+
152
+ prepared = OwnedProxyResponse(stream_generator(), close_callback=resources.close,
153
+ media_type="application/zip", headers=resp_headers)
154
+ handed_off = True
155
+ return prepared
156
+ except asyncio.CancelledError:
157
+ raise
158
+ except Exception as error:
159
+ print(f"[proxy_github_zip] 响应准备失败: error={type(error).__name__}")
160
+ return error_response(content={"error": "代理下载时发生网络异常,请稍后重试"}, status_code=502)
161
+ finally:
162
+ if not handed_off:
163
+ await resources.close()
router_proxy.py CHANGED
@@ -146,7 +146,7 @@ async def proxy_github_zip(request: Request, req_data: ProxyGithubZipRequest, ac
146
  from 云端_定时版本检测引擎 import fetch_github_commit
147
  return await fetch_github_commit(repo_url, token)
148
 
149
- return await prepare_github_zip(item, req_data.item_id, store=json_db,
150
  signing_key=JWT_SECRET,
151
  token_selector=select_github_token, resolve_commit=resolve_commit,
152
  send=_send_with_validated_redirects, client_factory=httpx.AsyncClient,
 
146
  from 云端_定时版本检测引擎 import fetch_github_commit
147
  return await fetch_github_commit(repo_url, token)
148
 
149
+ return await prepare_github_zip(item, req_data.item_id, expected_repository=req_data.url, store=json_db,
150
  signing_key=JWT_SECRET,
151
  token_selector=select_github_token, resolve_commit=resolve_commit,
152
  send=_send_with_validated_redirects, client_factory=httpx.AsyncClient,