Cleanup: MB 2.0 Gap Analysis előtti állapot (adatok kizárva)
This commit is contained in:
@@ -1,115 +1,164 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from typing import List, Any, Dict
|
||||
from sqlalchemy import select, func, text, delete
|
||||
from typing import List, Any, Dict, Optional
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from app.api import deps
|
||||
from app.models.identity import User, UserRole
|
||||
from app.models import SystemParameter
|
||||
from app.models.system import SystemParameter
|
||||
from app.models.security import PendingAction, ActionStatus
|
||||
from app.models.history import AuditLog, LogSeverity
|
||||
from app.schemas.admin_security import PendingActionResponse, SecurityStatusResponse
|
||||
|
||||
from app.services.security_service import security_service
|
||||
# Feltételezve, hogy a JSON-alapú TranslationService-ed már készen van
|
||||
from app.services.translation_service import TranslationService
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ConfigUpdate(BaseModel):
|
||||
key: str
|
||||
value: Any
|
||||
scope_level: str = "global"
|
||||
scope_id: Optional[str] = None
|
||||
category: str = "general"
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# --- 🛡️ ADMIN JOGOSULTSÁG ELLENŐRZŐ ---
|
||||
async def check_admin_access(current_user: User = Depends(deps.get_current_active_user)):
|
||||
"""Szigorú hozzáférés-ellenőrzés: Csak Admin vagy Superadmin."""
|
||||
if current_user.role not in [UserRole.admin, UserRole.superadmin]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Admin jogosultság szükséges!"
|
||||
detail="Sentinel jogosultság szükséges a művelethez!"
|
||||
)
|
||||
return current_user
|
||||
|
||||
# --- 1. SENTINEL: NÉGY SZEM ELV (Approval System) ---
|
||||
# --- 🛰️ 1. SENTINEL: RENDSZERÁLLAPOT ÉS MONITORING ---
|
||||
|
||||
@router.get("/pending-actions", response_model=List[PendingActionResponse])
|
||||
@router.get("/health-monitor", tags=["Sentinel Monitoring"])
|
||||
async def get_system_health(
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
admin: User = Depends(check_admin_access)
|
||||
):
|
||||
"""
|
||||
Rendszer pulzusának ellenőrzése (pgAdmin nélkül).
|
||||
Látod a felhasználók eloszlását, az eszközök számát és a kritikus hibákat.
|
||||
"""
|
||||
stats = {}
|
||||
|
||||
# Adatbázis statisztikák (Dynamic counts)
|
||||
user_stats = await db.execute(text("SELECT subscription_plan, count(*) FROM data.users GROUP BY subscription_plan"))
|
||||
stats["user_distribution"] = {row[0]: row[1] for row in user_stats}
|
||||
|
||||
asset_count = await db.execute(text("SELECT count(*) FROM data.assets"))
|
||||
stats["total_assets"] = asset_count.scalar()
|
||||
|
||||
org_count = await db.execute(text("SELECT count(*) FROM data.organizations"))
|
||||
stats["total_organizations"] = org_count.scalar()
|
||||
|
||||
# Biztonsági státusz (Kritikus logok az elmúlt 24 órában)
|
||||
day_ago = datetime.now() - timedelta(days=1)
|
||||
crit_logs = await db.execute(select(func.count(AuditLog.id)).where(
|
||||
AuditLog.severity.in_([LogSeverity.critical, LogSeverity.emergency]),
|
||||
AuditLog.timestamp >= day_ago
|
||||
))
|
||||
stats["critical_alerts_24h"] = crit_logs.scalar() or 0
|
||||
|
||||
return stats
|
||||
|
||||
# --- ⚖️ 2. SENTINEL: NÉGY SZEM ELV (Approval System) ---
|
||||
|
||||
@router.get("/pending-actions", response_model=List[PendingActionResponse], tags=["Sentinel Security"])
|
||||
async def list_pending_actions(
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
admin: User = Depends(check_admin_access)
|
||||
):
|
||||
"""Jóváhagyásra váró kritikus kérések listázása."""
|
||||
"""Jóváhagyásra váró kritikus kérések listázása (pl. törlések, rang-emelések)."""
|
||||
stmt = select(PendingAction).where(PendingAction.status == ActionStatus.pending)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
@router.post("/approve/{action_id}")
|
||||
@router.post("/approve/{action_id}", tags=["Sentinel Security"])
|
||||
async def approve_action(
|
||||
action_id: int,
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
admin: User = Depends(check_admin_access)
|
||||
):
|
||||
"""Művelet véglegesítése (második admin által)."""
|
||||
"""Művelet véglegesítése. Csak egy második admin hagyhatja jóvá az első kérését."""
|
||||
try:
|
||||
await security_service.approve_action(db, admin.id, action_id)
|
||||
return {"status": "success", "message": "Művelet végrehajtva."}
|
||||
return {"status": "success", "message": "Művelet sikeresen végrehajtva."}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
# --- 2. SENTINEL: BIZTONSÁGI ÖSSZEGZÉS ---
|
||||
# --- ⚙️ 3. DINAMIKUS KONFIGURÁCIÓ (Hierarchical Config) ---
|
||||
|
||||
@router.get("/security-status", response_model=SecurityStatusResponse)
|
||||
async def get_security_status(
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
@router.get("/parameters", tags=["Dynamic Configuration"])
|
||||
async def list_all_parameters(
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
admin: User = Depends(check_admin_access)
|
||||
):
|
||||
"""Rendszerállapot: Zárolt júzerek és kritikus események."""
|
||||
day_ago = datetime.now() - timedelta(days=1)
|
||||
|
||||
crit_count = (await db.execute(select(func.count(AuditLog.id)).where(
|
||||
AuditLog.severity.in_([LogSeverity.critical, LogSeverity.emergency]),
|
||||
AuditLog.timestamp >= day_ago
|
||||
))).scalar() or 0
|
||||
|
||||
locked_count = (await db.execute(select(func.count(User.id)).where(
|
||||
User.is_active == False, User.is_deleted == False
|
||||
))).scalar() or 0
|
||||
|
||||
return {
|
||||
"total_pending": (await db.execute(select(func.count(PendingAction.id)).where(PendingAction.status == ActionStatus.pending))).scalar() or 0,
|
||||
"critical_logs_last_24h": crit_count,
|
||||
"emergency_locks_active": locked_count
|
||||
}
|
||||
|
||||
# --- 3. RENDSZERBEÁLLÍTÁSOK (Dynamic Config) ---
|
||||
|
||||
@router.get("/settings")
|
||||
async def get_settings(db: AsyncSession = Depends(deps.get_db), admin: User = Depends(check_admin_access)):
|
||||
"""Minden globális paraméter (Gamification, Limitek stb.) lekérése."""
|
||||
"""Minden globális és lokális paraméter (Limitek, XP szorzók stb.) lekérése."""
|
||||
result = await db.execute(select(SystemParameter))
|
||||
return result.scalars().all()
|
||||
|
||||
@router.put("/settings/{key}")
|
||||
async def update_setting(key: str, value: Any, db: AsyncSession = Depends(deps.get_db), admin: User = Depends(check_admin_access)):
|
||||
"""Paraméter módosítása és Audit Log generálása."""
|
||||
stmt = select(SystemParameter).where(SystemParameter.key == key)
|
||||
param = (await db.execute(stmt)).scalar_one_or_none()
|
||||
if not param:
|
||||
raise HTTPException(status_code=404, detail="Nincs ilyen beállítás.")
|
||||
@router.post("/parameters", tags=["Dynamic Configuration"])
|
||||
async def set_parameter(
|
||||
config: ConfigUpdate, # <--- Most már egy objektumot várunk a Body-ban
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
admin: User = Depends(check_admin_access)
|
||||
):
|
||||
"""
|
||||
Paraméter beállítása. A Swaggerben most már látsz egy JSON ablakot a 'value' számára!
|
||||
"""
|
||||
query = text("""
|
||||
INSERT INTO data.system_parameters (key, value, scope_level, scope_id, category, last_modified_by)
|
||||
VALUES (:key, :val, :sl, :sid, :cat, :user)
|
||||
ON CONFLICT (key, scope_level, scope_id)
|
||||
DO UPDATE SET
|
||||
value = EXCLUDED.value,
|
||||
category = EXCLUDED.category,
|
||||
last_modified_by = EXCLUDED.last_modified_by,
|
||||
updated_at = now()
|
||||
""")
|
||||
|
||||
old_val = param.value
|
||||
param.value = value
|
||||
|
||||
await security_service.log_event(
|
||||
db, admin.id, action="SETTING_CHANGE", severity=LogSeverity.warning,
|
||||
old_data={key: old_val}, new_data={key: value}
|
||||
)
|
||||
await db.execute(query, {
|
||||
"key": config.key,
|
||||
"val": config.value, # Itt bármilyen komplex JSON-t átadhatsz
|
||||
"sl": config.scope_level,
|
||||
"sid": config.scope_id,
|
||||
"cat": config.category,
|
||||
"user": admin.email
|
||||
})
|
||||
await db.commit()
|
||||
return {"status": "success", "key": key, "new_value": value}
|
||||
return {"status": "success", "message": f"'{config.key}' frissítve."}
|
||||
|
||||
# --- 🌍 JSON FORDÍTÁSOK KEZELÉSE ---
|
||||
@router.delete("/parameters/{key}", tags=["Dynamic Configuration"])
|
||||
async def delete_parameter(
|
||||
key: str,
|
||||
scope_level: str = "global",
|
||||
scope_id: Optional[str] = None,
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
admin: User = Depends(check_admin_access)
|
||||
):
|
||||
"""Egy adott konfiguráció törlése (visszaállás az eggyel magasabb szintű alapértelmezésre)."""
|
||||
stmt = delete(SystemParameter).where(
|
||||
SystemParameter.key == key,
|
||||
SystemParameter.scope_level == scope_level,
|
||||
SystemParameter.scope_id == scope_id
|
||||
)
|
||||
await db.execute(stmt)
|
||||
await db.commit()
|
||||
return {"status": "success", "message": "Konfiguráció törölve."}
|
||||
|
||||
@router.post("/translations/sync")
|
||||
# --- 🌍 4. UTILITY: FORDÍTÁSOK ---
|
||||
|
||||
@router.post("/translations/sync", tags=["System Utilities"])
|
||||
async def sync_translations_to_json(
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
admin: User = Depends(check_admin_access)
|
||||
):
|
||||
"""Szinkronizálja az adatbázisban tárolt fordításokat a JSON fájlokba."""
|
||||
# A TranslationService-ben kell megírni a fájlbaíró logikát
|
||||
await TranslationService.export_to_json(db)
|
||||
return {"message": "JSON nyelvi fájlok frissítve."}
|
||||
return {"message": "JSON nyelvi fájlok frissítve a fájlrendszerben."}
|
||||
Reference in New Issue
Block a user