[온보딩 API (routers/onboarding.py)] - 8단계 온보딩 플로우: 0. 환영 → 1. 비밀번호변경 → 2. 대시보드 → 3. 프로젝트등록 → 4. 서버등록 → 5. 소스코드등록 → 6. 메신저봇 → 7. 완료 - POST /api/onboarding/message: 현재화면 + 사용자질문 → Ollama 답변 - 화면별 스포트라이트 target 정의 (CSS selector) - 사용자별 단계 상태 영속 관리 [온보딩 챗봇 UI (static/onboarding.js)] - 우측 고정 패널 (360px, 모바일 하단 슬라이드) - 타이핑 애니메이션 효과 + 마크다운 렌더링 - 스포트라이트: 현재 단계 UI 요소를 하이라이트 - 화면 변화 감지 (MutationObserver + click 이벤트) - 최소화/닫기/재시작 제어 - 사용자 질문 입력 → Ollama 실시간 답변 - 온보딩 완료 후 우측하단 ? 도움말 버튼 - 액션버튼: next/navigate/external/complete/skip [설치 자동화 연동] - install_auto.sh: 설치 완료 후 onboarding reset API 호출 - 브라우저 열릴 때 챗봇 자동 표시 사용자 경험: 설치 완료 → 브라우저 자동 오픈 → 챗봇 우측 등장 → "환영합니다!" → 비밀번호변경 화면 이동 안내 → CMDB 서버등록 스포트라이트 → Gitea 소스등록 → 완료 후 ? 버튼으로 재시작 가능 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
337 lines
13 KiB
Bash
337 lines
13 KiB
Bash
#!/bin/bash
|
|
# =============================================================
|
|
# GUARDiA ITSM 통합 자동 설치 스크립트 (Linux)
|
|
# =============================================================
|
|
# Claude Code Desktop에서 자동 호출됨
|
|
# 직접 실행: sudo bash install_auto.sh [OS유형] [옵션]
|
|
#
|
|
# OS유형: ubuntu | centos | rhel | auto(기본)
|
|
# 옵션:
|
|
# --license trial30 : 30일 체험판 (기본)
|
|
# --license trial7 : 7일 체험판
|
|
# --license <key> : 정식 라이선스 키
|
|
# --test : 설치 검증만
|
|
# --offline : 오프라인 모드 (setup/offline/ 필요)
|
|
# --db sqlite : SQLite 사용 (기본)
|
|
# --db postgres : PostgreSQL 사용
|
|
# --skip-ollama : Ollama 생략
|
|
# --skip-gitea : Gitea 생략
|
|
# =============================================================
|
|
|
|
set -euo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
LOG_FILE="/var/log/guardia_auto_install.log"
|
|
START_TIME=$(date +%s)
|
|
|
|
# 인수 파싱
|
|
OS_TYPE="${1:-auto}"
|
|
LICENSE_TYPE="trial30"
|
|
TEST_MODE=false
|
|
OFFLINE=false
|
|
DB_TYPE="sqlite"
|
|
INSTALL_OLLAMA=true
|
|
INSTALL_GITEA=true
|
|
|
|
shift 1 2>/dev/null || true
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--license) LICENSE_TYPE="$2"; shift 2 ;;
|
|
--test) TEST_MODE=true; shift ;;
|
|
--offline) OFFLINE=true; shift ;;
|
|
--db) DB_TYPE="$2"; shift 2 ;;
|
|
--skip-ollama) INSTALL_OLLAMA=false; shift ;;
|
|
--skip-gitea) INSTALL_GITEA=false; shift ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
ok() { echo -e "${GREEN}[OK]${NC} $*" | tee -a "$LOG_FILE"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*" | tee -a "$LOG_FILE"; }
|
|
fail() { echo -e "${RED}[FAIL]${NC} $*" | tee -a "$LOG_FILE"; exit 1; }
|
|
info() { echo -e "${CYAN}[INFO]${NC} $*" | tee -a "$LOG_FILE"; }
|
|
step() { echo -e "\n${CYAN}━━━ $* ━━━${NC}" | tee -a "$LOG_FILE"; }
|
|
|
|
# ── OS 자동 감지 ───────────────────────────────────────────────
|
|
detect_os() {
|
|
if [[ "$OS_TYPE" != "auto" ]]; then return; fi
|
|
|
|
if [[ -f /etc/os-release ]]; then
|
|
source /etc/os-release
|
|
case "${ID:-}" in
|
|
ubuntu|debian) OS_TYPE="ubuntu" ;;
|
|
centos|stream) OS_TYPE="centos" ;;
|
|
rhel|rocky|almalinux) OS_TYPE="rhel" ;;
|
|
*)
|
|
case "${ID_LIKE:-}" in
|
|
*debian*) OS_TYPE="ubuntu" ;;
|
|
*rhel*|*fedora*) OS_TYPE="rhel" ;;
|
|
*) fail "지원하지 않는 OS: $ID" ;;
|
|
esac ;;
|
|
esac
|
|
OS_VERSION="${VERSION_ID%%.*}"
|
|
info "OS 감지: $PRETTY_NAME → $OS_TYPE $OS_VERSION"
|
|
elif [[ "$(uname)" == "Darwin" ]]; then
|
|
OS_TYPE="mac"
|
|
info "OS 감지: macOS"
|
|
else
|
|
fail "OS를 감지할 수 없습니다."
|
|
fi
|
|
}
|
|
|
|
# ── root 확인 ──────────────────────────────────────────────────
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
echo ""
|
|
echo "관리자 권한이 필요합니다."
|
|
echo "다음 명령으로 재실행하세요:"
|
|
echo " sudo bash $0 $*"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# ── 설치 검증 모드 ─────────────────────────────────────────────
|
|
run_tests() {
|
|
echo "=== GUARDiA ITSM 설치 검증 ===" | tee -a "$LOG_FILE"
|
|
PASS=0; FAIL=0
|
|
|
|
chk() {
|
|
local desc="$1"; shift
|
|
if "$@" &>/dev/null 2>&1; then
|
|
ok "$desc"; ((PASS++))
|
|
else
|
|
echo -e "${RED}[FAIL]${NC} $desc"; ((FAIL++))
|
|
fi
|
|
}
|
|
|
|
chk "Python 3.11+" python3.11 --version
|
|
chk "Java 17 (OpenJDK)" java -version
|
|
chk "Tomcat 9 서비스" systemctl is-active tomcat9
|
|
chk "GUARDiA 서비스" systemctl is-active guardia-itsm
|
|
chk "GUARDiA HTTP" bash -c 'curl -sf http://localhost:8001/ -o /dev/null'
|
|
chk "GUARDiA 로그인 API" bash -c 'curl -sf -X POST http://localhost:8001/api/auth/login \
|
|
-H "Content-Type: application/json" -d "{\"username\":\"admin\",\"password\":\"1111\"}" -o /dev/null'
|
|
[[ "$INSTALL_OLLAMA" == "true" ]] && chk "Ollama API" bash -c 'curl -sf http://localhost:11434/api/version -o /dev/null'
|
|
[[ "$INSTALL_GITEA" == "true" ]] && chk "Gitea HTTP" bash -c 'curl -sf http://localhost:3000/api/v1/version -o /dev/null'
|
|
chk "라이선스 상태" bash -c 'curl -sf http://localhost:8001/api/license/status -o /dev/null'
|
|
|
|
echo ""
|
|
echo "검증 결과: 성공 $PASS / 실패 $FAIL"
|
|
[[ $FAIL -eq 0 ]] && ok "모든 검증 통과!" || warn "$FAIL개 확인 필요"
|
|
return $FAIL
|
|
}
|
|
|
|
# ── 라이선스 발급 ─────────────────────────────────────────────
|
|
issue_license() {
|
|
local lic_type="$1"
|
|
info "라이선스 발급 중: $lic_type"
|
|
|
|
# GUARDiA 기동 대기
|
|
local attempt=0
|
|
until curl -sf http://localhost:8001/ -o /dev/null 2>/dev/null; do
|
|
sleep 3; ((attempt++))
|
|
[[ $attempt -ge 20 ]] && { warn "GUARDiA 미기동 — 라이선스 수동 발급 필요"; return; }
|
|
done
|
|
|
|
# 로그인하여 토큰 획득
|
|
local TOKEN
|
|
TOKEN=$(curl -sf -X POST http://localhost:8001/api/auth/login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username":"admin","password":"1111"}' 2>/dev/null | \
|
|
python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null)
|
|
|
|
if [[ -z "$TOKEN" ]]; then
|
|
warn "로그인 실패 — 라이선스 수동 발급 필요"
|
|
return
|
|
fi
|
|
|
|
case "$lic_type" in
|
|
trial30|trial7)
|
|
# 체험판 발급
|
|
local days=30; [[ "$lic_type" == "trial7" ]] && days=7
|
|
local customer="GUARDiA ${days}일 체험판"
|
|
local result
|
|
result=$(curl -sf -X POST http://localhost:8001/api/license/trial \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"customer\":\"$customer\"}" 2>/dev/null)
|
|
|
|
if echo "$result" | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if d.get('valid') else 1)" 2>/dev/null; then
|
|
local lic_key exp_date
|
|
lic_key=$(echo "$result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('license_key',''))" 2>/dev/null)
|
|
exp_date=$(echo "$result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('expires_at','')[:10])" 2>/dev/null)
|
|
ok "체험 라이선스 발급 완료 (만료: $exp_date)"
|
|
|
|
# .env에 저장
|
|
local env_file
|
|
env_file=$(find /opt/guardia /home -name ".env" -path "*/itsm/*" 2>/dev/null | head -1)
|
|
if [[ -f "$env_file" ]]; then
|
|
grep -q "GUARDIA_LICENSE_KEY" "$env_file" && \
|
|
sed -i "s|GUARDIA_LICENSE_KEY=.*|GUARDIA_LICENSE_KEY=$lic_key|" "$env_file" || \
|
|
echo "GUARDIA_LICENSE_KEY=$lic_key" >> "$env_file"
|
|
ok "라이선스 키 .env에 저장 완료"
|
|
fi
|
|
else
|
|
warn "체험 라이선스 발급 실패 — 이미 사용한 체험판일 수 있습니다"
|
|
info "수동 발급: http://[서버IP]/license 에서 체험 시작"
|
|
fi
|
|
;;
|
|
|
|
*)
|
|
# 정식 라이선스 키 등록
|
|
local result
|
|
result=$(curl -sf -X POST http://localhost:8001/api/license/activate \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"license_key\":\"$lic_type\"}" 2>/dev/null)
|
|
if echo "$result" | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if d.get('valid') else 1)" 2>/dev/null; then
|
|
ok "정식 라이선스 등록 완료"
|
|
else
|
|
warn "라이선스 키 등록 실패 — 키를 확인하세요"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ── 설치 완료 보고 ─────────────────────────────────────────────
|
|
print_summary() {
|
|
local server_ip
|
|
server_ip=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")
|
|
local elapsed=$(( $(date +%s) - START_TIME ))
|
|
local elapsed_min=$(( elapsed / 60 ))
|
|
|
|
echo ""
|
|
echo -e "${GREEN}╔══════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${GREEN}║ GUARDiA ITSM 설치 완료! ║${NC}"
|
|
echo -e "${GREEN}╚══════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
echo " 소요 시간: ${elapsed_min}분"
|
|
echo ""
|
|
echo -e "${CYAN}접속 정보:${NC}"
|
|
echo " GUARDiA 대시보드: http://$server_ip"
|
|
echo " 초기 계정: admin / 1111"
|
|
echo " 라이선스: $LICENSE_TYPE"
|
|
echo ""
|
|
echo -e "${CYAN}서비스 현황:${NC}"
|
|
for svc in guardia-itsm tomcat9 ollama gitea nginx; do
|
|
systemctl is-active "$svc" &>/dev/null && \
|
|
echo " ✅ $svc" || \
|
|
echo " ⚠️ $svc (미실행)"
|
|
done
|
|
echo ""
|
|
echo -e "${CYAN}다음 단계:${NC}"
|
|
echo " 1. http://$server_ip 접속 → admin/1111 로그인"
|
|
echo " 2. 비밀번호 변경"
|
|
echo " 3. CMDB에 서버 등록"
|
|
echo " 4. 메신저 봇 연결 (선택)"
|
|
echo ""
|
|
echo " 검증: sudo bash $0 --test"
|
|
echo " 로그: $LOG_FILE"
|
|
echo ""
|
|
}
|
|
|
|
# ── 메인 실행 ─────────────────────────────────────────────────
|
|
main() {
|
|
mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || LOG_FILE="/tmp/guardia_install.log"
|
|
echo "=== GUARDiA ITSM 자동 설치 시작: $(date) ===" > "$LOG_FILE"
|
|
|
|
# 검증 모드
|
|
if [[ "$TEST_MODE" == "true" ]]; then
|
|
run_tests; exit $?
|
|
fi
|
|
|
|
# root 확인
|
|
check_root "$@"
|
|
|
|
# OS 감지
|
|
detect_os
|
|
|
|
# 설치 시작
|
|
echo ""
|
|
echo -e "${CYAN}╔══════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${CYAN}║ GUARDiA ITSM 자동 설치 ║${NC}"
|
|
echo -e "${CYAN}║ OS: $OS_TYPE | 라이선스: $LICENSE_TYPE ║${NC}"
|
|
echo -e "${CYAN}╚══════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
|
|
# OS별 설치 스크립트 실행
|
|
local SETUP_SCRIPT="$SCRIPT_DIR/setup_${OS_TYPE}.sh"
|
|
if [[ ! -f "$SETUP_SCRIPT" ]]; then
|
|
fail "설치 스크립트를 찾을 수 없습니다: $SETUP_SCRIPT"
|
|
fi
|
|
|
|
# 환경변수 전달
|
|
export DB_TYPE INSTALL_OLLAMA INSTALL_GITEA
|
|
[[ "$OFFLINE" == "true" ]] && export OFFLINE_PKG_DIR="$SCRIPT_DIR/offline/${OS_TYPE}"
|
|
[[ "$INSTALL_OLLAMA" == "false" ]] && export OLLAMA_INSTALL=skip
|
|
[[ "$INSTALL_GITEA" == "false" ]] && export INSTALL_GITEA=false
|
|
|
|
step "OS 설치 실행: $SETUP_SCRIPT"
|
|
bash "$SETUP_SCRIPT" 2>&1 | tee -a "$LOG_FILE"
|
|
|
|
step "라이선스 발급"
|
|
issue_license "$LICENSE_TYPE"
|
|
|
|
step "설치 검증"
|
|
run_tests || true
|
|
|
|
step "GUARDiA 자동 실행"
|
|
_auto_start
|
|
|
|
step "설치 완료"
|
|
print_summary
|
|
}
|
|
|
|
# ── GUARDiA 서비스 자동 실행 + 브라우저 열기 ──────────────────
|
|
_auto_start() {
|
|
info "GUARDiA 서비스 시작 확인 중..."
|
|
|
|
# 이미 실행 중이면 스킵, 아니면 시작
|
|
if systemctl is-active guardia-itsm &>/dev/null; then
|
|
ok "GUARDiA ITSM 이미 실행 중"
|
|
else
|
|
systemctl start guardia-itsm 2>/dev/null && ok "GUARDiA ITSM 시작" || warn "GUARDiA 시작 실패"
|
|
fi
|
|
|
|
# 연관 서비스 확인 및 시작
|
|
for svc in tomcat9 ollama gitea nginx; do
|
|
if systemctl is-enabled "$svc" &>/dev/null; then
|
|
systemctl is-active "$svc" &>/dev/null || \
|
|
{ systemctl start "$svc" 2>/dev/null && info "$svc 시작됨" || warn "$svc 시작 실패"; }
|
|
fi
|
|
done
|
|
|
|
# GUARDiA 응답 대기 (최대 60초)
|
|
info "GUARDiA 응답 대기 중..."
|
|
local attempt=0
|
|
until curl -sf http://localhost:8001/ -o /dev/null 2>/dev/null; do
|
|
sleep 3; ((attempt++))
|
|
[[ $attempt -ge 20 ]] && { warn "GUARDiA 응답 타임아웃 — 잠시 후 재확인"; return; }
|
|
done
|
|
ok "GUARDiA ITSM 정상 기동 확인!"
|
|
|
|
# 온보딩 초기화 (admin 계정 기준으로 온보딩 상태 리셋)
|
|
curl -sf -X POST http://localhost:8001/api/onboarding/reset \
|
|
-H "Authorization: Bearer $(curl -sf -X POST http://localhost:8001/api/auth/login \
|
|
-H 'Content-Type: application/json' -d '{"username":"admin","password":"1111"}' 2>/dev/null | \
|
|
python3 -c 'import sys,json; print(json.load(sys.stdin).get("access_token",""))' 2>/dev/null)" \
|
|
-o /dev/null 2>/dev/null && info "온보딩 가이드 활성화 완료" || true
|
|
|
|
# 브라우저 자동 열기 (GUI 환경)
|
|
local server_ip
|
|
server_ip=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")
|
|
local url="http://${server_ip}"
|
|
|
|
if command -v xdg-open &>/dev/null 2>&1; then
|
|
xdg-open "$url" 2>/dev/null & disown
|
|
ok "브라우저 자동 열기: $url"
|
|
elif command -v open &>/dev/null 2>&1; then # macOS
|
|
open "$url" 2>/dev/null & disown
|
|
ok "브라우저 자동 열기: $url"
|
|
else
|
|
info "브라우저에서 접속하세요: $url"
|
|
fi
|
|
}
|
|
|
|
main "$@"
|