JCEF migration: add jcefmaven dep, make JavaFX optional, create LogBridge, wire JCEFLauncher entry point

This commit is contained in:
SashegDev
2026-07-02 11:49:09 +00:00
parent 0d61ad1107
commit 8eabe232ff
17 changed files with 3017 additions and 133 deletions
+216 -7
View File
@@ -43,6 +43,32 @@ class BanUserRequest(BaseModel):
days: int = Field(..., ge=1, le=365)
reason: str
class CreatePassCodeRequest(BaseModel):
expires_days: Optional[int] = Field(None, ge=1, le=365)
max_uses: int = Field(1, ge=1, le=10)
class UpdatePassRequest(BaseModel):
code: str
expires_days: Optional[int] = None
max_uses: int = Field(1, ge=1, le=10)
is_active: bool = True
class DeletePassRequest(BaseModel):
code: str
class CreatePassRequest(BaseModel):
expires_days: Optional[int] = Field(None, ge=1, le=365)
max_uses: int = Field(1, ge=1, le=10)
class UpdatePassRequest(BaseModel):
code: str
expires_days: Optional[int] = None
max_uses: int = Field(1, ge=1, le=10)
is_active: bool = True
class DeletePassRequest(BaseModel):
code: str
# ====================== ЭНДПОИНТЫ ======================
@router.get("/users")
@@ -532,11 +558,17 @@ async def get_admin_stats(
recent_registrations = conn.execute("""
SELECT COUNT(*) as count FROM users WHERE created_at > ?
""", (week_ago,)).fetchone()["count"]
# Онлайн пользователи
online_now = conn.execute("""
SELECT COUNT(*) as count FROM user_status WHERE is_online = 1
""").fetchone()["count"]
return {
"total_users": total_users,
"active_passes": active_passes,
"banned_users": banned_users,
"online_now": online_now,
"recent_registrations_7d": recent_registrations,
"roles_distribution": [
{"role": r["role"], "role_name": ROLE_NAMES.get(r["role"], "Неизвестно"), "count": r["count"]}
@@ -550,6 +582,85 @@ async def get_admin_stats(
}
# ====================== PASS CRUD ======================
@router.post("/passes/create")
async def admin_create_pass(
body: CreatePassCodeRequest,
current_user: dict = Depends(require_role(ROLE_ELDER)),
request: Request = None,
):
"""Create a new pass code"""
ip = request.client.host if request.client else "unknown"
pass_code = secrets.token_hex(12).upper()
now = time.time()
expires_at = now + (body.expires_days * 86400) if body.expires_days else None
with get_db() as conn:
conn.execute("""
INSERT INTO passes (code, owner, expires_at, max_uses, is_active, uses, activated_at)
VALUES (?, ?, ?, ?, 1, 0, ?)
""", (pass_code, 'admin', expires_at, body.max_uses, now))
conn.commit()
log_audit(current_user["id"], "pass_create",
f"Created pass {pass_code[:8]}... (expires: {body.expires_days}d, max_uses: {body.max_uses})", ip)
return {"success": True, "code": pass_code, "expires_at": expires_at, "max_uses": body.max_uses}
@router.put("/passes/update")
async def admin_update_pass(
body: UpdatePassRequest,
current_user: dict = Depends(require_role(ROLE_ELDER)),
request: Request = None,
):
"""Update an existing pass"""
ip = request.client.host if request.client else "unknown"
with get_db() as conn:
existing = conn.execute("SELECT code FROM passes WHERE code = ?", (body.code,)).fetchone()
if not existing:
raise HTTPException(404, "Pass not found")
expires_at = time.time() + (body.expires_days * 86400) if body.expires_days else None
conn.execute("""
UPDATE passes SET expires_at = ?, max_uses = ?, is_active = ?
WHERE code = ?
""", (expires_at, body.max_uses, 1 if body.is_active else 0, body.code))
conn.commit()
log_audit(current_user["id"], "pass_update",
f"Updated pass {body.code[:8]}... (active: {body.is_active})", ip)
return {"success": True}
@router.post("/passes/delete")
async def admin_delete_pass(
body: DeletePassRequest,
current_user: dict = Depends(require_role(ROLE_ELDER)),
request: Request = None,
):
"""Delete (deactivate) a pass"""
ip = request.client.host if request.client else "unknown"
with get_db() as conn:
existing = conn.execute("SELECT code FROM passes WHERE code = ?", (body.code,)).fetchone()
if not existing:
raise HTTPException(404, "Pass not found")
conn.execute("UPDATE passes SET is_active = 0 WHERE code = ?", (body.code,))
conn.execute("DELETE FROM user_passes WHERE pass_code = ?", (body.code,))
conn.commit()
log_audit(current_user["id"], "pass_delete",
f"Deleted pass {body.code[:8]}...", ip)
return {"success": True}
# ====================== WHITELIST MODS ======================
WHITELIST_DIR = Path(__file__).parent / "whitelist"
@@ -656,15 +767,21 @@ async def remove_whitelist_mod(
@router.get("/clients")
async def admin_list_clients(current_user: dict = Depends(require_role(ROLE_MODERATOR))):
"""List online clients"""
"""List online clients with 5-minute stale timeout"""
with get_db() as conn:
# Auto-clean stale online status (no heartbeat > 5 min)
stale_cutoff = time.time() - 300
conn.execute("UPDATE user_status SET is_online = 0 WHERE last_seen < ?", (stale_cutoff,))
conn.commit()
rows = conn.execute("""
SELECT u.id, u.username, us.is_online, us.current_pack, us.last_seen, s.ip_address
SELECT u.id, u.username, us.is_online, us.current_pack, us.last_seen,
(SELECT s.ip_address FROM user_sessions s
WHERE s.user_id = u.id AND s.is_active = 1
ORDER BY s.created_at DESC LIMIT 1) as ip_address
FROM users u
LEFT JOIN user_status us ON u.id = us.user_id
LEFT JOIN user_sessions s ON u.id = s.user_id AND s.is_active = 1
JOIN user_status us ON u.id = us.user_id
WHERE us.is_online = 1
GROUP BY u.id
ORDER BY us.last_seen DESC
""").fetchall()
clients = []
@@ -672,7 +789,7 @@ async def admin_list_clients(current_user: dict = Depends(require_role(ROLE_MODE
clients.append({
"id": row["id"],
"username": row["username"],
"online": bool(row["is_online"]),
"online": True,
"current_pack": row["current_pack"] or "",
"last_seen": str(row["last_seen"]) if row["last_seen"] else None,
"ip": row["ip_address"] or ""
@@ -811,6 +928,98 @@ async def admin_list_passes(current_user: dict = Depends(require_role(ROLE_MODER
return {"success": True, "passes": passes}
@router.post("/passes/create")
async def create_pass(
body: CreatePassRequest,
current_user: dict = Depends(require_role(ROLE_ELDER)),
request: Request = None
):
ip = request.client.host if request.client else "unknown"
pass_code = secrets.token_hex(12).upper()
now = time.time()
expires_at = now + (body.expires_days * 86400) if body.expires_days else None
with get_db() as conn:
conn.execute("""
INSERT INTO passes (code, owner, expires_at, max_uses, is_active, uses, activated_by, activated_at)
VALUES (?, 'admin', ?, ?, 1, 0, NULL, ?)
""", (pass_code, expires_at, body.max_uses, now))
conn.commit()
log_audit(
current_user["id"],
"pass_create",
f"Created pass code {pass_code} (expires_days: {body.expires_days}, max_uses: {body.max_uses})",
ip
)
return {"success": True, "code": pass_code}
@router.put("/passes/update")
async def update_pass(
body: UpdatePassRequest,
current_user: dict = Depends(require_role(ROLE_ELDER)),
request: Request = None
):
ip = request.client.host if request.client else "unknown"
with get_db() as conn:
existing = conn.execute(
"SELECT code FROM passes WHERE code = ?", (body.code,)
).fetchone()
if not existing:
raise HTTPException(404, "Pass not found")
expires_at = None
if body.expires_days is not None:
expires_at = time.time() + (body.expires_days * 86400)
conn.execute("""
UPDATE passes SET expires_at = ?, max_uses = ?, is_active = ?
WHERE code = ?
""", (expires_at, body.max_uses, 1 if body.is_active else 0, body.code))
conn.commit()
log_audit(
current_user["id"],
"pass_update",
f"Updated pass {body.code} (expires_days: {body.expires_days}, max_uses: {body.max_uses}, is_active: {body.is_active})",
ip
)
return {"success": True}
@router.post("/passes/delete")
async def delete_pass(
body: DeletePassRequest,
current_user: dict = Depends(require_role(ROLE_ELDER)),
request: Request = None
):
ip = request.client.host if request.client else "unknown"
with get_db() as conn:
existing = conn.execute(
"SELECT code FROM passes WHERE code = ?", (body.code,)
).fetchone()
if not existing:
raise HTTPException(404, "Pass not found")
conn.execute("UPDATE passes SET is_active = 0 WHERE code = ?", (body.code,))
conn.execute("DELETE FROM user_passes WHERE pass_code = ?", (body.code,))
conn.commit()
log_audit(
current_user["id"],
"pass_delete",
f"Deactivated pass {body.code}",
ip
)
return {"success": True}
@router.post("/whitelist/mods/{mod_hash}/delete")
async def admin_delete_whitelist_mod_adapter(
mod_hash: str,