123 lines
5.3 KiB
Python
Executable File
123 lines
5.3 KiB
Python
Executable File
# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/admin.py
|
|
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.audit import SecurityAuditLog, OperationalLog
|
|
from app.models.security import PendingAction, ActionStatus
|
|
from app.services.security_service import security_service
|
|
from app.services.translation_service import TranslationService
|
|
from app.schemas.admin import PointRuleResponse, LevelConfigResponse, ConfigUpdate
|
|
from app.schemas.admin_security import PendingActionResponse, SecurityStatusResponse
|
|
|
|
router = APIRouter()
|
|
|
|
# --- 🛡️ ADMIN JOGOSULTSÁG ELLENŐRZŐ ---
|
|
async def check_admin_access(current_user: User = Depends(deps.get_current_active_user)):
|
|
""" Csak Admin vagy Superadmin léphet be a Sentinel központba. """
|
|
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", response_model=Dict[str, Any], tags=["Sentinel Monitoring"])
|
|
async def get_system_health(
|
|
db: AsyncSession = Depends(deps.get_db),
|
|
admin: User = Depends(check_admin_access)
|
|
):
|
|
""" Részletes rendszerstatisztikák (Felhasználók, Eszközök, Biztonság). """
|
|
stats = {}
|
|
|
|
# Felhasználói eloszlás (Nyers SQL a sebességért)
|
|
user_res = await db.execute(text("SELECT subscription_plan, count(*) FROM identity.users GROUP BY subscription_plan"))
|
|
stats["user_distribution"] = {row[0]: row[1] for row in user_res}
|
|
|
|
# Eszköz és Szervezet számlálók
|
|
stats["total_assets"] = (await db.execute(text("SELECT count(*) FROM vehicle.assets"))).scalar()
|
|
stats["total_organizations"] = (await db.execute(text("SELECT count(*) FROM fleet.organizations"))).scalar()
|
|
|
|
# Biztonsági riasztások (Kritikus logok az elmúlt 24 órában)
|
|
day_ago = datetime.now() - timedelta(days=1)
|
|
crit_logs = await db.execute(
|
|
select(func.count(SecurityAuditLog.id))
|
|
.where(SecurityAuditLog.is_critical == True, SecurityAuditLog.created_at >= 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 műveletek listázása. """
|
|
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 egy második admin által. """
|
|
try:
|
|
await security_service.approve_action(db, admin.id, action_id)
|
|
return {"status": "success", "message": "Művelet végrehajtva."}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
|
|
# --- ⚙️ 3. DINAMIKUS KONFIGURÁCIÓ (System Parameters) ---
|
|
|
|
@router.get("/parameters", tags=["Dynamic Configuration"])
|
|
async def list_all_parameters(
|
|
db: AsyncSession = Depends(deps.get_db),
|
|
admin: User = Depends(check_admin_access)
|
|
):
|
|
""" Globális és lokális paraméterek (Limitek, XP szorzók) 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,
|
|
db: AsyncSession = Depends(deps.get_db),
|
|
admin: User = Depends(check_admin_access)
|
|
):
|
|
""" Paraméter beállítása vagy frissítése hierarchikus scope-al. """
|
|
query = text("""
|
|
INSERT INTO system.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, "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.post("/translations/sync", tags=["System Utilities"])
|
|
async def sync_translations(db: AsyncSession = Depends(deps.get_db), admin: User = Depends(check_admin_access)):
|
|
""" DB fordítások exportálása JSON fájlokba a frontendnek. """
|
|
await TranslationService.export_to_json(db)
|
|
return {"message": "Nyelvi fájlok frissítve."} |