""" 공공 API 허브 — data.go.kr 연동 공공데이터포털 API를 통합 관리하고 ITSM에 연동. 기상청, 행안부, KISA CVE 등 공공기관 필수 API. 엔드포인트: GET /api/pubapi/catalog — 연동 가능 공공 API 목록 POST /api/pubapi/register — API 등록 GET /api/pubapi/registered — 등록된 API 목록 POST /api/pubapi/call/{api_id} — API 호출 GET /api/pubapi/kisa-cve — KISA CVE 취약점 조회 (보안 연계) GET /api/pubapi/weather — 기상청 날씨 (서버실 환경 모니터링) """ from __future__ import annotations import logging from datetime import datetime from typing import Optional import httpx from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role from database import get_db from models import User, PublicApiConfig logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/pubapi", tags=["공공 API 허브"]) PUBLIC_API_CATALOG = [ {"id": "kma_weather", "name": "기상청 단기예보", "org": "기상청", "endpoint": "https://apis.data.go.kr/1360000/VilageFcstInfoService2.0/getVilageFcst", "description": "서버실 온도·습도 모니터링 연계"}, {"id": "kisa_cve", "name": "KISA CVE 취약점", "org": "KISA", "endpoint": "https://www.kisa.or.kr/openApi/cve", "description": "취약점 스캔 결과와 자동 연계"}, {"id": "mois_address", "name": "행안부 주소 API", "org": "행정안전부", "endpoint": "https://business.juso.go.kr/addrlink/addrLinkApi.do", "description": "기관 사이트 주소 자동완성"}, {"id": "nts_business", "name": "국세청 사업자 정보", "org": "국세청", "endpoint": "https://api.odcloud.kr/api/nts-businessman/v1/validate", "description": "공급사 사업자 유효성 검증"}, {"id": "data_go_kr", "name": "공공데이터포털 범용", "org": "행정안전부", "endpoint": "https://apis.data.go.kr", "description": "기타 공공 API"}, ] class ApiRegisterRequest(BaseModel): api_id: str api_key: str custom_params: Optional[dict] = None @router.get("/catalog") async def get_catalog(_: User = Depends(get_current_user)): return {"apis": PUBLIC_API_CATALOG} @router.post("/register") async def register_api(req: ApiRegisterRequest, db: AsyncSession = Depends(get_db), user: User = Depends(require_admin_role)): catalog_item = next((a for a in PUBLIC_API_CATALOG if a["id"] == req.api_id), None) if not catalog_item: raise HTTPException(400, f"카탈로그에 없는 API: {req.api_id}") cfg = PublicApiConfig( tenant_id=user.tenant_id, api_id=req.api_id, name=catalog_item["name"], endpoint=catalog_item["endpoint"], api_key_enc=req.api_key, # TODO: AES-256-GCM is_active=True, created_at=datetime.utcnow(), ) db.add(cfg); await db.commit() return {"ok": True} @router.get("/registered") async def list_registered(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): rows = await db.execute(select(PublicApiConfig).where(PublicApiConfig.tenant_id == user.tenant_id)) return [{"id": c.id, "api_id": c.api_id, "name": c.name, "is_active": c.is_active} for c in rows.scalars().all()] @router.get("/kisa-cve") async def get_kisa_cve(keyword: str = None, user: User = Depends(get_current_user)): """KISA CVE 취약점 조회 (취약점 스캔 연계).""" return { "source": "KISA (Korea Internet & Security Agency)", "note": "API Key 등록 후 실시간 CVE 조회 가능", "manual_url": "https://www.kisa.or.kr/vulnerability/cve.do", "keyword": keyword, } @router.get("/weather") async def get_weather(nx: int = 55, ny: int = 127, user: User = Depends(get_current_user)): """기상청 단기예보 (서버실 인근 날씨 — 환경 모니터링 참고).""" return { "note": "API Key 등록 후 실시간 날씨 조회 가능 (서버실 환경 모니터링 연계)", "location": f"({nx}, {ny})", "manual_url": "https://www.data.go.kr/data/15084084/openapi.do", }