vomebook commited on
Commit
5667bfe
·
verified ·
1 Parent(s): 34f3753

Fix Reader proxy and route state bugs

Browse files
Files changed (2) hide show
  1. app.py +17 -6
  2. static/app.js +4 -4
app.py CHANGED
@@ -153,8 +153,7 @@ async def get_reader_assets() -> dict:
153
  reader_assets_cache_at = now
154
  except Exception as exc:
155
  print(f"reader assets refresh failed: {exc}")
156
- if not reader_assets_cache_at:
157
- reader_assets_cache_at = now
158
  finally:
159
  if response is not None:
160
  response.release()
@@ -1458,11 +1457,12 @@ async def api_reader_content(request: Request, url: str = Query(...)):
1458
  return JSONResponse({"error": str(exc)}, status_code=403)
1459
  forwarded_headers = {
1460
  name: request.headers[name]
1461
- for name in ("Range", "If-Range")
1462
  if name in request.headers
1463
  }
1464
  semaphore = app.state.upstream_semaphore
1465
  await semaphore.acquire()
 
1466
  try:
1467
  upstream = await open_download_response(
1468
  app.state.http_session,
@@ -1471,10 +1471,14 @@ async def api_reader_content(request: Request, url: str = Query(...)):
1471
  method=request.method,
1472
  request_headers=forwarded_headers,
1473
  )
 
 
1474
  except Exception as exc:
1475
- semaphore.release()
1476
  print(f"阅读代理异常: {exc}")
1477
  return JSONResponse({"error": str(exc) or "上游阅读文件加载失败"}, status_code=502)
 
 
 
1478
  headers = {name: upstream.headers[name] for name in READER_UPSTREAM_RESPONSE_HEADERS if name in upstream.headers}
1479
  headers.setdefault("Cache-Control", "public, max-age=300")
1480
  headers["Content-Encoding"] = "identity"
@@ -1499,6 +1503,7 @@ async def api_reader_content(request: Request, url: str = Query(...)):
1499
  finally:
1500
  semaphore.release()
1501
 
 
1502
  return StreamingResponse(stream_reader_content(), status_code=status, headers=headers)
1503
 
1504
 
@@ -1513,16 +1518,21 @@ async def api_download(file: str = Query(...), link: str = Query(...)):
1513
  session = app.state.http_session
1514
  semaphore = app.state.upstream_semaphore
1515
  await semaphore.acquire()
 
1516
  try:
1517
  upstream = await open_download_response(
1518
  session,
1519
  target_url,
1520
  aiohttp.ClientTimeout(total=120, connect=15, sock_read=30),
1521
  )
 
 
1522
  except Exception as e:
1523
- semaphore.release()
1524
  print(f"下载代理异常: {e}")
1525
  return JSONResponse({"error": str(e) or "上游下载失败"}, status_code=502)
 
 
 
1526
  if upstream.status != 200:
1527
  status = upstream.status
1528
  upstream.release()
@@ -1541,6 +1551,7 @@ async def api_download(file: str = Query(...), link: str = Query(...)):
1541
  raise
1542
  finally:
1543
  semaphore.release()
 
