Update app.py
Browse files
app.py
CHANGED
@@ -31,50 +31,37 @@ class Config:
|
|
31 |
"""配置类"""
|
32 |
ASSET_URL = "https://1pages.nbid.bid/"
|
33 |
PREFIX = "/"
|
34 |
-
JSDELIVR = 0
|
35 |
CACHE_TTL = 3600
|
36 |
MAX_RETRIES = 3
|
37 |
-
TIMEOUT =
|
38 |
-
CHUNK_SIZE = 1024 * 1024 # 10KB 分块大小
|
39 |
-
SIZE_LIMIT = 1024 * 1024 * 1024 * 99 # 999GB 文件大小限制
|
40 |
-
|
41 |
RATE_LIMIT = {
|
42 |
"window_ms": 15 * 60 * 1000, # 15分钟
|
43 |
-
"max":
|
44 |
}
|
45 |
WHITE_LIST: List[str] = [] # 白名单
|
46 |
-
BLACK_LIST: List[str] = [] # 黑名单
|
47 |
-
PASS_LIST: List[str] = [] # 直接通过名单(使用 jsDelivr)
|
48 |
|
49 |
# 请求头
|
50 |
DEFAULT_HEADERS = {
|
51 |
-
"User-Agent": "
|
52 |
-
"Accept": "*/*",
|
53 |
-
"Accept-Encoding": "gzip, deflate, br"
|
54 |
}
|
55 |
|
56 |
# CORS设置
|
57 |
CORS = {
|
58 |
"allow_origins": ["*"],
|
59 |
-
"allow_methods": ["GET", "POST", "OPTIONS"
|
60 |
"allow_headers": ["*"],
|
61 |
"max_age": 1728000
|
62 |
}
|
63 |
|
64 |
-
# URL
|
65 |
PATTERNS = {
|
66 |
-
|
67 |
-
"
|
68 |
-
|
69 |
-
"
|
70 |
-
|
71 |
-
"
|
72 |
-
# Raw内容
|
73 |
-
"raw": r"^(?:https?:\/\/)?raw\.(?:githubusercontent|github)\.com\/(?P<author>.+?)\/(?P<repo>.+?)\/.+?\/.+$",
|
74 |
-
# Gist
|
75 |
-
"gist": r"^(?:https?:\/\/)?gist\.(?:githubusercontent|github)\.com\/(?P<author>.+?)\/.+?\/.+$",
|
76 |
-
# 标签
|
77 |
-
"tags": r"^(?:https?:\/\/)?github\.com\/(?P<author>.+?)\/(?P<repo>.+?)\/tags.*$"
|
78 |
}
|
79 |
|
80 |
class RateLimiter:
|
@@ -276,58 +263,6 @@ def create_interface():
|
|
276 |
path = 'https://' + path
|
277 |
|
278 |
try:
|
279 |
-
# 检查URL格式
|
280 |
-
match = None
|
281 |
-
for pattern in Config.PATTERNS.values():
|
282 |
-
if re.match(pattern, path):
|
283 |
-
match = re.match(pattern, path)
|
284 |
-
break
|
285 |
-
|
286 |
-
if not match:
|
287 |
-
return JSONResponse({"error": "不支持的URL格式"}, status_code=400)
|
288 |
-
|
289 |
-
# 检查白名单和黑名单
|
290 |
-
author, repo = match.group('author', 'repo')
|
291 |
-
if Config.WHITE_LIST and not any(
|
292 |
-
(a == '*' or a == author) and (r == '*' or r == repo)
|
293 |
-
for a, r in [x.split('/') for x in Config.WHITE_LIST]
|
294 |
-
):
|
295 |
-
return JSONResponse({"error": "不在白名单中"}, status_code=403)
|
296 |
-
|
297 |
-
if any(
|
298 |
-
(a == '*' or a == author) and (r == '*' or r == repo)
|
299 |
-
for a, r in [x.split('/') for x in Config.BLACK_LIST]
|
300 |
-
):
|
301 |
-
return JSONResponse({"error": "在黑名单中"}, status_code=403)
|
302 |
-
|
303 |
-
# 处理 jsDelivr 重定向
|
304 |
-
use_jsdelivr = Config.JSDELIVR or any(
|
305 |
-
(a == '*' or a == author) and (r == '*' or r == repo)
|
306 |
-
for a, r in [x.split('/') for x in Config.PASS_LIST]
|
307 |
-
)
|
308 |
-
|
309 |
-
if use_jsdelivr and ('blob' in path or 'raw.githubusercontent.com' in path):
|
310 |
-
# 转换为 jsDelivr URL
|
311 |
-
if 'blob' in path:
|
312 |
-
path = path.replace('/blob/', '@').replace('github.com', 'cdn.jsdelivr.net/gh', 1)
|
313 |
-
else:
|
314 |
-
path = re.sub(r'(\.com/.*?/.+?)/(.+?/)', r'\1@\2', path, 1)
|
315 |
-
path = path.replace('raw.githubusercontent.com', 'cdn.jsdelivr.net/gh', 1)
|
316 |
-
return RedirectResponse(path)
|
317 |
-
|
318 |
-
# 处理 blob 到 raw 的转换
|
319 |
-
if 'blob' in path:
|
320 |
-
path = path.replace('/blob/', '/raw/', 1)
|
321 |
-
|
322 |
-
# 处理 Git 请求
|
323 |
-
if 'git-upload-pack' in path or 'git-receive-pack' in path or 'info/refs' in path:
|
324 |
-
headers = dict(request.headers)
|
325 |
-
headers.update({
|
326 |
-
'User-Agent': 'git/2.41.0',
|
327 |
-
'Accept': 'application/x-git-upload-pack-result, */*',
|
328 |
-
})
|
329 |
-
proxy.session.headers.update(headers)
|
330 |
-
|
331 |
# 获取代理响应
|
332 |
response = proxy.proxy_request(path, request)
|
333 |
|
@@ -342,23 +277,10 @@ def create_interface():
|
|
342 |
if proxy_response.error:
|
343 |
return JSONResponse({"error": proxy_response.error}, status_code=proxy_response.status)
|
344 |
|
345 |
-
# 检查文件大小限制
|
346 |
-
content_length = int(proxy_response.headers.get('content-length', 0))
|
347 |
-
if content_length > Config.SIZE_LIMIT:
|
348 |
-
return RedirectResponse(path)
|
349 |
-
|
350 |
# 返回流式响应
|
351 |
headers = dict(proxy_response.headers)
|
352 |
-
# 设置正确的内容类型
|
353 |
-
if 'git-upload-pack' in path:
|
354 |
-
headers['Content-Type'] = 'application/x-git-upload-pack-result'
|
355 |
-
elif 'git-receive-pack' in path:
|
356 |
-
headers['Content-Type'] = 'application/x-git-receive-pack-result'
|
357 |
-
elif 'info/refs' in path:
|
358 |
-
headers['Content-Type'] = 'application/x-git-upload-pack-advertisement'
|
359 |
-
|
360 |
return StreamingResponse(
|
361 |
-
proxy_response.content.iter_content(chunk_size=
|
362 |
headers=headers,
|
363 |
status_code=proxy_response.status
|
364 |
)
|
|
|
31 |
"""配置类"""
|
32 |
ASSET_URL = "https://1pages.nbid.bid/"
|
33 |
PREFIX = "/"
|
34 |
+
JSDELIVR = 0
|
35 |
CACHE_TTL = 3600
|
36 |
MAX_RETRIES = 3
|
37 |
+
TIMEOUT = 10
|
|
|
|
|
|
|
38 |
RATE_LIMIT = {
|
39 |
"window_ms": 15 * 60 * 1000, # 15分钟
|
40 |
+
"max": 100 # 限制每个IP最多100个请求
|
41 |
}
|
42 |
WHITE_LIST: List[str] = [] # 白名单
|
|
|
|
|
43 |
|
44 |
# 请求头
|
45 |
DEFAULT_HEADERS = {
|
46 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
|
|
|
|
47 |
}
|
48 |
|
49 |
# CORS设置
|
50 |
CORS = {
|
51 |
"allow_origins": ["*"],
|
52 |
+
"allow_methods": ["GET", "POST", "OPTIONS"],
|
53 |
"allow_headers": ["*"],
|
54 |
"max_age": 1728000
|
55 |
}
|
56 |
|
57 |
+
# URL模式
|
58 |
PATTERNS = {
|
59 |
+
"releases": r"^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:releases|archive)\/.*$",
|
60 |
+
"blob": r"^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:blob|raw)\/.*$",
|
61 |
+
"git": r"^(?:https?:\/\/)?github\.com\/.+?\/.+?\/(?:info|git-).*$",
|
62 |
+
"raw": r"^(?:https?:\/\/)?raw\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+?\/.+$",
|
63 |
+
"gist": r"^(?:https?:\/\/)?gist\.(?:githubusercontent|github)\.com\/.+?\/.+?\/.+$",
|
64 |
+
"tags": r"^(?:https?:\/\/)?github\.com\/.+?\/.+?\/tags.*$"
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
}
|
66 |
|
67 |
class RateLimiter:
|
|
|
263 |
path = 'https://' + path
|
264 |
|
265 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
# 获取代理响应
|
267 |
response = proxy.proxy_request(path, request)
|
268 |
|
|
|
277 |
if proxy_response.error:
|
278 |
return JSONResponse({"error": proxy_response.error}, status_code=proxy_response.status)
|
279 |
|
|
|
|
|
|
|
|
|
|
|
280 |
# 返回流式响应
|
281 |
headers = dict(proxy_response.headers)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
282 |
return StreamingResponse(
|
283 |
+
proxy_response.content.iter_content(chunk_size=8192),
|
284 |
headers=headers,
|
285 |
status_code=proxy_response.status
|
286 |
)
|