guardia-itsm/main.py
DESKTOP-TKLFCPRython 9e4932640a feat(autonomous): 자율 운영 자동처리 + 승인 게이트 시스템 구현
## 자동처리 엔진 (core/auto_processor.py)
위험도 평가 함수 assess_risk():
  LOW/MEDIUM → 즉시 자동 처리
  HIGH       → 승인 요청 발송 후 대기
  CRITICAL   → 관리자 승인 필수

자동 처리 항목:
  - SR 자동 분류·배정 (키워드/우선순위 추론)
  - INQUIRY SR → KB 검색 후 자동 답변 (신뢰도 75% 이상)
  - SLA 임박(30분) → 자동 에스컬레이션
  - 이상 감지(HIGH+) → 인시던트 자동 생성
  - 완료 SR → KB 아티클 초안 자동 생성

## 자율 운영 API (routers/autonomous.py)
  GET  /api/auto/status          오늘 자동처리 통계
  POST /api/auto/run             사이클 즉시 실행 (ADMIN)
  GET  /api/auto/queue           승인 대기 작업 목록
  POST /api/auto/queue           작업 등록 → 위험도 평가 후 분기
  POST /api/auto/approve/{id}    승인 (HIGH=ENGINEER+, CRITICAL=ADMIN)
  POST /api/auto/reject/{id}     거부
  GET  /api/auto/history         처리 이력

## 스케줄러 (core/scheduler.py)
  5분마다 _auto_processing_cycle() 실행
  - 신규 SR 자동 분류·배정
  - INQUIRY SR KB 자동 답변
  - SLA 에스컬레이션
  - 완료 SR KB 초안 생성

## 봇 명령어 (routers/messenger.py)
  /autoq              승인 대기 큐 조회
  /approve <ID> [의견] 승인
  /reject  <ID> [사유] 거부

## DB 모델 (models.py)
  AutoAction: 자동처리 이력 + 승인 큐
  AutoActionStatus: AUTO_DONE|PENDING_APPROVAL|APPROVED|REJECTED|EXPIRED

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 13:03:41 +09:00

