"""마스터 조직 관리, 활동 기록 및 시스템 관리자 API.""" import json import psutil from fastapi import APIRouter, Depends, HTTPException from common_util.common_util_auth import require_master, require_system_admin, verify_session from common_util.common_util_auth_repository import ( admin_dashboard, admin_list, admin_update_status, decide_join_request, list_company_members, list_join_requests, master_dashboard, remove_member, ) from common_util.common_util_email import send_email_background from common_util.common_util_email_templates import join_result_email from config.config_db import get_db_pool from .A09_Security_Schema import ActivityLogRequest, AdminStatusRequest, JoinDecisionRequest router = APIRouter(prefix="/api/security", tags=["Security"]) @router.get("/master/dashboard") async def get_master_dashboard(session: dict = Depends(require_master)): return {"status": "success", "dashboard": await master_dashboard(session["company_id"])} @router.get("/master/members") async def get_members(session: dict = Depends(require_master)): return {"status": "success", "members": await list_company_members(session["company_id"])} @router.get("/master/join-requests") async def get_join_requests(session: dict = Depends(require_master)): return {"status": "success", "requests": await list_join_requests(session["company_id"])} @router.post("/master/join-requests/{request_id}/decision") async def review_join_request( request_id: int, payload: JoinDecisionRequest, session: dict = Depends(require_master), ): user = await decide_join_request( request_id, session["company_id"], session["user_id"], payload.approved, payload.comment ) if not user: raise HTTPException(status_code=404, detail="처리할 가입 요청이 없습니다.") subject, html = join_result_email(payload.approved) send_email_background(user["email"], subject, html) return {"status": "success"} @router.post("/master/members/{user_id}/remove") async def deactivate_member(user_id: int, session: dict = Depends(require_master)): if not await remove_member(user_id, session["company_id"]): raise HTTPException(status_code=404, detail="내보낼 팀원을 찾을 수 없습니다.") return {"status": "success"} @router.post("/activity", status_code=202) async def write_activity(payload: ActivityLogRequest, session: dict = Depends(verify_session)): pool = get_db_pool() async with pool.acquire() as connection, connection.cursor() as cursor: await cursor.execute( """INSERT INTO activity_logs (user_id, action_type, resource_id, status, details) VALUES (%s, %s, %s, %s, %s)""", ( session["user_id"], payload.action_type, payload.resource_id, payload.status, json.dumps(payload.details), ), ) await connection.commit() return {"status": "success"} @router.get("/admin/dashboard") async def get_admin_dashboard(session: dict = Depends(require_system_admin)): disk = psutil.disk_usage("/") process = psutil.Process() resources = { "cpu_percent": psutil.cpu_percent(interval=None), "memory_percent": psutil.virtual_memory().percent, "disk_total_gb": round(disk.total / 1024**3, 2), "disk_used_gb": round(disk.used / 1024**3, 2), "process_count": len(psutil.pids()), "app_memory_mb": round(process.memory_info().rss / 1024**2, 2), } return {"status": "success", "dashboard": await admin_dashboard(), "resources": resources} @router.get("/admin/{collection}") async def get_admin_collection(collection: str, session: dict = Depends(require_system_admin)): try: rows = await admin_list(collection) except ValueError as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc return {"status": "success", "items": rows} @router.patch("/admin/{entity}/{target_id}/status") async def set_admin_status( entity: str, target_id: int, payload: AdminStatusRequest, session: dict = Depends(require_system_admin), ): changed = await admin_update_status( session["user_id"], entity, target_id, payload.status, {"reason": payload.reason} ) if not changed: raise HTTPException(status_code=400, detail="대상 또는 상태값이 올바르지 않습니다.") return {"status": "success"}