from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession 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.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 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="Sentinel jogosultsĂĄg szĂŒksĂ©ges a mƱvelethez!" ) return current_user # --- đŸ›°ïž 1. SENTINEL: RENDSZERÁLLAPOT ÉS MONITORING --- @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 (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}", 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. 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 sikeresen vĂ©grehajtva."} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) # --- ⚙ 3. DINAMIKUS KONFIGURÁCIÓ (Hierarchical Config) --- @router.get("/parameters", tags=["Dynamic Configuration"]) async def list_all_parameters( db: AsyncSession = Depends(deps.get_db), admin: User = Depends(check_admin_access) ): """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.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() """) 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", "message": f"'{config.key}' frissĂ­tve."} @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."} # --- 🌍 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.""" await TranslationService.export_to_json(db) return {"message": "JSON nyelvi fĂĄjlok frissĂ­tve a fĂĄjlrendszerben."}