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 |
)
|