From a60451b95fdbe390bf93f42f32db1bccd93b23c5 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Wed, 1 Jul 2026 12:58:21 +0900 Subject: [PATCH] fix: favicon and ci deployment checks --- .gitea/workflows/browser-e2e.yml | 22 ++++++++- .gitea/workflows/deploy.yml | 23 +++++++-- DEPLOYMENT_GUIDE.md | 41 +++++++++++++--- PRODUCTION_CHECKLIST.md | 48 ++++--------------- README.md | 2 +- TaxBaik.Web/Components/Admin/App.razor | 2 + TaxBaik.Web/Pages/_Layout.cshtml | 2 + TaxBaik.Web/Program.cs | 38 ++++++++++----- .../TelegramReportBackgroundService.cs | 27 +++++++---- TaxBaik.Web/wwwroot/favicon.svg | 12 +++++ TaxBaik.Web/wwwroot/js/admin-session.js | 3 ++ deploy.sh | 5 ++ deploy/taxbaik-proxy.service | 22 +++++++++ deploy/taxbaik.service | 4 +- deploy_gb.sh | 31 ++++++++++++ 15 files changed, 208 insertions(+), 74 deletions(-) create mode 100644 TaxBaik.Web/wwwroot/favicon.svg create mode 100644 deploy/taxbaik-proxy.service diff --git a/.gitea/workflows/browser-e2e.yml b/.gitea/workflows/browser-e2e.yml index d8ae63d..d250d7c 100644 --- a/.gitea/workflows/browser-e2e.yml +++ b/.gitea/workflows/browser-e2e.yml @@ -49,12 +49,13 @@ jobs: # Suppress stderr and allow failures to handle transition/down periods cleanly VERSION_BODY="$(curl -fsS "http://${DEPLOY_HOST}/taxbaik/version.json" 2>/dev/null || true)" BLOG_STATUS="$(curl -s -o /dev/null -w '%{http_code}' "http://${DEPLOY_HOST}/taxbaik/blog/accountant-mistakes-5" || true)" - if echo "$VERSION_BODY" | grep -q "\"version\": \"${SHORT_VERSION}\"" && [ "$BLOG_STATUS" = "200" ]; then + LOGIN_STATUS="$(curl -s -o /dev/null -w '%{http_code}' "http://${DEPLOY_HOST}/taxbaik/admin/login" || true)" + if echo "$VERSION_BODY" | grep -q "\"version\": \"${SHORT_VERSION}\"" && [ "$BLOG_STATUS" = "200" ] && [ "$LOGIN_STATUS" = "200" ]; then echo "✓ Deployment ready for ${SHORT_VERSION} (attempt $i/20)" exit 0 fi if [ $i -lt 20 ]; then - echo " Attempt $i/20: waiting for deployment... (blog=${BLOG_STATUS:-?}, version=${VERSION_BODY:0:30}...)" + echo " Attempt $i/20: waiting for deployment... (blog=${BLOG_STATUS:-?}, login=${LOGIN_STATUS:-?}, version=${VERSION_BODY:0:30}...)" sleep 3 fi done @@ -72,6 +73,23 @@ jobs: echo "Running E2E tests on Desktop Chrome (production verification)" npx playwright test --project="Desktop Chrome" --reporter=html --reporter=list + - name: API smoke verification + env: + DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} + E2E_ADMIN_USERNAME: test_admin + E2E_ADMIN_PASSWORD: TestAdmin@123456 + run: | + set -e + TOKEN="$(curl -s -X POST "http://${DEPLOY_HOST}/taxbaik/api/auth/login" -H "Content-Type: application/json" -d "{\"username\":\"${E2E_ADMIN_USERNAME}\",\"password\":\"${E2E_ADMIN_PASSWORD}\"}" | python3 -c 'import sys, json; print(json.load(sys.stdin)["accessToken"])')" + test -n "$TOKEN" + curl -fsS -H "Authorization: Bearer $TOKEN" "http://${DEPLOY_HOST}/taxbaik/api/blog/admin?page=1&pageSize=1" >/dev/null + curl -fsS -H "Authorization: Bearer $TOKEN" "http://${DEPLOY_HOST}/taxbaik/api/faq" >/dev/null + curl -fsS -H "Authorization: Bearer $TOKEN" "http://${DEPLOY_HOST}/taxbaik/api/announcement" >/dev/null + curl -fsS -H "Authorization: Bearer $TOKEN" "http://${DEPLOY_HOST}/taxbaik/api/inquiry?page=1&pageSize=1" >/dev/null + curl -fsS "http://${DEPLOY_HOST}/taxbaik/favicon.svg" >/dev/null + curl -fsS "http://${DEPLOY_HOST}/taxbaik/favicon.ico" >/dev/null + curl -fsS "http://${DEPLOY_HOST}/taxbaik/robots.txt" >/dev/null + - name: Browser E2E summary if: always() run: | diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 9e6d593..3df5c79 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -1,7 +1,6 @@ name: TaxBaik CI/CD on: - workflow_dispatch: push: branches: - master @@ -33,6 +32,9 @@ jobs: - name: Publish Web run: dotnet publish TaxBaik.Web/ -c Release -o ./publish --no-restore + - name: Publish Proxy + run: dotnet publish TaxBaik.Proxy/ -c Release -o ./publish/proxy --no-restore + - name: Write production secrets run: | set -e @@ -67,6 +69,11 @@ jobs: )' test -s ./publish/appsettings.Production.json || { echo "appsettings.Production.json is empty" >&2; exit 1; } + - name: Verify proxy artifact + run: | + test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; } + test -s ./publish/proxy/TaxBaik.Proxy.runtimeconfig.json || { echo "TaxBaik.Proxy.runtimeconfig.json missing" >&2; exit 1; } + - name: Copy migrations run: cp -r db/migrations ./publish/migrations || true @@ -107,6 +114,7 @@ jobs: - name: Deploy & verify on server run: | set -e + export TAXBAIK_DEPLOY_FROM_CI=1 TIMESTAMP=$(date +%Y%m%d_%H%M%S) COMMIT=$(git rev-parse --short HEAD) DEPLOY_HOST="${{ secrets.DEPLOY_HOST }}" @@ -163,6 +171,8 @@ jobs: echo "--- [2/5] 운영 설정 검증 ---" test -s "\$DEPLOY_DIR/appsettings.Production.json" \ || { echo "FATAL: appsettings.Production.json 없음" >&2; exit 1; } + test -s "\$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" \ + || { echo "FATAL: TaxBaik.Proxy.dll 없음" >&2; exit 1; } echo "--- [3/4] Green-Blue 배포 실행 ---" chmod +x "\$DEPLOY_DIR/deploy_gb.sh" @@ -190,13 +200,20 @@ jobs: fi echo "✓ [3/4] 버전 정보 확인 완료" - # 검증 3: 관리자 로그인 페이지 + # 검증 4: 5001 프록시 확인 + if ! ss -tlnp | grep -q ':5001 '; then + echo "❌ 5001 프록시가 실행 중이 아님" >&2 + exit 1 + fi + echo "✓ [4/5] 5001 프록시 확인 완료" + + # 검증 5: 관리자 로그인 페이지 LOGIN_STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/admin/login 2>/dev/null || echo "000") if [ "\$LOGIN_STATUS" != "200" ]; then echo "❌ 관리자 로그인 페이지 로드 실패 (상태: \$LOGIN_STATUS)" >&2 exit 1 fi - echo "✓ [4/4] 관리자 페이지 로드 완료" + echo "✓ [5/5] 관리자 페이지 로드 완료" echo "✓ 서비스 정상 (시도 \$i/\$ATTEMPTS)" # 구 배포 디렉토리 정리 (최근 5개 보존) diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md index e6edf47..f821f0f 100644 --- a/DEPLOYMENT_GUIDE.md +++ b/DEPLOYMENT_GUIDE.md @@ -19,20 +19,30 @@ GRANT ALL PRIVILEGES ON DATABASE taxbaikdb TO taxbaik; ### 2. 환경 변수 설정 -**Web 서비스** (`/etc/systemd/system/taxbaik.service`): +**Web 서비스** (`/etc/systemd/system/taxbaik.service`, 백엔드 전용): ```ini [Service] Environment=ASPNETCORE_ENVIRONMENT=Production -Environment=ASPNETCORE_URLS=http://127.0.0.1:5001 +Environment=ASPNETCORE_URLS=http://127.0.0.1:5004 Environment=ConnectionStrings__Default=Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=your_secure_password ``` +**프록시 서비스** (`/etc/systemd/system/taxbaik-proxy.service`, 5001 진입점): +```ini +[Service] +ExecStart=/usr/bin/dotnet TaxBaik.Proxy.dll +WorkingDirectory=/home/kjh2064/taxbaik_active +Restart=always +``` + ### 3. systemd 서비스 파일 설치 ```bash sudo cp deploy/taxbaik.service /etc/systemd/system/ +sudo cp deploy/taxbaik-proxy.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable taxbaik +sudo systemctl enable taxbaik-proxy ``` ### 4. Nginx 설정 @@ -69,7 +79,7 @@ sudo systemctl reload nginx master 브랜치 push → build → test → publish → restart → health check → Playwright ``` -수동 배포는 비상 롤백 외에는 사용하지 않습니다. 배포 이슈는 Gitea Actions 로그로 해결합니다. +수동 배포는 사용하지 않습니다. `deploy_gb.sh`는 `TAXBAIK_DEPLOY_FROM_CI=1`이 없으면 즉시 종료하므로, 배포는 반드시 Gitea Actions에서만 실행됩니다. ## 마이그레이션 자동 실행 @@ -132,6 +142,7 @@ ls -la ~/deployments/ | grep taxbaik # 심링크 변경 (예: 이전 버전이 taxbaik_20260626_140000) ln -sfn ~/deployments/taxbaik_20260626_140000 ~/taxbaik_active +sudo systemctl restart taxbaik-proxy sudo systemctl restart taxbaik ``` @@ -143,10 +154,10 @@ sudo systemctl restart taxbaik ssh kjh2064@178.104.200.7 # 서비스 상태 -systemctl status taxbaik +systemctl status taxbaik taxbaik-proxy # 포트 확인 -netstat -tlnp | grep -E '5001' +netstat -tlnp | grep -E '5001|5004' # 프로세스 확인 ps aux | grep TaxBaik @@ -169,9 +180,27 @@ journalctl -u taxbaik -f | 404 /taxbaik | Nginx 설정 미적용 | `sudo nginx -t && sudo systemctl reload nginx` | | Blazor WebSocket 안 됨 | `/taxbaik` location에 `proxy_http_version 1.1`, `Upgrade`, `Connection \"Upgrade\"` 헤더가 모두 있는지 확인 | | DB 연결 오류 | 환경 변수 미설정 | systemd service 파일의 ConnectionStrings__Default 확인 | -| 503 Service Unavailable | 앱 미시작 | `sudo systemctl restart taxbaik` | +| 503 Service Unavailable | 백엔드 또는 프록시 미시작 | `sudo systemctl restart taxbaik-proxy taxbaik` | | 마이그레이션 실패 | DB 권한 문제 | `GRANT ALL PRIVILEGES ON DATABASE taxbaikdb TO taxbaik;` | +## 운영 복구 순서 + +```bash +ssh kjh2064@178.104.200.7 +sudo cp /home/kjh2064/taxbaik.service /etc/systemd/system/taxbaik.service +sudo cp /home/kjh2064/taxbaik-proxy.service /etc/systemd/system/taxbaik-proxy.service +sudo systemctl daemon-reload +sudo systemctl restart taxbaik-proxy +sudo systemctl restart taxbaik +curl -I http://127.0.0.1:5001/taxbaik/admin/login +``` + +## 원라인 점검 + +```bash +ssh kjh2064@178.104.200.7 'systemctl status taxbaik taxbaik-proxy --no-pager --lines=3 && ss -tlnp | grep -E ":5001 |:5004 " && curl -fsSI http://127.0.0.1:5001/taxbaik/admin/login && curl -fsS http://127.0.0.1:5001/taxbaik/favicon.svg >/dev/null && curl -fsS http://127.0.0.1:5001/taxbaik/robots.txt >/dev/null' +``` + ## 초기 데이터 ### 관리자 계정 diff --git a/PRODUCTION_CHECKLIST.md b/PRODUCTION_CHECKLIST.md index 3420227..f1589f1 100644 --- a/PRODUCTION_CHECKLIST.md +++ b/PRODUCTION_CHECKLIST.md @@ -48,29 +48,7 @@ ssh kjh2064@178.104.200.7 'bash ~/SERVER_SETUP.sh' # ~/taxbaik_active ``` -### 2단계: 첫 배포 (수동) - -```bash -# 로컬에서 실행 -TIMESTAMP=$(date +%Y%m%d_%H%M%S) - -# SSH 키 설정 (필요시) -export DEPLOY_USER="kjh2064" -export DEPLOY_HOST="178.104.200.7" - -# 배포 -rsync -avz --delete ./publish/ \ - $DEPLOY_USER@$DEPLOY_HOST:~/deployments/taxbaik_${TIMESTAMP}/ - -# 심링크 변경 및 시작 -ssh $DEPLOY_USER@$DEPLOY_HOST << EOF -ln -sfn ~/deployments/taxbaik_${TIMESTAMP} ~/taxbaik_active -sudo systemctl start taxbaik -sudo systemctl status taxbaik -EOF -``` - -### 3단계: Gitea Actions 설정 (선택) +### 2단계: Gitea Actions 설정 **Gitea 저장소 Settings → Secrets 추가**: - `DEPLOY_USER`: `kjh2064` @@ -217,8 +195,8 @@ curl -I -H "Accept-Encoding: gzip" http://178.104.200.7/taxbaik/ | grep -i encod | 증상 | 원인 | 해결 방법 | |------|------|----------| | 404 /taxbaik | Nginx 설정 미적용 | `sudo nginx -t && sudo systemctl reload nginx` | -| 502 Bad Gateway | 앱 미실행 | `sudo systemctl restart taxbaik` | -| 503 Service Unavailable | 앱 충돌 | 로그 확인: `journalctl -u taxbaik -n 50` | +| 502 Bad Gateway | 프록시 또는 백엔드 미실행 | `sudo systemctl restart taxbaik-proxy taxbaik` | +| 503 Service Unavailable | 백엔드 충돌 또는 비밀값 누락 | 로그 확인: `journalctl -u taxbaik -n 50` | | DB 연결 오류 | 환경 변수 미설정 | systemd 파일의 ConnectionStrings__Default 확인 | | HTTPS 오류 | SSL 미구성 | 개발 환경에서는 HTTP 사용 (IP 기반) | | 마이그레이션 실패 | 테이블 존재 | `DROP DATABASE taxbaikdb;` 후 재시작 | @@ -230,11 +208,11 @@ curl -I -H "Accept-Encoding: gzip" http://178.104.200.7/taxbaik/ | grep -i encod ### 실시간 모니터링 ```bash -# 터미널 1: 웹 서비스 로그 +# 터미널 1: 백엔드 로그 ssh kjh2064@178.104.200.7 'journalctl -u taxbaik -f' -# 터미널 2: 통합 서비스 로그 -ssh kjh2064@178.104.200.7 'journalctl -u taxbaik -f' +# 터미널 2: 프록시 로그 +ssh kjh2064@178.104.200.7 'journalctl -u taxbaik-proxy -f' # 터미널 3: Nginx 로그 ssh kjh2064@178.104.200.7 'sudo tail -f /var/log/nginx/access.log | grep taxbaik' @@ -246,13 +224,7 @@ ssh kjh2064@178.104.200.7 'watch -n 1 "ps aux | grep TaxBaik"' ### 정기적 검사 ```bash -# 일일 체크 (cron job) -0 9 * * * /home/kjh2064/health-check.sh - -# 내용: -#!/bin/bash -curl -f http://127.0.0.1:5001/taxbaik || systemctl restart taxbaik -curl -f http://127.0.0.1:5001/taxbaik/admin/login || systemctl restart taxbaik +# 일일 체크는 CI 배포 후 자동 검증으로 대체 ``` --- @@ -268,11 +240,6 @@ git commit -m "기능: 새로운 기능 추가" git push origin master # 2. Gitea Actions가 자동으로 배포 -# 또는 수동 배포: -TIMESTAMP=$(date +%Y%m%d_%H%M%S) -dotnet publish TaxBaik.Web -c Release -o ./publish -rsync -avz ./publish/ kjh2064@178.104.200.7:~/deployments/taxbaik_${TIMESTAMP}/ -ssh kjh2064@178.104.200.7 "ln -sfn ~/deployments/taxbaik_${TIMESTAMP} ~/taxbaik_active && sudo systemctl restart taxbaik" ``` ### 롤백 절차 @@ -284,6 +251,7 @@ ssh kjh2064@178.104.200.7 'ls -la ~/deployments/ | grep taxbaik' # 롤백 (예: 이전 버전이 taxbaik_20260625_100000) ssh kjh2064@178.104.200.7 << EOF ln -sfn ~/deployments/taxbaik_20260625_100000 ~/taxbaik_active +sudo systemctl restart taxbaik-proxy sudo systemctl restart taxbaik EOF ``` diff --git a/README.md b/README.md index 4e99196..27b7ece 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ master 브랜치에 푸시하면 파이프라인이 다음 단계를 수행합 - `TAXBAIK_ADMIN_TEST_PASSWORD`: 배포 검증용 관리자 비밀번호 - `Admin__PasswordResetToken`: 관리자 비밀번호 재설정 API용 서버 비밀값 -수동 배포는 비상 롤백 절차 외에는 사용하지 않습니다. 실패 시 [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md)의 CI 점검 절차를 따릅니다. +배포는 Gitea Actions CI/CD로만 수행합니다. 수동 배포 경로는 CI 하네스로 차단되어 있으며, 실패 시 [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md)의 CI 점검 절차를 따릅니다. --- diff --git a/TaxBaik.Web/Components/Admin/App.razor b/TaxBaik.Web/Components/Admin/App.razor index 7cca9f5..2749041 100644 --- a/TaxBaik.Web/Components/Admin/App.razor +++ b/TaxBaik.Web/Components/Admin/App.razor @@ -6,6 +6,8 @@ 백원숙 세무회계 - 관리자 + + diff --git a/TaxBaik.Web/Pages/_Layout.cshtml b/TaxBaik.Web/Pages/_Layout.cshtml index 51b53b0..acea753 100644 --- a/TaxBaik.Web/Pages/_Layout.cshtml +++ b/TaxBaik.Web/Pages/_Layout.cshtml @@ -26,6 +26,8 @@ + + diff --git a/TaxBaik.Web/Program.cs b/TaxBaik.Web/Program.cs index cfc0922..ee03f21 100644 --- a/TaxBaik.Web/Program.cs +++ b/TaxBaik.Web/Program.cs @@ -210,58 +210,58 @@ var apiBaseUrl = builder.Configuration["ApiClient:BaseUrl"] builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); // Phase 5: Tax Accounting & CRM Browser Clients builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(apiBaseUrl); -}); +}).AddHttpMessageHandler(); // UI & 캐시 (MudBlazor Theme Customization) builder.Services.AddMudServices(config => @@ -315,6 +315,20 @@ app.UseForwardedHeaders(new ForwardedHeadersOptions ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); +app.Use(async (context, next) => +{ + var path = context.Request.Path.Value ?? string.Empty; + if (path.Equals("/favicon.ico", StringComparison.OrdinalIgnoreCase) || + path.Equals("/taxbaik/favicon.ico", StringComparison.OrdinalIgnoreCase)) + { + context.Response.ContentType = "image/svg+xml"; + await context.Response.SendFileAsync(Path.Combine(app.Environment.WebRootPath ?? "wwwroot", "favicon.svg")); + return; + } + + await next(); +}); + // Run migrations on startup (non-blocking for development) try { diff --git a/TaxBaik.Web/Services/TelegramReportBackgroundService.cs b/TaxBaik.Web/Services/TelegramReportBackgroundService.cs index ac838be..05e088e 100644 --- a/TaxBaik.Web/Services/TelegramReportBackgroundService.cs +++ b/TaxBaik.Web/Services/TelegramReportBackgroundService.cs @@ -15,18 +15,29 @@ public class TelegramReportBackgroundService( { using var timer = new PeriodicTimer(TimeSpan.FromMinutes(30)); - while (await timer.WaitForNextTickAsync(stoppingToken)) + try { - try + while (await timer.WaitForNextTickAsync(stoppingToken)) { - var now = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, KoreaTimeZone); - await TrySendReportsAsync(now, stoppingToken); - } - catch (Exception ex) - { - logger.LogError(ex, "Telegram report background loop failed"); + try + { + var now = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, KoreaTimeZone); + await TrySendReportsAsync(now, stoppingToken); + } + catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) + { + break; + } + catch (Exception ex) + { + logger.LogError(ex, "Telegram report background loop failed"); + } } } + catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) + { + // Normal shutdown path. + } } private async Task TrySendReportsAsync(DateTimeOffset nowKst, CancellationToken ct) diff --git a/TaxBaik.Web/wwwroot/favicon.svg b/TaxBaik.Web/wwwroot/favicon.svg new file mode 100644 index 0000000..f289443 --- /dev/null +++ b/TaxBaik.Web/wwwroot/favicon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/TaxBaik.Web/wwwroot/js/admin-session.js b/TaxBaik.Web/wwwroot/js/admin-session.js index 5f406ab..26e6659 100644 --- a/TaxBaik.Web/wwwroot/js/admin-session.js +++ b/TaxBaik.Web/wwwroot/js/admin-session.js @@ -11,6 +11,9 @@ window.taxbaikAdminSession = { clearAuthToken: function () { try { + localStorage.removeItem('accessToken'); + localStorage.removeItem('refreshToken'); + localStorage.removeItem('tokenExpiry'); localStorage.removeItem('auth_token'); } catch { // Ignore storage errors; redirect still recovers the session. diff --git a/deploy.sh b/deploy.sh index 3caf8c0..b67ccda 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,11 @@ #!/bin/bash set -e +if [ "${TAXBAIK_DEPLOY_FROM_CI:-}" != "1" ]; then + echo "❌ This deployment script may only be run from CI." >&2 + exit 1 +fi + DEPLOY_HOME="/home/kjh2064" WEB_TIMESTAMP=$(date +%Y%m%d_%H%M%S) diff --git a/deploy/taxbaik-proxy.service b/deploy/taxbaik-proxy.service new file mode 100644 index 0000000..50463b6 --- /dev/null +++ b/deploy/taxbaik-proxy.service @@ -0,0 +1,22 @@ +[Unit] +Description=TaxBaik Local TCP Proxy (5001 -> active blue/green port) +After=network.target + +[Service] +Type=simple +User=kjh2064 +WorkingDirectory=/home/kjh2064/taxbaik_active +ExecStart=/usr/bin/dotnet TaxBaik.Proxy.dll +Restart=always +RestartSec=3 + +# Proxy는 백엔드 포트(5003/5004) 전환 중에도 살아 있어야 한다. +TimeoutStopSec=15 +KillMode=mixed +KillSignal=SIGTERM + +SyslogIdentifier=taxbaik-proxy +Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false + +[Install] +WantedBy=multi-user.target diff --git a/deploy/taxbaik.service b/deploy/taxbaik.service index 135d535..d4cd116 100644 --- a/deploy/taxbaik.service +++ b/deploy/taxbaik.service @@ -1,5 +1,5 @@ [Unit] -Description=TaxBaik Website and Admin (.NET 10) +Description=TaxBaik Backend App (.NET 10) After=network.target [Service] @@ -17,7 +17,7 @@ KillSignal=SIGTERM SyslogIdentifier=taxbaik Environment=ASPNETCORE_ENVIRONMENT=Production -Environment=ASPNETCORE_URLS=http://127.0.0.1:5001 +Environment=ASPNETCORE_URLS=http://127.0.0.1:5004 Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false # 아래 줄은 서버에서 직접 편집 (git에 커밋하지 않음) # Environment=ConnectionStrings__Default=Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=CHANGE_ME diff --git a/deploy_gb.sh b/deploy_gb.sh index 0869a84..562eb23 100644 --- a/deploy_gb.sh +++ b/deploy_gb.sh @@ -7,6 +7,37 @@ TIMESTAMP=$(date +%Y%m%d_%H%M%S) echo "===== 🚀 TaxBaik Green/Blue Deployment Script =====" +if [ "${TAXBAIK_DEPLOY_FROM_CI:-}" != "1" ]; then + echo "❌ This deployment script may only be run from CI." >&2 + exit 1 +fi + +if [ ! -s "$DEPLOY_HOME/taxbaik_active/appsettings.Production.json" ]; then + echo "❌ Missing production settings: $DEPLOY_HOME/taxbaik_active/appsettings.Production.json" >&2 + exit 1 +fi + +if [ ! -s "$DEPLOY_HOME/taxbaik_active/proxy/TaxBaik.Proxy.dll" ]; then + echo "❌ Missing proxy artifact: $DEPLOY_HOME/taxbaik_active/proxy/TaxBaik.Proxy.dll" >&2 + exit 1 +fi + +# 0. Ensure the local TCP proxy exists and is running. +# Nginx and external traffic always enter through 127.0.0.1:5001. +if ! ss -tln | grep -q ':5001 '; then + if [ -f "$DEPLOY_HOME/taxbaik_active/proxy/TaxBaik.Proxy.dll" ]; then + echo "=== Starting proxy on 127.0.0.1:5001 ===" + cd "$DEPLOY_HOME/taxbaik_active/proxy" + nohup /usr/bin/dotnet TaxBaik.Proxy.dll > "$DEPLOY_HOME/taxbaik_proxy.log" 2>&1 & + sleep 2 + fi +fi + +if ! ss -tln | grep -q ':5001 '; then + echo "❌ Proxy on 127.0.0.1:5001 is not running. Abort deploy." >&2 + exit 1 +fi + # 1. Determine active port ACTIVE_PORT=5003 if [ -f "$PORT_FILE" ]; then