Files
Aislo/A09_Security/A09_Security_Router.py

123 lines
4.4 KiB
Python

"""마스터 조직 관리, 활동 기록 및 시스템 관리자 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"}