From d827f22f4df1c910287198154c8ddb3e184ef35f Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Thu, 12 Mar 2026 08:00:14 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20tkreport/tkqc=20UX=20=EA=B0=9C=EC=84=A0?= =?UTF-8?q?=20-=20=EC=8B=A0=EA=B3=A0=20=EC=99=84=EB=A3=8C=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC,=20=ED=81=AC=EB=A1=9C=EC=8A=A4=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=20=EB=B0=B0=EB=84=88,=20AI=20=EB=8F=84=EC=9A=B0?= =?UTF-8?q?=EB=AF=B8=20=EA=B0=80=EC=8B=9C=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 신고 제출 후 alert → 성공 모달로 교체 (신고현황/새신고 버튼) - cross-nav.js: tkreport 페이지 상단 크로스시스템 네비게이션 배너 - report-status.html: AI 신고 도우미 버튼 추가 - common-header.js: tkqc 헤더에 "신고" 외부 링크 추가 - 배포 스크립트/가이드 추가 Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 + DEPLOY-GUIDE.md | 90 ++++++-- scripts/check-version.sh | 87 ++++++++ scripts/deploy-remote.sh | 208 ++++++++++++++++++ scripts/rollback-remote.sh | 135 ++++++++++++ system2-report/web/js/cross-nav.js | 42 ++++ system2-report/web/js/issue-report.js | 3 +- .../web/pages/safety/chat-report.html | 1 + .../web/pages/safety/issue-report.html | 89 +++++++- .../web/pages/safety/report-status.html | 2 + .../web/static/js/components/common-header.js | 36 ++- 11 files changed, 677 insertions(+), 17 deletions(-) create mode 100755 scripts/check-version.sh create mode 100755 scripts/deploy-remote.sh create mode 100755 scripts/rollback-remote.sh create mode 100644 system2-report/web/js/cross-nav.js diff --git a/.gitignore b/.gitignore index e314ca7..8205a99 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ venv/ coverage/ db_archive/ *.log +DEPLOY_LOG diff --git a/DEPLOY-GUIDE.md b/DEPLOY-GUIDE.md index 1c85a4b..71d993b 100644 --- a/DEPLOY-GUIDE.md +++ b/DEPLOY-GUIDE.md @@ -1,6 +1,6 @@ # TK Factory Services - NAS 배포 가이드 -> 최종 업데이트: 2026-02-09 +> 최종 업데이트: 2026-03-12 ## 아키텍처 개요 @@ -128,21 +128,38 @@ cp -r /volume1/docker/tkqc/tkqc-package/uploads \ /volume1/docker/backups/$(date +%Y%m%d)/tkqc-uploads ``` -### Step 2: 프로젝트 NAS 전송 +### Step 2: 프로젝트 NAS 전송 (git 기반) +NAS에 git이 설치되어 있으므로, Gitea에서 직접 clone/pull 합니다. + +**최초 설정 (1회):** ```bash -# 로컬 Mac에서 실행 -# node_modules 제외하여 전송 -rsync -avz --exclude='node_modules' --exclude='.env' --exclude='__pycache__' \ - -e ssh /Users/hyungiahn/Documents/code/tk-factory-services/ \ - hyungi@192.168.0.3:/volume1/docker/tk-factory-services/ +# NAS SSH 접속 +ssh hyungi@192.168.0.3 -# .env 파일 별도 전송 -scp -O /Users/hyungiahn/Documents/code/tk-factory-services/.env \ - hyungi@192.168.0.3:/volume1/docker/tk-factory-services/.env +# git credential 저장 설정 +git config --global credential.helper store + +# 기존 배포 디렉토리 백업 후 clone +cd /volume1/docker_1/ +mv tk-factory-services tk-factory-services.bak +git clone https://git.hyungi.net/hyungi/tk-factory-services.git + +# 기존 .env 복원 +cp tk-factory-services.bak/.env tk-factory-services/.env + +# 확인 후 백업 삭제 +cd tk-factory-services && git log -1 +rm -rf ../tk-factory-services.bak ``` -> **참고**: Synology에서 `scp`는 `-O` 옵션 필수 (레거시 프로토콜) +**이후 배포 (맥북에서 원커맨드):** +```bash +# 맥북에서 실행 - 자동으로 push 확인, NAS 비교, 빌드, health check 수행 +./scripts/deploy-remote.sh +``` + +> **참고**: 설정 파일 `~/.tk-deploy-config` 필요 (아래 "원격 배포 스크립트" 섹션 참고) ### Step 3: 기존 서비스 중지 @@ -220,13 +237,60 @@ curl -s http://localhost:30050/api/health # SSO Auth --- +## 원격 배포 스크립트 + +맥북에서 원커맨드로 배포/확인/롤백할 수 있는 스크립트입니다. + +### 설정 파일 + +`~/.tk-deploy-config` 생성 (git 추적 안 함): +```bash +NAS_HOST=100.71.132.52 +NAS_USER=hyungi +NAS_DEPLOY_PATH=/volume1/docker_1/tk-factory-services +NAS_SUDO_PASS= +``` + +### 사용법 + +```bash +# 배포 (pre-flight 체크 → NAS 비교 → 빌드 → health check → 로그 기록) +./scripts/deploy-remote.sh + +# 배포 상태 확인 (NAS 버전, origin 대비 차이, 컨테이너 상태) +./scripts/check-version.sh + +# 특정 커밋으로 롤백 +./scripts/rollback-remote.sh +``` + +### 배포 흐름 + +1. 로컬에서 코드 수정 → `git commit` → `git push` +2. `./scripts/deploy-remote.sh` 실행 +3. 스크립트가 자동으로: 로컬 clean 확인 → origin push 확인 → NAS 버전 비교 → 배포될 커밋 표시 → 사용자 확인 → git pull → docker build → nginx 재시작 → health check + +--- + ## 롤백 방법 -문제 발생 시 기존 서비스로 복원: +### git 기반 롤백 (권장) + +```bash +# 맥북에서 실행 - 특정 커밋으로 롤백 +./scripts/rollback-remote.sh + +# 최근 커밋 목록 확인 +git log --oneline -10 +``` + +### 레거시 서비스 복원 (통합 이전으로) + +문제 발생 시 기존 개별 서비스로 복원: ```bash # 통합 서비스 중지 -cd /volume1/docker/tk-factory-services +cd /volume1/docker_1/tk-factory-services echo 'fukdon-riwbaq-fiQfy2' | sudo -S /usr/local/bin/docker compose down # TK-FB 복원 diff --git a/scripts/check-version.sh b/scripts/check-version.sh new file mode 100755 index 0000000..8b38cbc --- /dev/null +++ b/scripts/check-version.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# =================================================================== +# TK Factory Services - 배포 상태 확인 (맥북에서 실행) +# =================================================================== +# 사용법: ./scripts/check-version.sh +# 설정: ~/.tk-deploy-config +# =================================================================== + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CONFIG_FILE="$HOME/.tk-deploy-config" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +# === 설정 로드 === +if [ ! -f "$CONFIG_FILE" ]; then + echo -e "${RED}ERROR: 설정 파일이 없습니다: $CONFIG_FILE${NC}" + exit 1 +fi + +source "$CONFIG_FILE" + +DOCKER="/usr/local/bin/docker" + +ssh_cmd() { + ssh -o ConnectTimeout=10 "${NAS_USER}@${NAS_HOST}" "$@" +} + +# === NAS 배포 버전 확인 === +echo "=== TK Factory Services - 배포 상태 ===" +echo "" + +echo -e "${CYAN}[NAS 배포 버전]${NC}" +NAS_INFO=$(ssh_cmd "cd ${NAS_DEPLOY_PATH} && git log -1 --format='%H|%s|%ci'" 2>/dev/null || echo "") + +if [ -z "$NAS_INFO" ]; then + echo -e " ${RED}NAS에서 git 정보를 가져올 수 없습니다${NC}" + exit 1 +fi + +NAS_HASH=$(echo "$NAS_INFO" | cut -d'|' -f1) +NAS_MSG=$(echo "$NAS_INFO" | cut -d'|' -f2) +NAS_DATE=$(echo "$NAS_INFO" | cut -d'|' -f3) +NAS_SHORT="${NAS_HASH:0:7}" + +echo " 커밋: ${NAS_SHORT} - ${NAS_MSG}" +echo " 날짜: ${NAS_DATE}" + +# === origin/main 대비 상태 === +echo "" +echo -e "${CYAN}[origin/main 대비]${NC}" + +cd "$PROJECT_DIR" +git fetch origin --quiet + +LOCAL_BRANCH=$(git rev-parse --abbrev-ref HEAD) +ORIGIN_HASH=$(git rev-parse "origin/${LOCAL_BRANCH}" 2>/dev/null || echo "") + +if [ -n "$ORIGIN_HASH" ]; then + if [ "$NAS_HASH" = "$ORIGIN_HASH" ]; then + echo -e " ${GREEN}최신 상태${NC} (origin/${LOCAL_BRANCH}과 동일)" + else + BEHIND_COUNT=$(git log "${NAS_HASH}..${ORIGIN_HASH}" --oneline 2>/dev/null | wc -l | tr -d ' ') + if [ "$BEHIND_COUNT" -gt 0 ]; then + echo -e " ${YELLOW}${BEHIND_COUNT}개 커밋 뒤처짐${NC}" + echo "" + echo " 미배포 커밋:" + git log "${NAS_HASH}..${ORIGIN_HASH}" --oneline --no-decorate | sed 's/^/ /' + else + echo -e " ${YELLOW}NAS가 origin보다 앞서 있거나 브랜치가 다릅니다${NC}" + fi + fi +fi + +# === Docker 컨테이너 상태 === +echo "" +echo -e "${CYAN}[Docker 컨테이너 상태]${NC}" +ssh_cmd "cd ${NAS_DEPLOY_PATH} && echo '${NAS_SUDO_PASS}' | sudo -S ${DOCKER} compose ps --format 'table {{.Name}}\t{{.Status}}'" 2>&1 | grep -v '^\[sudo\]' || echo " 컨테이너 상태를 가져올 수 없습니다" + +# === 최근 배포 로그 === +echo "" +echo -e "${CYAN}[최근 배포 로그]${NC}" +ssh_cmd "tail -5 ${NAS_DEPLOY_PATH}/DEPLOY_LOG" 2>/dev/null | sed 's/^/ /' || echo " 배포 로그가 없습니다" diff --git a/scripts/deploy-remote.sh b/scripts/deploy-remote.sh new file mode 100755 index 0000000..805bbeb --- /dev/null +++ b/scripts/deploy-remote.sh @@ -0,0 +1,208 @@ +#!/bin/bash +# =================================================================== +# TK Factory Services - 원격 배포 스크립트 (맥북에서 실행) +# =================================================================== +# 사용법: ./scripts/deploy-remote.sh +# 설정: ~/.tk-deploy-config +# =================================================================== + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CONFIG_FILE="$HOME/.tk-deploy-config" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +# === 설정 로드 === +if [ ! -f "$CONFIG_FILE" ]; then + echo -e "${RED}ERROR: 설정 파일이 없습니다: $CONFIG_FILE${NC}" + cat <<'EXAMPLE' + +다음 내용으로 생성하세요: + + NAS_HOST=100.71.132.52 + NAS_USER=hyungi + NAS_DEPLOY_PATH=/volume1/docker_1/tk-factory-services + NAS_SUDO_PASS= +EXAMPLE + exit 1 +fi + +source "$CONFIG_FILE" + +for var in NAS_HOST NAS_USER NAS_DEPLOY_PATH NAS_SUDO_PASS; do + if [ -z "${!var}" ]; then + echo -e "${RED}ERROR: $CONFIG_FILE에 $var 가 설정되지 않았습니다${NC}" + exit 1 + fi +done + +DOCKER="/usr/local/bin/docker" +DEPLOY_BRANCH="${DEPLOY_BRANCH:-main}" + +# === 헬퍼 함수 === +ssh_cmd() { + ssh -o ConnectTimeout=10 "${NAS_USER}@${NAS_HOST}" "$@" +} + +nas_docker() { + ssh_cmd "cd ${NAS_DEPLOY_PATH} && echo '${NAS_SUDO_PASS}' | sudo -S ${DOCKER} $*" 2>&1 +} + +# === Phase 1: Pre-flight 체크 === +echo "=== TK Factory Services - 원격 배포 ===" +echo "" +echo -e "${CYAN}[1/5] Pre-flight 체크${NC}" + +cd "$PROJECT_DIR" + +# Working tree clean 확인 +if [ -n "$(git status --porcelain)" ]; then + echo -e "${RED}ERROR: 로컬에 커밋되지 않은 변경사항이 있습니다${NC}" + echo "" + git status --short + echo "" + echo "먼저 커밋하거나 stash하세요." + exit 1 +fi + +# 로컬 커밋 정보 +LOCAL_HASH=$(git rev-parse HEAD) +LOCAL_SHORT=$(git rev-parse --short HEAD) +LOCAL_MSG=$(git log -1 --format='%s') +LOCAL_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# origin 동기화 확인 +git fetch origin --quiet + +ORIGIN_HASH=$(git rev-parse "origin/${LOCAL_BRANCH}" 2>/dev/null || echo "") +if [ "$LOCAL_HASH" != "$ORIGIN_HASH" ]; then + echo -e "${RED}ERROR: 로컬 커밋이 origin에 push되지 않았습니다${NC}" + echo " 로컬: ${LOCAL_SHORT} (${LOCAL_MSG})" + echo " 원격: $(git rev-parse --short "origin/${LOCAL_BRANCH}" 2>/dev/null || echo 'N/A')" + echo "" + echo "먼저 push하세요: git push origin ${LOCAL_BRANCH}" + exit 1 +fi + +echo -e " 로컬 HEAD: ${GREEN}${LOCAL_SHORT}${NC} - ${LOCAL_MSG}" +echo -e " 브랜치: ${LOCAL_BRANCH}" + +# === Phase 2: NAS 상태 비교 === +echo "" +echo -e "${CYAN}[2/5] NAS 배포 상태 확인${NC}" + +NAS_HASH=$(ssh_cmd "cd ${NAS_DEPLOY_PATH} && git log -1 --format='%H'" 2>/dev/null || echo "") + +if [ -z "$NAS_HASH" ]; then + echo -e "${RED}ERROR: NAS에서 git 정보를 가져올 수 없습니다${NC}" + echo " 경로: ${NAS_DEPLOY_PATH}" + echo " NAS에 git clone이 완료되었는지 확인하세요." + exit 1 +fi + +NAS_SHORT="${NAS_HASH:0:7}" +NAS_MSG=$(ssh_cmd "cd ${NAS_DEPLOY_PATH} && git log -1 --format='%s'" 2>/dev/null) + +if [ "$LOCAL_HASH" = "$NAS_HASH" ]; then + echo -e " ${GREEN}이미 최신 버전입니다!${NC} (${NAS_SHORT} - ${NAS_MSG})" + exit 0 +fi + +echo -e " NAS 현재: ${YELLOW}${NAS_SHORT}${NC} - ${NAS_MSG}" +echo -e " 배포 대상: ${GREEN}${LOCAL_SHORT}${NC} - ${LOCAL_MSG}" + +# 배포될 커밋 목록 +COMMIT_COUNT=$(git log "${NAS_HASH}..${LOCAL_HASH}" --oneline | wc -l | tr -d ' ') +echo "" +echo "=== 배포될 커밋 (${COMMIT_COUNT}개) ===" +git log "${NAS_HASH}..${LOCAL_HASH}" --oneline --no-decorate +echo "" + +read -p "배포를 진행하시겠습니까? [y/N] " confirm +if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + echo "배포가 취소되었습니다." + exit 0 +fi + +# === Phase 3: 배포 실행 === +echo "" +echo -e "${CYAN}[3/5] NAS 코드 업데이트${NC}" + +ssh_cmd "cd ${NAS_DEPLOY_PATH} && git fetch origin && git reset --hard origin/${DEPLOY_BRANCH}" + +UPDATED_HASH=$(ssh_cmd "cd ${NAS_DEPLOY_PATH} && git log -1 --format='%H %s'" 2>/dev/null) +echo -e " ${GREEN}완료${NC}: ${UPDATED_HASH}" + +echo "" +echo -e "${CYAN}[4/5] Docker 컨테이너 빌드 및 재시작${NC}" +echo " (빌드에 시간이 걸릴 수 있습니다...)" +echo "" + +nas_docker "compose up -d --build" + +echo "" +echo " nginx 프록시 컨테이너 재시작 (IP 캐시 갱신)..." +nas_docker "restart tk-gateway tk-system2-web tk-system3-web" + +# === Phase 4: 배포 후 검증 === +echo "" +echo -e "${CYAN}[5/5] 배포 검증${NC} (15초 대기 후 health check)" +sleep 15 + +echo "" +echo "=== Container Status ===" +nas_docker "compose ps --format 'table {{.Name}}\t{{.Status}}'" || true + +echo "" +echo "=== HTTP Health Check ===" +HEALTH_PASS=0 +HEALTH_FAIL=0 + +check_remote() { + local name="$1" + local path="$2" + local status + status=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 http://localhost:${path}" 2>/dev/null || echo "000") + if [ "$status" -ge 200 ] 2>/dev/null && [ "$status" -lt 400 ] 2>/dev/null; then + printf " %-25s ${GREEN}OK${NC} (%s)\n" "$name" "$status" + ((HEALTH_PASS++)) + else + printf " %-25s ${RED}FAIL${NC} (%s)\n" "$name" "$status" + ((HEALTH_FAIL++)) + fi +} + +check_remote "Gateway" "30000/" +check_remote "SSO Auth" "30050/health" +check_remote "System 1 API" "30005/api/health" +check_remote "System 1 Web" "30080/" +check_remote "System 1 FastAPI" "30008/health" +check_remote "System 2 API" "30105/api/health" +check_remote "System 2 Web" "30180/" +check_remote "System 3 API" "30200/api/health" +check_remote "System 3 Web" "30280/" +check_remote "tkuser API" "30300/api/health" +check_remote "tkuser Web" "30380/" +check_remote "phpMyAdmin" "30880/" + +echo "" +echo " Health: PASS=${HEALTH_PASS} FAIL=${HEALTH_FAIL}" + +# === Phase 5: 배포 로그 기록 === +TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') +ssh_cmd "echo '${TIMESTAMP} | ${LOCAL_SHORT} | ${LOCAL_MSG}' >> ${NAS_DEPLOY_PATH}/DEPLOY_LOG" + +echo "" +if [ "$HEALTH_FAIL" -gt 0 ]; then + echo -e "${YELLOW}배포 완료 (일부 서비스 health check 실패)${NC}" + echo " 로그 확인: ssh ${NAS_USER}@${NAS_HOST} \"cd ${NAS_DEPLOY_PATH} && echo '...' | sudo -S ${DOCKER} compose logs --tail=50\"" +else + echo -e "${GREEN}배포 완료!${NC}" +fi +echo " 버전: ${LOCAL_SHORT} - ${LOCAL_MSG}" diff --git a/scripts/rollback-remote.sh b/scripts/rollback-remote.sh new file mode 100755 index 0000000..8fb5dd5 --- /dev/null +++ b/scripts/rollback-remote.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# =================================================================== +# TK Factory Services - 원격 롤백 스크립트 (맥북에서 실행) +# =================================================================== +# 사용법: ./scripts/rollback-remote.sh +# 설정: ~/.tk-deploy-config +# =================================================================== + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CONFIG_FILE="$HOME/.tk-deploy-config" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +# === 인자 확인 === +TARGET_HASH="${1:-}" +if [ -z "$TARGET_HASH" ]; then + echo "사용법: ./scripts/rollback-remote.sh " + echo "" + echo "예시:" + echo " ./scripts/rollback-remote.sh abc1234" + echo " ./scripts/rollback-remote.sh abc1234567890" + echo "" + echo "최근 커밋 목록:" + cd "$(dirname "$0")/.." + git log --oneline -10 + exit 1 +fi + +# === 설정 로드 === +if [ ! -f "$CONFIG_FILE" ]; then + echo -e "${RED}ERROR: 설정 파일이 없습니다: $CONFIG_FILE${NC}" + exit 1 +fi + +source "$CONFIG_FILE" + +for var in NAS_HOST NAS_USER NAS_DEPLOY_PATH NAS_SUDO_PASS; do + if [ -z "${!var}" ]; then + echo -e "${RED}ERROR: $CONFIG_FILE에 $var 가 설정되지 않았습니다${NC}" + exit 1 + fi +done + +DOCKER="/usr/local/bin/docker" + +ssh_cmd() { + ssh -o ConnectTimeout=10 "${NAS_USER}@${NAS_HOST}" "$@" +} + +nas_docker() { + ssh_cmd "cd ${NAS_DEPLOY_PATH} && echo '${NAS_SUDO_PASS}' | sudo -S ${DOCKER} $*" 2>&1 +} + +# === 커밋 유효성 확인 === +cd "$PROJECT_DIR" + +FULL_HASH=$(git rev-parse "$TARGET_HASH" 2>/dev/null || echo "") +if [ -z "$FULL_HASH" ]; then + echo -e "${RED}ERROR: 유효하지 않은 커밋 해시입니다: ${TARGET_HASH}${NC}" + exit 1 +fi + +TARGET_SHORT="${FULL_HASH:0:7}" +TARGET_MSG=$(git log -1 --format='%s' "$FULL_HASH") +TARGET_DATE=$(git log -1 --format='%ci' "$FULL_HASH") + +# === 현재 NAS 상태 확인 === +echo "=== TK Factory Services - 원격 롤백 ===" +echo "" + +NAS_HASH=$(ssh_cmd "cd ${NAS_DEPLOY_PATH} && git log -1 --format='%H'" 2>/dev/null || echo "") +NAS_SHORT="${NAS_HASH:0:7}" +NAS_MSG=$(ssh_cmd "cd ${NAS_DEPLOY_PATH} && git log -1 --format='%s'" 2>/dev/null) + +echo -e " NAS 현재: ${YELLOW}${NAS_SHORT}${NC} - ${NAS_MSG}" +echo -e " 롤백 대상: ${CYAN}${TARGET_SHORT}${NC} - ${TARGET_MSG}" +echo -e " 커밋 날짜: ${TARGET_DATE}" + +if [ "$FULL_HASH" = "$NAS_HASH" ]; then + echo "" + echo -e "${GREEN}이미 해당 버전입니다.${NC}" + exit 0 +fi + +echo "" +read -p "롤백을 진행하시겠습니까? [y/N] " confirm +if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + echo "롤백이 취소되었습니다." + exit 0 +fi + +# === 롤백 실행 === +echo "" +echo -e "${CYAN}[1/3] NAS 코드 롤백${NC}" + +ssh_cmd "cd ${NAS_DEPLOY_PATH} && git fetch origin && git checkout ${FULL_HASH}" + +echo -e " ${GREEN}완료${NC}" + +echo "" +echo -e "${CYAN}[2/3] Docker 컨테이너 재빌드${NC}" +echo " (빌드에 시간이 걸릴 수 있습니다...)" +echo "" + +nas_docker "compose up -d --build" + +echo "" +echo " nginx 프록시 컨테이너 재시작..." +nas_docker "restart tk-gateway tk-system2-web tk-system3-web" + +echo "" +echo -e "${CYAN}[3/3] 배포 검증${NC} (15초 대기)" +sleep 15 + +echo "" +echo "=== Container Status ===" +nas_docker "compose ps --format 'table {{.Name}}\t{{.Status}}'" || true + +# 배포 로그 기록 +TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') +ssh_cmd "echo '${TIMESTAMP} | ${TARGET_SHORT} | ROLLBACK: ${TARGET_MSG}' >> ${NAS_DEPLOY_PATH}/DEPLOY_LOG" + +echo "" +echo -e "${GREEN}롤백 완료!${NC}" +echo " 버전: ${TARGET_SHORT} - ${TARGET_MSG}" +echo "" +echo -e "${YELLOW}주의: NAS가 detached HEAD 상태입니다.${NC}" +echo " 다음 정상 배포 시 자동으로 main 브랜치로 복귀합니다." diff --git a/system2-report/web/js/cross-nav.js b/system2-report/web/js/cross-nav.js new file mode 100644 index 0000000..2c17d2b --- /dev/null +++ b/system2-report/web/js/cross-nav.js @@ -0,0 +1,42 @@ +/** + * 크로스시스템 네비게이션 배너 + * tkreport 페이지 상단에 시스템 간 이동 링크를 제공 + */ +document.addEventListener('DOMContentLoaded', function() { + var path = window.location.pathname; + var host = window.location.hostname; + var protocol = window.location.protocol; + + // tkqc URL 결정 + var tkqcUrl; + if (host.includes('technicalkorea.net')) { + tkqcUrl = protocol + '//tkqc.technicalkorea.net'; + } else { + tkqcUrl = protocol + '//' + host + ':30200'; + } + + // 현재 페이지 판별 + var isIssueReport = path.includes('issue-report'); + var isReportStatus = path.includes('report-status'); + var isChatReport = path.includes('chat-report'); + + var nav = document.createElement('div'); + nav.id = 'crossNav'; + nav.innerHTML = + '신고하기' + + '신고현황' + + 'AI 도우미' + + '부적합관리(TKQC) →'; + + var style = document.createElement('style'); + style.textContent = + '#crossNav{display:flex;align-items:center;gap:0.25rem;padding:0.5rem 0.75rem;background:#1e293b;overflow-x:auto;-webkit-overflow-scrolling:touch;}' + + '.cn-link{color:rgba(255,255,255,0.7);text-decoration:none;font-size:0.8125rem;font-weight:500;padding:0.25rem 0.625rem;border-radius:0.375rem;white-space:nowrap;transition:background 0.15s,color 0.15s;-webkit-tap-highlight-color:transparent;}' + + '.cn-link:hover,.cn-link:active{color:#fff;background:rgba(255,255,255,0.1);}' + + '.cn-active{color:#fff !important;background:rgba(255,255,255,0.15) !important;font-weight:600;}' + + '.cn-external{margin-left:auto;color:#38bdf8;border:1px solid rgba(56,189,248,0.3);font-size:0.75rem;}' + + '.cn-external:hover,.cn-external:active{background:rgba(56,189,248,0.15);color:#7dd3fc;}'; + + document.head.appendChild(style); + document.body.insertBefore(nav, document.body.firstChild); +}); diff --git a/system2-report/web/js/issue-report.js b/system2-report/web/js/issue-report.js index 2475684..e67424c 100644 --- a/system2-report/web/js/issue-report.js +++ b/system2-report/web/js/issue-report.js @@ -1185,8 +1185,7 @@ async function submitReport() { const data = await response.json(); if (data.success) { - alert('신고가 등록되었습니다.'); - window.location.href = '/pages/safety/my-reports.html'; + document.getElementById('successModal').style.display = 'flex'; } else { throw new Error(data.error || '신고 등록 실패'); } diff --git a/system2-report/web/pages/safety/chat-report.html b/system2-report/web/pages/safety/chat-report.html index 6f7f4e4..1da9c3b 100644 --- a/system2-report/web/pages/safety/chat-report.html +++ b/system2-report/web/pages/safety/chat-report.html @@ -32,6 +32,7 @@ + diff --git a/system2-report/web/pages/safety/issue-report.html b/system2-report/web/pages/safety/issue-report.html index f6c0aba..5422aed 100644 --- a/system2-report/web/pages/safety/issue-report.html +++ b/system2-report/web/pages/safety/issue-report.html @@ -556,6 +556,79 @@ } } + /* 성공 모달 */ + .success-modal-overlay { + display: none; + position: fixed; + inset: 0; + z-index: 10001; + background: rgba(0,0,0,0.5); + align-items: center; + justify-content: center; + padding: 1rem; + } + .success-modal { + background: white; + border-radius: 1rem; + padding: 2rem 1.5rem; + text-align: center; + max-width: 320px; + width: 100%; + box-shadow: 0 20px 60px rgba(0,0,0,0.3); + } + .success-modal .success-icon { + width: 64px; + height: 64px; + border-radius: 50%; + background: #d1fae5; + color: #059669; + font-size: 2rem; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + } + .success-modal h3 { + font-size: 1.125rem; + font-weight: 700; + color: #1f2937; + margin: 0 0 0.5rem; + } + .success-modal p { + font-size: 0.875rem; + color: #6b7280; + margin: 0 0 1.5rem; + } + .success-modal .modal-buttons { + display: flex; + flex-direction: column; + gap: 0.625rem; + } + .success-modal .btn-primary { + padding: 0.75rem; + background: #ef4444; + color: white; + border: none; + border-radius: 0.625rem; + font-size: 0.9375rem; + font-weight: 600; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + } + .success-modal .btn-primary:active { background: #dc2626; } + .success-modal .btn-secondary { + padding: 0.75rem; + background: #f3f4f6; + color: #374151; + border: none; + border-radius: 0.625rem; + font-size: 0.9375rem; + font-weight: 600; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + } + .success-modal .btn-secondary:active { background: #e5e7eb; } + /* Responsive */ @media (min-width: 480px) { body { max-width: 480px; margin: 0 auto; min-height: 100vh; } @@ -688,9 +761,23 @@ + +
+
+
+