394 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from database import init_db
from fastapi.middleware.cors import CORSMiddleware
from routers import (
approvals, assign, audit, auth, cmdb, dashboard, kb, nlcmd, rating, tasks, work,
institutions, shell_scripts, timetable, attachments, notifications,
messenger, ssh, projects, vibe,
ssl_manager, pm, incidents, oncall, batch,
si_projects, si_wbs, si_requirements, si_issues,
si_risks, si_milestones, si_change_requests, si_tests,
agents,
analytics,
ws as ws_router,
timeline,
code_review,
anomaly,
chatbot,
kb_agent,
orchestrator,
predictive,
change,
problem,
capacity,
catalog,
ldap,
pam,
vuln_scan,
report,
metrics,
finops,
tenant_mgmt,
gateway,
license as license_router,
learning,
push as push_router,
scouter as scouter_router,
deliverables,
si_report,
compliance,
jmeter,
public_checklist,
customer_portal,
onboarding,
groupware,
siem,
topology,
portfolio,
infra_ext,
admin as admin_router,
external_api,
export_import,
dr,
network_devices,
autonomous,
)
@asynccontextmanager
async def lifespan(app: FastAPI):
# 디렉토리 생성
from pathlib import Path
(Path(__file__).parent / "uploads" / "sr_files").mkdir(parents=True, exist_ok=True)
(Path(__file__).parent / "uploads" / "workspaces").mkdir(parents=True, exist_ok=True)
await init_db()
from database import SessionLocal
from core.seed import seed_all
async with SessionLocal() as db:
await seed_all(db)
# 라이선스 상태 확인 (시작 시) — TRIAL 키는 GUARDIA_LICENSE_KEY 없이도 동작
from routers.license import get_license_status
async with SessionLocal() as db:
lic_status = await get_license_status(db)
if lic_status.get("valid"):
edition = lic_status["edition"]
days = lic_status["days_remaining"]
cust = lic_status["customer"]
if lic_status.get("is_trial"):
print(f"[LICENSE] TRIAL 체험판 활성 (D-{days}) - {cust}")
else:
print(f"[LICENSE] {edition} 라이선스 활성 ({days}일 남음) - {cust}")
if lic_status.get("expiry_warning"):
print(f"[LICENSE] 만료 {days}일 남음 - 갱신을 준비하세요.")
elif lic_status.get("expired"):
print("[LICENSE] 라이선스가 만료되었습니다. 갱신이 필요합니다.")
else:
print("[LICENSE] 라이선스 미등록 - /license 에서 무료 체험을 시작하거나 키를 등록하세요.")
# A-1: WebSocket ↔ SSE 통합 패치
from routers.ws import _integrate_with_sse_bus
_integrate_with_sse_bus()
# 백그라운드 스케줄러 시작
from core.scheduler import start_scheduler, init_batch_jobs_from_db
start_scheduler()
await init_batch_jobs_from_db() # DB에서 활성 배치 잡 자동 등록
yield
# 스케줄러 종료
from core.scheduler import stop_scheduler
stop_scheduler()
# F-2: Redis 연결 종료
try:
from core.cache import close_redis
await close_redis()
except Exception:
pass
app = FastAPI(title="GUARDiA ITSM", version="2.0.0", lifespan=lifespan)
@app.middleware("http")
async def add_copyright_header(request, call_next):
response = await call_next(request)
response.headers["X-Powered-By"] = "GUARDiA ITSM 2.0"
response.headers["X-Copyright"] = "Copyright 2026 GUARDiA All Rights Reserved"
return response
# ── F-2: Redis 캐시 종료 훅 ──────────────────────────────────────────────────
# (lifespan의 yield 이후에 실행 — close_redis는 shutdown시 호출)
# ── F-3: Rate Limiting 미들웨어 등록 ─────────────────────────────────────────
from core.ratelimit import setup_rate_limiting
setup_rate_limiting(app)
# ── CORS: 개방망/폐쇄망 자동 전환 ───────────────────────────────────────────
import os as _os
_NETWORK_MODE = _os.environ.get("GUARDIA_NETWORK_MODE", "closed") # closed | open
_ALLOWED_ORIGINS_ENV = _os.environ.get("GUARDIA_ALLOWED_ORIGINS", "")
if _NETWORK_MODE == "open":
# 개방망: 환경변수로 지정된 출처 + 기본 로컬 허용
_extra = [o.strip() for o in _ALLOWED_ORIGINS_ENV.split(",") if o.strip()]
_cors_origins = ["http://localhost:8001", "http://127.0.0.1:8001"] + _extra
_cors_allow_credentials = True
else:
# 폐쇄망 기본값 (localhost only)
_cors_origins = ["http://localhost:8001", "http://127.0.0.1:8001"]
_cors_allow_credentials = False
app.add_middleware(
CORSMiddleware,
allow_origins=_cors_origins,
allow_origin_regex=r"https?://.*" if _NETWORK_MODE == "open" else None,
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
allow_headers=["*"],
allow_credentials=_cors_allow_credentials,
expose_headers=["X-Request-ID", "X-Powered-By"],
max_age=600,
)
app.include_router(auth.router)
app.include_router(dashboard.router)
app.include_router(assign.router)
app.include_router(kb.router)
app.include_router(nlcmd.router)
app.include_router(tasks.router)
app.include_router(approvals.router)
app.include_router(cmdb.router)
app.include_router(audit.router)
app.include_router(work.router)
app.include_router(rating.router)
app.include_router(institutions.router)
app.include_router(shell_scripts.router)
app.include_router(timetable.router)
app.include_router(attachments.router)
app.include_router(notifications.router)
app.include_router(messenger.router)
app.include_router(ssh.router)
app.include_router(projects.router)
app.include_router(vibe.router)
app.include_router(ssl_manager.router)
app.include_router(pm.router)
app.include_router(incidents.router)
app.include_router(oncall.router)
app.include_router(batch.router)
# ── SI 프로젝트 관리 (분석→설계→구현→인도) ─────────────────────────────────
app.include_router(si_projects.router)
app.include_router(si_wbs.router)
app.include_router(si_requirements.router)
app.include_router(si_issues.router)
app.include_router(si_risks.router)
app.include_router(si_milestones.router)
app.include_router(si_change_requests.router)
app.include_router(si_tests.router)
# ── AI 에이전트 (Paperclip × GUARDiA, Ollama 로컬 LLM) ──────────────────────
app.include_router(agents.router)
# ── Analytics (E-2 배포 성공률 트렌드, E-3 엔지니어 워크로드) ─────────────────
app.include_router(analytics.router)
# ── A-1: WebSocket 실시간 대시보드 ───────────────────────────────────────────
app.include_router(ws_router.router)
# ── A-4: 운영 이벤트 타임라인 ────────────────────────────────────────────────
app.include_router(timeline.router)
# ── B-3: 코드 리뷰 에이전트 ──────────────────────────────────────────────────
app.include_router(code_review.router)
# ── B-1: AI 이상 탐지 ────────────────────────────────────────────────────────
app.include_router(anomaly.router)
# ── B-2: 자연어 SR 접수 챗봇 ─────────────────────────────────────────────────
app.include_router(chatbot.router)
# ── B-4: KB 자동 업데이트 에이전트 ───────────────────────────────────────────
app.include_router(kb_agent.router)
# ── B-5: 멀티 에이전트 협업 오케스트레이션 ────────────────────────────────────
app.include_router(orchestrator.router)
# ── B-6: 예측 유지보수 ────────────────────────────────────────────────────────
app.include_router(predictive.router)
# ── C-2: 변경 관리 CAB ───────────────────────────────────────────────────────
app.include_router(change.router)
# ── C-3: Problem Management ─────────────────────────────────────────────────
app.include_router(problem.router)
# ── C-4: 용량 관리 대시보드 ──────────────────────────────────────────────────
app.include_router(capacity.router)
# ── C-5: 서비스 카탈로그 ──────────────────────────────────────────────────────
app.include_router(catalog.router)
# ── D-1: LDAP/AD 연동 ────────────────────────────────────────────────────────
app.include_router(ldap.router)
# ── D-3: 특권 접근 관리 (PAM) ─────────────────────────────────────────────────
app.include_router(pam.router)
# ── D-4: 보안 취약점 자동 스캔 ───────────────────────────────────────────────
app.include_router(vuln_scan.router)
# ── E-1: 월별 리포트 자동 생성 ───────────────────────────────────────────────
app.include_router(report.router)
# ── E-4: Grafana 연동 (Prometheus 메트릭) ─────────────────────────────────
app.include_router(metrics.router)
# ── E-5: FinOps 비용 분석 ────────────────────────────────────────────────
app.include_router(finops.router)
# ── F-1: 멀티테넌트 데이터 격리 ──────────────────────────────────────────
from middleware.tenant import TenantMiddleware
app.add_middleware(TenantMiddleware)
app.include_router(tenant_mgmt.router)
# ── F-5: OpenAPI 외부 연동 게이트웨이 ────────────────────────────────────
app.include_router(gateway.router)
# ── Self-Improving Learning Loop ──────────────────────────────────────────
app.include_router(learning.router)
# ── 라이선스 관리 ──────────────────────────────────────────────────────────
app.include_router(license_router.router)
# ── G-10: PWA Push 알림 ──────────────────────────────────────────────────
app.include_router(push_router.router)
# Scouter APM
app.include_router(scouter_router.router)
# PMS — 산출물 + 보고서
app.include_router(deliverables.router)
app.include_router(si_report.router)
# 준수성 점검 (시큐어코딩/웹접근성/개인정보보호)
app.include_router(compliance.router)
# 성능 테스트 (JMeter JTL 분석 + 내장 부하 테스트)
app.include_router(jmeter.router)
# 공공기관 필수 기능 체크리스트
app.include_router(public_checklist.router)
# 추가 기능
app.include_router(customer_portal.router) # 고객 셀프서비스 포털
app.include_router(onboarding.router) # 온보딩 가이드 챗봇
app.include_router(groupware.router) # 그룹웨어 전자결재 연동
app.include_router(siem.router) # SIEM 보안 이벤트 연동
app.include_router(topology.router) # 네트워크 토폴로지 시각화
app.include_router(portfolio.router) # 포트폴리오 + 리소스 관리
app.include_router(infra_ext.router) # Zero Trust + K8s + ERP
app.include_router(admin_router.router) # GS인증: About + 백업/복구 + 에러코드
app.include_router(external_api.router) # 개방망 외부 API (API Key 인증)
app.include_router(export_import.router) # 폐쇄망 ↔ 개방망 Export/Import
app.include_router(dr.router) # DR 자동화 (Failover/RTO-RPO/백업검증)
app.include_router(network_devices.router) # 네트워크 장비 관리 (스위치/라우터/방화벽)
app.include_router(autonomous.router) # 자율 운영 (자동처리/승인 게이트)
# ── 개방망 보안 헤더 미들웨어 ────────────────────────────────────────────────
@app.middleware("http")
async def add_security_headers(request, call_next):
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
if _os.environ.get("GUARDIA_NETWORK_MODE") == "open":
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
response.headers["Content-Security-Policy"] = (
"default-src 'self'; script-src 'self' 'unsafe-inline'; "
"style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
)
return response
@app.get("/topology")
async def topology_page():
return FileResponse("static/index.html")
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
async def index():
return FileResponse("static/index.html")
@app.get("/customer")
async def customer():
return FileResponse("static/customer.html")
@app.get("/login")
async def login_page():
return FileResponse("static/login.html")
@app.get("/change-password")
async def change_password_page():
return FileResponse("static/change-password.html")
@app.get("/agents")
async def agents_page():
return FileResponse("static/agents.html")
@app.get("/incidents")
async def incidents_page():
return FileResponse("static/incidents.html")
@app.get("/ssl")
async def ssl_page():
return FileResponse("static/ssl.html")
@app.get("/pm")
async def pm_page():
return FileResponse("static/pm.html")
@app.get("/oncall")
async def oncall_page():
return FileResponse("static/oncall.html")
@app.get("/batch")
async def batch_page():
return FileResponse("static/batch.html")
@app.get("/vibe")
async def vibe_page():
return FileResponse("static/vibe.html")
@app.get("/si")
async def si_page():
return FileResponse("static/si.html")
@app.get("/license")
async def license_page():
return FileResponse("static/license.html")