minor fixes
This commit is contained in:
+3
-10
@@ -60,8 +60,8 @@ async def list_users(
|
||||
query += " FROM users"
|
||||
|
||||
if search:
|
||||
query += " AND (username LIKE ? OR email LIKE ?)"
|
||||
params.extend([f"%{search}%", f"%{search}%"])
|
||||
query += " AND username LIKE ?"
|
||||
params.append(f"%{search}%")
|
||||
|
||||
query += " ORDER BY role DESC, username"
|
||||
|
||||
@@ -108,19 +108,13 @@ async def get_user_detail(
|
||||
"""Детальная информация о пользователе"""
|
||||
with get_db() as conn:
|
||||
row = conn.execute("""
|
||||
SELECT id, username, email, uuid, role, created_at, last_login, is_active, banned_until
|
||||
SELECT id, username, uuid, role, created_at, last_login, is_active, banned_until
|
||||
FROM users WHERE id = ?
|
||||
""", (user_id,)).fetchone()
|
||||
|
||||
if not row:
|
||||
raise HTTPException(404, "Пользователь не найден")
|
||||
|
||||
# Модераторы не видят email обычных пользователей
|
||||
if current_user["role"] < ROLE_ELDER and row["role"] < ROLE_MODERATOR:
|
||||
email = None
|
||||
else:
|
||||
email = row["email"]
|
||||
|
||||
# Получаем активную проходку
|
||||
pass_info = None
|
||||
if row["role"] >= ROLE_PASS_HOLDER or current_user["role"] >= ROLE_ELDER:
|
||||
@@ -151,7 +145,6 @@ async def get_user_detail(
|
||||
return {
|
||||
"id": row["id"],
|
||||
"username": row["username"],
|
||||
"email": email,
|
||||
"uuid": row["uuid"],
|
||||
"role": row["role"],
|
||||
"role_name": ROLE_NAMES.get(row["role"], "Неизвестно"),
|
||||
|
||||
+2
-2
@@ -135,7 +135,7 @@ async def list_friends(current_user: dict = Depends(get_current_user)):
|
||||
"role": row[2],
|
||||
"online": bool(row[3]),
|
||||
"current_pack": row[4],
|
||||
"last_seen": row[5].isoformat() if row[5] else None
|
||||
"last_seen": row[5] if row[5] else None
|
||||
})
|
||||
|
||||
return {"friends": friends}
|
||||
@@ -155,7 +155,7 @@ async def list_friend_requests(current_user: dict = Depends(get_current_user)):
|
||||
"id": row[0],
|
||||
"username": row[1],
|
||||
"role": row[2],
|
||||
"created_at": row[3].isoformat() if row[3] else None
|
||||
"created_at": row[3] if row[3] else None
|
||||
})
|
||||
return {"requests": requests}
|
||||
|
||||
|
||||
+9
-32
@@ -647,18 +647,12 @@ class CacheControlMiddleware:
|
||||
await self.app(scope, receive, send)
|
||||
return
|
||||
|
||||
# Add caching headers for static files
|
||||
async def send_wrapper(status, headers, *args, **kwargs):
|
||||
# Add cache headers for static files
|
||||
cache_headers = [
|
||||
(b"cache-control", b"public, max-age=86400"), # 24 hours
|
||||
(b"etag", b'"file-etag"'),
|
||||
]
|
||||
cache_headers = [(b"cache-control", b"public, max-age=86400")]
|
||||
headers = list(headers) + cache_headers
|
||||
await send(status, headers, *args, **kwargs)
|
||||
|
||||
# Use original send
|
||||
await self.app(scope, receive, send)
|
||||
await self.app(scope, receive, send_wrapper)
|
||||
|
||||
|
||||
app.add_middleware(CacheControlMiddleware)
|
||||
@@ -962,7 +956,7 @@ async def get_pack_diff(
|
||||
|
||||
|
||||
@app.get("/pack/{pack_name}")
|
||||
async def get_pack_manifest(pack_name: str, request: Request):
|
||||
async def get_pack_manifest(pack_name: str, request: Request, current_user: dict = Depends(get_current_user)):
|
||||
"""Get pack manifest with caching"""
|
||||
client_ip = request.client.host if request.client else "unknown"
|
||||
|
||||
@@ -1009,7 +1003,12 @@ async def get_pack_file(pack_name: str, file_path: str, request: Request):
|
||||
client_ip = request.client.host if request.client else None
|
||||
|
||||
# Security: prevent path traversal
|
||||
if ".." in file_path:
|
||||
try:
|
||||
full_path = full_path.resolve()
|
||||
pack_root = (PACKS_DIR / pack_name).resolve()
|
||||
if not str(full_path).startswith(str(pack_root)):
|
||||
raise HTTPException(403, "Invalid file path")
|
||||
except (ValueError, OSError):
|
||||
raise HTTPException(403, "Invalid file path")
|
||||
|
||||
if not full_path.exists() or not full_path.is_file():
|
||||
@@ -1461,28 +1460,6 @@ async def download_legacy_launcher():
|
||||
raise HTTPException(404, "No legacy launcher files available")
|
||||
|
||||
|
||||
@app.get("/launcher/download/zip/{filename}")
|
||||
async def download_launcher_zip(filename: str):
|
||||
"""Download specific launcher ZIP archive"""
|
||||
if ".." in filename:
|
||||
raise HTTPException(400, "Invalid filename")
|
||||
|
||||
valid_patterns = ["ZernMCLauncher-", "ZernMC-win-"]
|
||||
if not any(filename.startswith(p) for p in valid_patterns) or not filename.endswith(".zip"):
|
||||
raise HTTPException(400, "Invalid filename")
|
||||
|
||||
file_path = BUILDS_DIR / filename
|
||||
|
||||
if not file_path.exists():
|
||||
raise HTTPException(404, "ZIP file not found")
|
||||
|
||||
return FileResponse(
|
||||
path=file_path,
|
||||
filename=filename,
|
||||
media_type="application/zip"
|
||||
)
|
||||
|
||||
|
||||
# ====================== ЛАУНЧЕР МЕТА ЭНДПОИНТЫ ======================
|
||||
|
||||
@app.get("/launcher/meta")
|
||||
|
||||
+12
-12
@@ -5,6 +5,8 @@ from pathlib import Path
|
||||
import json
|
||||
from typing import Optional, Dict
|
||||
import structlog
|
||||
import asyncio
|
||||
import aiofiles
|
||||
|
||||
from models import PackMeta, FileEntry
|
||||
|
||||
@@ -33,9 +35,9 @@ def calculate_sha256_sync(file_path: Path) -> str:
|
||||
return hash_sha.hexdigest()
|
||||
|
||||
async def calculate_sha256(file_path: Path) -> str:
|
||||
"""Calculate SHA256 hash of a file (async wrapper)"""
|
||||
# Используем синхронную версию для простоты
|
||||
return calculate_sha256_sync(file_path)
|
||||
"""Calculate SHA256 hash of a file (async)"""
|
||||
loop = asyncio.get_running_loop()
|
||||
return await loop.run_in_executor(None, calculate_sha256_sync, file_path)
|
||||
|
||||
async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
|
||||
"""Scan pack directory and update manifest if needed"""
|
||||
@@ -51,11 +53,11 @@ async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
|
||||
if not force_rescan and pack_name in _manifest_cache:
|
||||
return _manifest_cache[pack_name]
|
||||
|
||||
# Load existing meta if available (синхронно)
|
||||
# Load existing meta if available
|
||||
if meta_path.exists():
|
||||
try:
|
||||
with open(meta_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
async with aiofiles.open(meta_path, 'r', encoding='utf-8') as f:
|
||||
data = json.loads(await f.read())
|
||||
current_meta = PackMeta.model_validate(data)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load existing meta for pack {pack_name}: {e}")
|
||||
@@ -114,9 +116,8 @@ async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
|
||||
pack_config_path = pack_path / "instance.json"
|
||||
if pack_config_path.exists():
|
||||
try:
|
||||
# Синхронное чтение конфига
|
||||
with open(pack_config_path, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
async with aiofiles.open(pack_config_path, 'r', encoding='utf-8') as f:
|
||||
config = json.loads(await f.read())
|
||||
minecraft_version = config.get("minecraftVersion", minecraft_version)
|
||||
loader_type = config.get("loaderType", loader_type)
|
||||
loader_version = config.get("loaderVersion")
|
||||
@@ -137,9 +138,8 @@ async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
|
||||
asset_index=asset_index
|
||||
)
|
||||
|
||||
# Save to disk (синхронно)
|
||||
with open(meta_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_meta.model_dump_json(indent=2))
|
||||
async with aiofiles.open(meta_path, 'w', encoding='utf-8') as f:
|
||||
await f.write(new_meta.model_dump_json(indent=2))
|
||||
|
||||
# Update cache
|
||||
_manifest_cache[pack_name] = new_meta
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ async def sync_playtime(
|
||||
with get_db() as conn:
|
||||
cursor = conn.execute(
|
||||
"SELECT id, minutes FROM playtime WHERE user_id = ? AND pack_name = ?",
|
||||
(current_user["user_id"], req.pack_name)
|
||||
(current_user["id"], req.pack_name)
|
||||
)
|
||||
existing = cursor.fetchone()
|
||||
if existing:
|
||||
|
||||
Reference in New Issue
Block a user