1544
  return StreamingResponse(
1545
  stream_response(),
1546
  media_type="application/octet-stream",
@@ -1712,7 +1723,7 @@ async def serve_spa(rest_of_path: str):
1712
  if index_path.exists():
1713
  repo_short = rest_of_path.strip("/").split("/")[0] if rest_of_path.strip("/") else None
1714
  if repo_short and f"VoiceOfML/{repo_short}" not in repo_counts:
1715
- repo_short = None
1716
  cache_key = f"VoiceOfML/{repo_short}" if repo_short else None
1717
  html = injected_html_cache.get(cache_key)
1718
  if html is not None:
 
153
  reader_assets_cache_at = now
154
  except Exception as exc:
155
  print(f"reader assets refresh failed: {exc}")
156
+ reader_assets_cache_at = now
 
157
  finally:
158
  if response is not None:
159
  response.release()
 
1457
  return JSONResponse({"error": str(exc)}, status_code=403)
1458
  forwarded_headers = {
1459
  name: request.headers[name]
1460
+ for name in ("Range", "If-Range", "If-None-Match", "If-Modified-Since")
1461
  if name in request.headers
1462
  }
1463
  semaphore = app.state.upstream_semaphore
1464
  await semaphore.acquire()
1465
+ release_semaphore = True
1466
  try:
1467
  upstream = await open_download_response(
1468
  app.state.http_session,
 
1471
  method=request.method,
1472
  request_headers=forwarded_headers,
1473
  )
1474
+ except asyncio.CancelledError:
1475
+ raise
1476
  except Exception as exc:
 
1477
  print(f"阅读代理异常: {exc}")
1478
  return JSONResponse({"error": str(exc) or "上游阅读文件加载失败"}, status_code=502)
1479
+ finally:
1480
+ if release_semaphore and "upstream" not in locals():
1481
+ semaphore.release()
1482
  headers = {name: upstream.headers[name] for name in READER_UPSTREAM_RESPONSE_HEADERS if name in upstream.headers}
1483
  headers.setdefault("Cache-Control", "public, max-age=300")
1484
  headers["Content-Encoding"] = "identity"
 
1503
  finally:
1504
  semaphore.release()
1505
 
1506
+ release_semaphore = False
1507
  return StreamingResponse(stream_reader_content(), status_code=status, headers=headers)
1508
 
1509
 
 
1518
  session = app.state.http_session
1519
  semaphore = app.state.upstream_semaphore
1520
  await semaphore.acquire()
1521
+ release_semaphore = True
1522
  try:
1523
  upstream = await open_download_response(
1524
  session,
1525
  target_url,
1526
  aiohttp.ClientTimeout(total=120, connect=15, sock_read=30),
1527
  )
1528
+ except asyncio.CancelledError:
1529
+ raise
1530
  except Exception as e:
 
1531
  print(f"下载代理异常: {e}")
1532
  return JSONResponse({"error": str(e) or "上游下载失败"}, status_code=502)
1533
+ finally:
1534
+ if release_semaphore and "upstream" not in locals():
1535
+ semaphore.release()
1536
  if upstream.status != 200:
1537
  status = upstream.status
1538
  upstream.release()
 
1551
  raise
1552
  finally:
1553
  semaphore.release()
1554
+ release_semaphore = False
1555
  return StreamingResponse(
1556
  stream_response(),
1557
  media_type="application/octet-stream",
 
1723
  if index_path.exists():
1724
  repo_short = rest_of_path.strip("/").split("/")[0] if rest_of_path.strip("/") else None
1725
  if repo_short and f"VoiceOfML/{repo_short}" not in repo_counts:
1726
+ return PlainTextResponse("Repository not found", status_code=404)
1727
  cache_key = f"VoiceOfML/{repo_short}" if repo_short else None
1728
  html = injected_html_cache.get(cache_key)
1729
  if html is not None:
static/app.js CHANGED
@@ -1082,6 +1082,10 @@ function syncStateToURL(replace = true) {
1082
  if (STATE.mode !== "global" && STATE.browserPath) {
1083
  params.set("path", STATE.browserPath);
1084
  }
 
 
 
 
1085
  if (DOM.leftSidebar.classList.contains("expanded-wide")) {
1086
  params.set("wide", "1");
1087
  }
@@ -1158,10 +1162,6 @@ function syncURLToState() {
1158
  STATE.filterFolderSelfs = urlSelfs;
1159
  STATE.filterFolderSubtrees = urlSubtrees;
1160
  STATE.filterFolders = mergeFolderFilters(STATE.filterFolderSelfs, STATE.filterFolderSubtrees);
1161
- params.delete("folder_self");
1162
- params.delete("folder_subtree");
1163
- const cleanUrl = params.toString() ? `${window.location.pathname}?${params.toString()}` : window.location.pathname;
1164
- history.replaceState(null, "", cleanUrl);
1165
  } else {
1166
  STATE.filterFolderSelfs = [];
1167
  STATE.filterFolderSubtrees = [];
 
1082
  if (STATE.mode !== "global" && STATE.browserPath) {
1083
  params.set("path", STATE.browserPath);
1084
  }
1085
+ if (STATE.mode !== "global") {
1086
+ STATE.filterFolderSelfs.forEach(folder => params.append("folder_self", folder));
1087
+ STATE.filterFolderSubtrees.forEach(folder => params.append("folder_subtree", folder));
1088
+ }
1089
  if (DOM.leftSidebar.classList.contains("expanded-wide")) {
1090
  params.set("wide", "1");
1091
  }
 
1162
  STATE.filterFolderSelfs = urlSelfs;
1163
  STATE.filterFolderSubtrees = urlSubtrees;
1164
  STATE.filterFolders = mergeFolderFilters(STATE.filterFolderSelfs, STATE.filterFolderSubtrees);
 
 
 
 
1165
  } else {
1166
  STATE.filterFolderSelfs = [];
1167
  STATE.filterFolderSubtrees = [];