- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스) - Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포 - 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
204 lines
5.6 KiB
Markdown
204 lines
5.6 KiB
Markdown
---
|
|
name: manager-integration
|
|
description: >
|
|
GUARDiA Manager와 외부 시스템(GUARDiA ITSM, Gitea, Ollama) 연동 코드를 구현하는 스킬.
|
|
API 클라이언트, TypeScript 타입, React 훅을 생성한다.
|
|
트리거: API 연동 구현, axios 클라이언트 작성, GUARDiA API 호출, Gitea 연동,
|
|
Ollama 모델 조회, 데이터 페칭 훅 작성 요청 시.
|
|
---
|
|
|
|
# GUARDiA Manager 연동 구현 스킬
|
|
|
|
## 연동 대상 API 목록
|
|
|
|
### GUARDiA ITSM (http://zioinfo.co.kr:8001)
|
|
|
|
| 엔드포인트 | 용도 | 필요 권한 |
|
|
|-----------|------|---------|
|
|
| `POST /api/auth/login` | 로그인 | 없음 |
|
|
| `GET /api/dashboard` | 대시보드 통계 | 로그인 |
|
|
| `GET /api/tasks` | SR 목록 | 로그인 |
|
|
| `GET /api/incidents` | 인시던트 목록 | 로그인 |
|
|
| `GET /api/cmdb/servers` | 서버 자산 목록 | 로그인 |
|
|
| `GET /api/tenant` | 테넌트 목록 | admin |
|
|
| `GET /api/external/keys` | API Key 목록 | admin |
|
|
| `GET /api/audit` | 감사 로그 | admin |
|
|
| `GET /api/metrics` | Prometheus 메트릭 | 로그인 |
|
|
|
|
### Gitea (http://zioinfo.co.kr:3000/api/v1)
|
|
|
|
| 엔드포인트 | 용도 |
|
|
|-----------|------|
|
|
| `GET /repos/search` | 저장소 목록 |
|
|
| `GET /repos/{user}/{repo}/commits` | 최신 커밋 |
|
|
| `GET /repos/{user}/{repo}/hooks` | 웹훅 목록 |
|
|
|
|
### Ollama (http://localhost:11434)
|
|
|
|
| 엔드포인트 | 용도 |
|
|
|-----------|------|
|
|
| `GET /api/tags` | 설치된 모델 목록 |
|
|
| `GET /api/ps` | 로드된 모델 |
|
|
|
|
---
|
|
|
|
## API 클라이언트 구현
|
|
|
|
### guardiaClient.ts
|
|
|
|
```typescript
|
|
// frontend/src/api/guardiaClient.ts
|
|
import axios from 'axios';
|
|
|
|
const BASE = import.meta.env.VITE_GUARDIA_API ?? 'http://zioinfo.co.kr:8001';
|
|
|
|
export const guardiaApi = axios.create({ baseURL: BASE });
|
|
|
|
// 요청 인터셉터: JWT 자동 주입
|
|
guardiaApi.interceptors.request.use((config) => {
|
|
const token = sessionStorage.getItem('guardia_token');
|
|
if (token) config.headers.Authorization = `Bearer ${token}`;
|
|
return config;
|
|
});
|
|
|
|
// 응답 인터셉터: 401 → 로그인 리다이렉트
|
|
guardiaApi.interceptors.response.use(
|
|
res => res,
|
|
err => {
|
|
if (err.response?.status === 401) {
|
|
sessionStorage.removeItem('guardia_token');
|
|
window.location.href = '/login';
|
|
}
|
|
return Promise.reject(err);
|
|
}
|
|
);
|
|
```
|
|
|
|
### types.ts — 핵심 타입 정의
|
|
|
|
```typescript
|
|
// frontend/src/api/types.ts
|
|
|
|
export interface User {
|
|
id: number;
|
|
username: string;
|
|
display_name: string;
|
|
role: 'admin' | 'pm' | 'engineer' | 'customer';
|
|
is_active: boolean;
|
|
}
|
|
|
|
export interface Server {
|
|
id: number;
|
|
hostname: string;
|
|
ip_addr: string; // ServerOut 스키마에서 제외됨 — 실제 응답에 없을 수 있음
|
|
os_type: string;
|
|
status: 'running' | 'stopped' | 'error' | 'unknown';
|
|
inst_id?: number;
|
|
}
|
|
|
|
export interface SRRequest {
|
|
id: number;
|
|
sr_id: string;
|
|
title: string;
|
|
status: string;
|
|
priority: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
|
|
requested_by: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface DashboardStats {
|
|
total_sr: number;
|
|
open_sr: number;
|
|
critical_incidents: number;
|
|
sla_achievement: number;
|
|
server_count: number;
|
|
running_servers: number;
|
|
}
|
|
|
|
export interface APIKey {
|
|
id: number;
|
|
name: string;
|
|
scopes: string;
|
|
is_active: boolean;
|
|
use_count: number;
|
|
last_used_at: string | null;
|
|
expires_at: string | null;
|
|
}
|
|
|
|
export interface AuditLog {
|
|
id: number;
|
|
action: string;
|
|
entity_type: string;
|
|
entity_id: string;
|
|
username: string;
|
|
ip_hash: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface SystemResources {
|
|
cpu_percent: number;
|
|
memory: { total_gb: number; used_gb: number; percent: number };
|
|
disk: { total_gb: number; used_gb: number; percent: number };
|
|
}
|
|
|
|
export interface ServiceStatus {
|
|
[serviceName: string]: 'active' | 'inactive' | 'failed' | 'unknown';
|
|
}
|
|
```
|
|
|
|
### React 훅 — useGuardiaApi.ts
|
|
|
|
```typescript
|
|
// frontend/src/hooks/useGuardiaApi.ts
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { guardiaApi } from '../api/guardiaClient';
|
|
|
|
export function useGuardiaApi<T>(
|
|
url: string,
|
|
options: { immediate?: boolean; deps?: unknown[] } = {}
|
|
) {
|
|
const { immediate = true, deps = [] } = options;
|
|
const [data, setData] = useState<T | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const fetch = useCallback(async () => {
|
|
setLoading(true); setError(null);
|
|
try {
|
|
const res = await guardiaApi.get<T>(url);
|
|
setData(res.data);
|
|
} catch (e: any) {
|
|
setError(e.response?.data?.detail ?? '오류가 발생했습니다.');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [url]);
|
|
|
|
useEffect(() => { if (immediate) fetch(); }, [fetch, immediate, ...deps]);
|
|
|
|
return { data, loading, error, refetch: fetch };
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 환경변수 설정
|
|
|
|
```typescript
|
|
// frontend/src/config/env.ts
|
|
export const ENV = {
|
|
GUARDIA_API: import.meta.env.VITE_GUARDIA_API ?? 'http://zioinfo.co.kr:8001',
|
|
MANAGER_API: import.meta.env.VITE_MANAGER_API ?? 'http://localhost:8002',
|
|
GITEA_API: import.meta.env.VITE_GITEA_API ?? 'http://zioinfo.co.kr:3000/api/v1',
|
|
GITEA_USER: import.meta.env.VITE_GITEA_USER ?? 'zio',
|
|
} as const;
|
|
```
|
|
|
|
---
|
|
|
|
## 주의사항
|
|
|
|
- **ServerOut 스키마**: GUARDiA ITSM은 보안 정책으로 `ip_addr`, `ssh_user`, `os_pw_enc` 필드를 API 응답에서 제외한다. 이 필드를 프론트엔드에 표시하려면 ITSM 쪽에 별도 admin 엔드포인트가 필요하다.
|
|
- **인증 토큰 저장**: `localStorage` 금지. `sessionStorage` 또는 메모리 변수 사용.
|
|
- **Gitea CORS**: Gitea API를 직접 호출하면 CORS 오류 발생 가능. Manager Backend의 프록시 라우터(`/api/proxy/gitea`) 를 거쳐 호출한다.
|