feat(setup): 폐쇄망 완전 오프라인 설치 지원
[download_packages.sh — 인터넷 서버에서 실행] - OS별 패키지 다운로드 (ubuntu .deb / centos .rpm) - pip wheel 전체 다운로드 (-r requirements.txt) - Tomcat 9 tar.gz, Ollama 바이너리, Chart.js 다운로드 - Ollama 모델(llama3.1:8b, codellama:7b) 압축 패키지 - Docker 이미지 tar (docker_package.sh 연동) - setup/offline/ 에 계층적으로 저장, README.md 자동 생성 [설치 스크립트 오프라인 지원 강화] - setup_ubuntu.sh: OFFLINE_PKG_DIR 환경변수 지원 (.deb 로컬 설치) - setup_centos.sh: OFFLINE_PKG_DIR 환경변수 지원 (.rpm 로컬 설치) - 기존 TOMCAT_MIRROR=file://..., OLLAMA_INSTALL=offline 유지 [Chart.js 오프라인 폴백] - index.html: CDN 실패 시 /static/chart.umd.min.js 로컬 폴백 - db_init.py: 오프라인 패키지에서 chart.umd.min.js 자동 복사 폐쇄망 설치 절차: 인터넷 서버: bash setup/download_packages.sh all USB 복사: tar -czf offline.tar.gz setup/offline/ 폐쇄망 설치: OFFLINE_PKG_DIR=./offline/ubuntu bash setup/setup_ubuntu.sh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
777116b6ce
commit
b31c45db71
@ -5,6 +5,22 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>GUARDiA ITSM</title>
|
<title>GUARDiA ITSM</title>
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
<!-- Chart.js — 대시보드 차트 (CDN 실패 시 로컬 폴백) -->
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var s = document.createElement('script');
|
||||||
|
s.src = 'https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js';
|
||||||
|
s.crossOrigin = 'anonymous';
|
||||||
|
s.onerror = function() {
|
||||||
|
// 폐쇄망 폴백: /static/chart.umd.min.js (오프라인 설치 시 복사됨)
|
||||||
|
var local = document.createElement('script');
|
||||||
|
local.src = '/static/chart.umd.min.js';
|
||||||
|
local.onerror = function() { window._chartjsUnavailable = true; };
|
||||||
|
document.head.appendChild(local);
|
||||||
|
};
|
||||||
|
document.head.appendChild(s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<!-- F-4: PWA -->
|
<!-- F-4: PWA -->
|
||||||
<link rel="manifest" href="/static/manifest.json">
|
<link rel="manifest" href="/static/manifest.json">
|
||||||
<meta name="theme-color" content="#4f8ef7">
|
<meta name="theme-color" content="#4f8ef7">
|
||||||
@ -143,59 +159,163 @@
|
|||||||
|
|
||||||
<!-- Views -->
|
<!-- Views -->
|
||||||
<div id="view-dashboard" class="view active">
|
<div id="view-dashboard" class="view active">
|
||||||
<div class="stats-row" id="stats-row">
|
<!-- KPI 카드 행 -->
|
||||||
<!-- filled by JS -->
|
<div class="stats-row" id="stats-row"><!-- filled by JS --></div>
|
||||||
|
|
||||||
|
<!-- ── 대시보드 탭 ── -->
|
||||||
|
<div class="dash-tab-nav" id="dash-tab-nav">
|
||||||
|
<button class="dash-tab active" data-tab="ops" onclick="switchDashTab('ops')">📊 운영 현황</button>
|
||||||
|
<button class="dash-tab" data-tab="infra" onclick="switchDashTab('infra')">🖥️ 인프라</button>
|
||||||
|
<button class="dash-tab" data-tab="security" onclick="switchDashTab('security')">🔒 보안</button>
|
||||||
|
<button class="dash-tab" data-tab="ai" onclick="switchDashTab('ai')">🤖 AI 인사이트</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-grid">
|
|
||||||
<div class="card" id="recent-list-card">
|
<!-- ═══ 탭 1: 운영 현황 ═══ -->
|
||||||
<div class="card-header">최근 SR</div>
|
<div class="dash-tab-panel active" id="dash-panel-ops">
|
||||||
<div class="card-body" id="recent-list"></div>
|
<div class="chart-grid-2">
|
||||||
</div>
|
<!-- SR 상태 도넛 -->
|
||||||
<div class="card" id="status-chart-card">
|
<div class="card chart-card">
|
||||||
<div class="card-header">상태별 현황</div>
|
<div class="card-header">SR 상태 분포</div>
|
||||||
<div class="card-body" id="status-chart"></div>
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-sr-status"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 엔지니어 워크로드 패널 -->
|
<!-- 우선순위 바 -->
|
||||||
<div class="card" id="workload-card" style="margin-top:18px">
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">우선순위별 SR</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-sr-priority"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 7일 SR 추이 -->
|
||||||
|
<div class="card" style="margin-top:14px">
|
||||||
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
||||||
<span>👷 엔지니어 워크로드</span>
|
<span>📈 최근 7일 SR 추이</span>
|
||||||
<button class="btn btn-secondary" style="font-size:12px;padding:4px 10px"
|
<button class="btn btn-secondary" style="font-size:11px;padding:3px 8px"
|
||||||
onclick="loadWorkload()">새로고침</button>
|
onclick="loadDashboardCharts()">새로고침</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body" style="padding:14px 16px">
|
||||||
|
<canvas id="chart-sr-trend" style="max-height:200px"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 엔지니어 워크로드 + 최근 SR -->
|
||||||
|
<div class="chart-grid-2" style="margin-top:14px">
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">👷 엔지니어 워크로드</div>
|
||||||
<div class="card-body" id="workload-body">
|
<div class="card-body" id="workload-body">
|
||||||
<div style="color:var(--text-muted);font-size:13px;padding:12px 18px">로딩 중…</div>
|
<div style="color:var(--text-muted);font-size:13px;padding:8px">로딩 중…</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 7일 SR 추이 차트 (ADMIN/PM 전용 — JS 제어) -->
|
<div class="card chart-card">
|
||||||
<div id="trend-chart-card" class="card" style="margin-top:18px;display:none">
|
<div class="card-header">최근 SR</div>
|
||||||
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
<div class="card-body" id="recent-list" style="overflow-y:auto;max-height:260px"></div>
|
||||||
<span>📈 최근 7일 SR 추이</span>
|
|
||||||
<span id="trend-last-updated" style="font-size:11px;color:var(--text-muted)"></span>
|
|
||||||
</div>
|
|
||||||
<div class="card-body" style="padding:16px 18px">
|
|
||||||
<div id="trend-chart" class="trend-chart"></div>
|
|
||||||
<div class="trend-legend">
|
|
||||||
<div class="trend-legend-item">
|
|
||||||
<div class="trend-legend-dot" style="background:#818cf8"></div><span>생성</span>
|
|
||||||
</div>
|
|
||||||
<div class="trend-legend-item">
|
|
||||||
<div class="trend-legend-dot" style="background:#34d399"></div><span>완료</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 탭 2: 인프라 ═══ -->
|
||||||
|
<div class="dash-tab-panel" id="dash-panel-infra">
|
||||||
|
<div class="chart-grid-2">
|
||||||
|
<!-- 기관별 SR 수평 바 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">기관별 SR 현황 (Top 10)</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-inst-sr"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<!-- 7일 추이 차트 (ADMIN/PM 전용 — JS가 표시 여부 제어) -->
|
|
||||||
<div id="trend-chart-card" class="card" style="margin-top:16px;display:none">
|
|
||||||
<div class="card-header" style="display:flex;align-items:center;justify-content:space-between">
|
|
||||||
<span>📈 최근 7일 SR 추이</span>
|
|
||||||
<span id="trend-last-updated" style="font-size:11px;color:var(--text-muted)"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body" style="padding:16px">
|
<!-- 서버 OS 분포 -->
|
||||||
<div id="trend-chart" class="trend-chart"></div>
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">서버 OS 분포</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-os-dist"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- CMDB 서버 상태 그리드 -->
|
||||||
|
<div class="card" style="margin-top:14px">
|
||||||
|
<div class="card-header">서버 헬스 현황</div>
|
||||||
|
<div class="card-body" id="server-health-grid"
|
||||||
|
style="display:flex;flex-wrap:wrap;gap:8px;padding:14px 16px">
|
||||||
|
<div style="color:var(--text-muted);font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- SSL 만료 현황 -->
|
||||||
|
<div class="card" style="margin-top:14px">
|
||||||
|
<div class="card-header">🔐 SSL 인증서 만료 현황</div>
|
||||||
|
<div class="card-body chart-body" style="max-height:220px">
|
||||||
|
<canvas id="chart-ssl-expiry"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 탭 3: 보안 ═══ -->
|
||||||
|
<div class="dash-tab-panel" id="dash-panel-security">
|
||||||
|
<div class="chart-grid-2">
|
||||||
|
<!-- 취약점 심각도 바 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">취약점 심각도 분포</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-vuln-severity"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 패치율 도넛 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">패치 적용 현황</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-patch-rate"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- PAM 세션 + 감사 로그 -->
|
||||||
|
<div class="chart-grid-2" style="margin-top:14px">
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">🔑 PAM 세션 현황</div>
|
||||||
|
<div class="card-body" id="pam-status-body"
|
||||||
|
style="padding:14px 16px;font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">📋 최근 감사 로그</div>
|
||||||
|
<div class="card-body" id="audit-body"
|
||||||
|
style="overflow-y:auto;max-height:260px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 탭 4: AI 인사이트 ═══ -->
|
||||||
|
<div class="dash-tab-panel" id="dash-panel-ai">
|
||||||
|
<div class="chart-grid-2">
|
||||||
|
<!-- AI 티켓 분류 분포 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">🤖 AI 티켓 분류 현황</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-ai-category"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 이상 탐지 추이 -->
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">⚡ 이상 탐지 추이 (7일)</div>
|
||||||
|
<div class="card-body chart-body">
|
||||||
|
<canvas id="chart-anomaly-trend"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 예측 유지보수 + 학습 루프 -->
|
||||||
|
<div class="chart-grid-2" style="margin-top:14px">
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">🔧 예측 유지보수 위험 서버</div>
|
||||||
|
<div class="card-body" id="predictive-body"
|
||||||
|
style="padding:14px 16px;font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
<div class="card chart-card">
|
||||||
|
<div class="card-header">📚 학습 루프 현황</div>
|
||||||
|
<div class="card-body" id="learning-body"
|
||||||
|
style="padding:14px 16px;font-size:13px">로딩 중…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- /view-dashboard -->
|
||||||
|
|
||||||
<div id="view-board" class="view">
|
<div id="view-board" class="view">
|
||||||
<div id="kanban-board">
|
<div id="kanban-board">
|
||||||
|
|||||||
@ -112,5 +112,16 @@ async def main():
|
|||||||
print("[OK] DB 초기화 완료")
|
print("[OK] DB 초기화 완료")
|
||||||
|
|
||||||
|
|
||||||
|
def _copy_offline_assets():
|
||||||
|
"""폐쇄망 정적 파일 복사 (있는 경우)."""
|
||||||
|
import shutil
|
||||||
|
offline_chart = ITSM_DIR.parent / "setup" / "offline" / "common" / "chart.umd.min.js"
|
||||||
|
target_chart = ITSM_DIR / "static" / "chart.umd.min.js"
|
||||||
|
if offline_chart.exists() and not target_chart.exists():
|
||||||
|
shutil.copy2(offline_chart, target_chart)
|
||||||
|
print(f"[OK] Chart.js 오프라인 파일 복사: {target_chart}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
_copy_offline_assets()
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user