신고가 성공적으로 등록되었습니다!

+

등록된 신고는 담당자가 확인 후 처리합니다.

+ +
+
+ - + + diff --git a/system2-report/web/pages/safety/report-status.html b/system2-report/web/pages/safety/report-status.html index aa47934..fc04b16 100644 --- a/system2-report/web/pages/safety/report-status.html +++ b/system2-report/web/pages/safety/report-status.html @@ -286,6 +286,7 @@ + 🤖 AI 신고 + 신고하기 @@ -299,6 +300,7 @@ + diff --git a/system3-nonconformance/web/static/js/components/common-header.js b/system3-nonconformance/web/static/js/components/common-header.js index 28d9566..1b1af01 100644 --- a/system3-nonconformance/web/static/js/components/common-header.js +++ b/system3-nonconformance/web/static/js/components/common-header.js @@ -95,9 +95,31 @@ class CommonHeader { color: 'text-purple-600', bgColor: 'text-purple-600 hover:bg-purple-50' }, + { + id: 'report', + title: '신고', + icon: 'fas fa-exclamation-triangle', + url: this.getReportUrl(), + pageName: 'report', + color: 'text-orange-600', + bgColor: 'text-orange-600 hover:bg-orange-50', + external: true + }, ]; } + /** + * tkreport URL 생성 + */ + getReportUrl() { + var host = window.location.hostname; + var protocol = window.location.protocol; + if (host.includes('technicalkorea.net')) { + return protocol + '//tkreport.technicalkorea.net/pages/safety/issue-report.html'; + } + return protocol + '//' + host + ':30100/pages/safety/issue-report.html'; + } + /** * 헤더 초기화 * @param {Object} user - 현재 사용자 정보 @@ -372,9 +394,21 @@ class CommonHeader { `; } + // 외부 링크 + if (menu.external) { + return ` + + + ${menu.title} + + `; + } + // 일반 메뉴 아이템 return ` -