site: add server section, highlights carousel, OBT/Season content

- server status cards (online/players/version) via /api/status mcstatus ping
- highlights carousel ported from old site (/highlights/ static media)
- OBT (war in Zarya, open beta event server) + Season (Prologue, Create:
  Aeronautics, year-long) sections
- nav updated, sections renumbered 01-06
This commit is contained in:
SashegDev
2026-08-20 09:15:14 +00:00
parent 1aefca32f2
commit aaecf7e395
20 changed files with 465 additions and 7 deletions
+46
View File
@@ -53,6 +53,9 @@ SITE_DIR = Path(__file__).parent / "site"
# SkinRestorer skins (shared with TG bot)
SKINS_DIR = Path("/root/python/site/skins")
# Minecraft server status (same host the TG bot pings)
MC_STATUS_HOST = os.environ.get("MC_STATUS_HOST", "mc.zernmc.ru")
# Mirror configuration
LAUNCHER_MIRRORS = {
"main": "https://api.zern.cc", # primary
@@ -966,6 +969,11 @@ for sub in ("css", "js", "img"):
mount_dir.mkdir(parents=True, exist_ok=True)
app.mount(f"/{sub}", StaticFiles(directory=mount_dir), name=f"site-{sub}")
# Server highlights media (screenshots / clips for the landing carousel)
HIGHLIGHTS_DIR = SITE_DIR / "highlights"
HIGHLIGHTS_DIR.mkdir(parents=True, exist_ok=True)
app.mount("/highlights", StaticFiles(directory=HIGHLIGHTS_DIR), name="site-highlights")
# Monkey patch to catch invalid HTTP requests
original_data_received = HttpToolsProtocol.data_received
@@ -1054,6 +1062,44 @@ async def get_skin(filename: str, request: Request):
raise HTTPException(status_code=500, detail="Ошибка чтения скина")
@app.get("/api/status")
async def api_server_status():
"""Live Minecraft server status via mcstatus ping (same host as TG bot)"""
try:
from mcstatus import JavaServer
server = JavaServer.lookup(f"{MC_STATUS_HOST}")
status = await server.async_status()
players_list = []
try:
query = await server.async_query()
if query.players.names:
players_list = query.players.names
elif query.players.online > 0 and query.players.sample:
players_list = [p.name for p in query.players.sample]
except Exception:
if status.players.sample:
players_list = [p.name for p in status.players.sample]
version = getattr(status.version, "name", None) or "unknown"
motd = status.description
return {
"online": True,
"players_online": status.players.online,
"players_max": status.players.max,
"players_list": ", ".join(players_list) if players_list else "никого нет",
"players": players_list,
"motd": motd,
"version": version if isinstance(version, str) else str(version),
"updated": datetime.now().strftime("%H:%M:%S")
}
except asyncio.TimeoutError:
return {"online": False, "error": "Таймаут подключения к серверу", "updated": datetime.now().strftime("%H:%M:%S")}
except Exception as e:
return {"online": False, "error": f"{type(e).__name__}: {e}", "updated": datetime.now().strftime("%H:%M:%S")}
@app.get("/health")
async def health():
"""Health check endpoint"""