fix: pack zip route shadowing (zip before file, 404 -> 403), manual ZernPrologue specify NeoForge 21.1.228 with warning
This commit is contained in:
+26
-26
@@ -1310,6 +1310,32 @@ async def get_pack_manifest(pack_name: str, request: Request, current_user: dict
|
||||
)
|
||||
|
||||
|
||||
@app.get("/pack/{pack_name}/zip")
|
||||
async def download_pack_zip(pack_name: str, request: Request, current_user: dict = Depends(get_current_user)):
|
||||
"""Download whole pack as zip for manual installation (requires pass)"""
|
||||
if not has_permission(current_user["role"], Permissions.DOWNLOAD_PACK):
|
||||
raise HTTPException(403, "Requires active pass")
|
||||
if is_pack_disabled(pack_name):
|
||||
raise HTTPException(403, "Pack is disabled")
|
||||
pack_path = PACKS_DIR / pack_name
|
||||
if not pack_path.exists() or not pack_path.is_dir():
|
||||
raise HTTPException(404, "Pack not found")
|
||||
import tempfile, zipfile
|
||||
tmp = Path(tempfile.gettempdir()) / f"{pack_name}.zip"
|
||||
# re-use if fresh (<5 min)
|
||||
if tmp.exists() and (datetime.utcnow().timestamp() - tmp.stat().st_mtime) < 300:
|
||||
return await send_file_async(tmp, request, content_type="application/zip", cache=False)
|
||||
# create zip
|
||||
def _zip():
|
||||
with zipfile.ZipFile(tmp, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
for f in pack_path.rglob("*"):
|
||||
if f.is_file():
|
||||
zf.write(f, f.relative_to(pack_path))
|
||||
loop = asyncio.get_running_loop()
|
||||
await loop.run_in_executor(None, _zip)
|
||||
return await send_file_async(tmp, request, content_type="application/zip", cache=False)
|
||||
|
||||
|
||||
@app.get("/pack/{pack_name}/file/{file_path:path}")
|
||||
async def get_pack_file(pack_name: str, file_path: str, request: Request, current_user: dict = Depends(get_current_user), preset: Optional[str] = None):
|
||||
"""Get a file from a pack. If preset is specified, try preset dir first, fall back to base."""
|
||||
@@ -1364,32 +1390,6 @@ async def get_pack_file(pack_name: str, file_path: str, request: Request, curren
|
||||
return await send_file_async(full_path, request, cache=True)
|
||||
|
||||
|
||||
@app.get("/pack/{pack_name}/zip")
|
||||
async def download_pack_zip(pack_name: str, request: Request, current_user: dict = Depends(get_current_user)):
|
||||
"""Download whole pack as zip for manual installation (requires pass)"""
|
||||
if not has_permission(current_user["role"], Permissions.DOWNLOAD_PACK):
|
||||
raise HTTPException(403, "Requires active pass")
|
||||
if is_pack_disabled(pack_name):
|
||||
raise HTTPException(403, "Pack is disabled")
|
||||
pack_path = PACKS_DIR / pack_name
|
||||
if not pack_path.exists() or not pack_path.is_dir():
|
||||
raise HTTPException(404, "Pack not found")
|
||||
import tempfile, zipfile
|
||||
tmp = Path(tempfile.gettempdir()) / f"{pack_name}.zip"
|
||||
# re-use if fresh (<5 min)
|
||||
if tmp.exists() and (datetime.utcnow().timestamp() - tmp.stat().st_mtime) < 300:
|
||||
return await send_file_async(tmp, request, content_type="application/zip", cache=False)
|
||||
# create zip
|
||||
def _zip():
|
||||
with zipfile.ZipFile(tmp, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
for f in pack_path.rglob("*"):
|
||||
if f.is_file():
|
||||
zf.write(f, f.relative_to(pack_path))
|
||||
loop = asyncio.get_running_loop()
|
||||
await loop.run_in_executor(None, _zip)
|
||||
return await send_file_async(tmp, request, content_type="application/zip", cache=False)
|
||||
|
||||
|
||||
# ====================== ЭНДПОИНТЫ ДЛЯ ЛАУНЧЕРА ======================
|
||||
|
||||
def get_current_launcher_version() -> str:
|
||||
|
||||
@@ -447,11 +447,12 @@
|
||||
</div>
|
||||
<div>
|
||||
<h4 style="font-size:15px;font-weight:700">Zern Prologue — ручная установка</h4>
|
||||
<p style="font-size:12px;color:var(--text-secondary);margin-top:2px">1.21.1 NeoForge · Create: Aeronautics · без лаунчера</p>
|
||||
<p style="font-size:12px;color:var(--text-secondary);margin-top:2px">1.21.1 NeoForge <strong style="color:var(--text)">21.1.228</strong> · Create: Aeronautics · без лаунчера</p>
|
||||
</div>
|
||||
<span style="margin-left:auto;font-size:11px;color:var(--accent);background:rgba(233,69,96,0.12);padding:4px 8px;border-radius:999px;white-space:nowrap">v4 · 1.8 ГБ</span>
|
||||
</div>
|
||||
<p style="font-size:13px;color:var(--text-secondary);line-height:1.5">Скачай архив модпака и распакуй вручную. Подходит если лаунчер не качает или хочешь поставить на Prism / MultiMC / ATLauncher.</p>
|
||||
<div style="background:rgba(251,191,36,0.08);border:1px solid rgba(251,191,36,0.2);border-radius:8px;padding:8px 10px;font-size:12px;color:#fbbf24;display:flex;gap:8px;align-items:center"><span style="font-size:14px">⚠️</span> <span><strong>Важно:</strong> используй только <strong>NeoForge 21.1.228</strong> для 1.21.1 — на новейшей (21.5+) сборка крашится, мир не загрузится.</span></div>
|
||||
<div style="background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-family:var(--mono);font-size:12px;color:var(--text-secondary)">
|
||||
%USERPROFILE%\.zernmc\instances\ZernPrologue
|
||||
<span style="color:var(--text-muted)"> · ~/.zernmc/instances/ZernPrologue на Linux</span>
|
||||
|
||||
Reference in New Issue
Block a user