From 502f06dd87f8f70ddbe3818b7d60cd8b5a0bfb23 Mon Sep 17 00:00:00 2001 From: happybell80 Date: Mon, 25 Aug 2025 20:54:28 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20Gmail=20=ED=86=A0=ED=81=B0=20NULL?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EC=9D=BC=EC=9D=BC=20=EB=B8=8C?= =?UTF-8?q?=EB=A6=AC=ED=95=91=20=EC=8B=A4=ED=8C=A8=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?=EA=B0=80=EC=9D=B4=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 매일 9시 브리핑 이메일 수집 실패 문제 (3명 사용자 영향) - 모든 사용자 token_data=NULL 상태 분석 - 단계별 해결 방안 제시: - 즉시: 관리자 대신 OAuth 인증 - 중기: 사용자 재인증 유도 - 장기: 자동 토큰 갱신 구현 - 1시간 내 실행 가능한 상세 가이드 포함 --- ...825_gmail_token_null_daily_briefing_fix.md | 313 ++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 troubleshooting/250825_gmail_token_null_daily_briefing_fix.md diff --git a/troubleshooting/250825_gmail_token_null_daily_briefing_fix.md b/troubleshooting/250825_gmail_token_null_daily_briefing_fix.md new file mode 100644 index 0000000..afe408f --- /dev/null +++ b/troubleshooting/250825_gmail_token_null_daily_briefing_fix.md @@ -0,0 +1,313 @@ +# Gmail 토큰 NULL로 인한 일일 브리핑 실패 해결 가이드 + +## 작성일: 2025-08-25 +## 작성자: 서버 관리자 +## 상태: 해결 진행중 +## 영향: 매일 9시 브리핑 이메일 수집 실패 (사용자 3명) + +--- + +## 1. 문제 상황 + +### 1.1 증상 +- **발생 시간**: 매일 오전 9시 (KST) +- **영향 사용자**: + - happybell80 (goeun2dc@gmail.com) + - 0914eagle (0914eagle@gmail.com) + - cdctfm (cdctfm@gmail.com) +- **현상**: 이메일 요약 없이 뉴스만 포함된 불완전한 브리핑 전송 + +### 1.2 근본 원인 +```sql +-- 현재 DB 상태: 모든 사용자 token_data가 NULL +SELECT username, has_token, is_equipped +FROM gmail_tokens; + +결과: +happybell80 | f (NULL) | t (장착됨) -- 모순 상태 +0914eagle | f (NULL) | t (장착됨) -- 모순 상태 +cdctfm | f (NULL) | t (장착됨) -- 모순 상태 +``` + +**문제**: is_equipped=true이지만 실제 token_data=NULL + +--- + +## 2. 시스템 플로우 분석 + +### 2.1 일일 브리핑 플로우 +```mermaid +sequenceDiagram + participant Cron as Gateway Cron
(매일 9시) + participant RB as rb8001
(51124:8001) + participant Email as skill-email
(51124:8501) + participant DB as PostgreSQL
(main_db) + participant Slack as Slack API + + Cron->>RB: POST /api/cron/daily-summary + RB->>Email: GET /messages?user_id={slack_id} + Email->>DB: SELECT token_data FROM gmail_tokens + DB-->>Email: token_data: NULL ❌ + Email-->>RB: 500 Internal Server Error + RB->>Slack: 불완전한 브리핑 전송
(이메일 없이 뉴스만) +``` + +### 2.2 Gmail OAuth 플로우 (재인증 필요) +```mermaid +sequenceDiagram + participant User as 사용자 + participant Front as 프론트엔드 + participant Auth as auth-server
(51123:9000) + participant Google as Google OAuth + participant DB as PostgreSQL + + User->>Front: Gmail 연결 클릭 + Front->>Auth: GET /api/gmail/auth?user_id={uuid} + Auth->>Auth: OAuth URL 생성 + Auth-->>Front: Redirect to Google + Front->>Google: 사용자 리다이렉트 + User->>Google: 권한 승인 + Google->>Auth: Callback with code + Auth->>Google: Exchange code for tokens + Google-->>Auth: access_token, refresh_token + Auth->>DB: UPDATE gmail_tokens
SET token_data = {...} + Auth-->>Front: 인증 완료 +``` + +--- + +## 3. 해결 방안 + +### 3.1 단기 해결책 (즉시 적용) + +#### 방법 1: 관리자 대신 인증 (추천) +```bash +# 1. 테스트 계정으로 OAuth 인증 URL 생성 +curl -X GET "http://localhost:9000/api/gmail/auth?user_id=1e16e9d5-59f3-54da-a661-8abeabff4230" + +# 2. 반환된 URL을 브라우저에서 열어 Google 로그인 +# 3. 권한 승인 후 콜백 확인 + +# 4. 토큰 저장 확인 +export PGPASSWORD=robeings +psql -h localhost -U robeings -d main_db -c \ + "SELECT user_id, token_data IS NOT NULL FROM gmail_tokens WHERE user_id = '1e16e9d5-59f3-54da-a661-8abeabff4230';" + +# 5. 브리핑 수동 테스트 +curl -X POST http://192.168.219.52:8001/api/cron/daily-summary \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer cron-secret-2024" +``` + +#### 방법 2: 임시 토큰 주입 (비추천, 테스트용) +```sql +-- 주의: 실제 유효한 토큰이 필요함 +UPDATE gmail_tokens +SET token_data = '{ + "access_token": "실제_액세스_토큰", + "refresh_token": "실제_리프레시_토큰", + "token_type": "Bearer", + "expires_in": 3599 +}'::jsonb, +expiry = NOW() + INTERVAL '1 hour' +WHERE user_id = '1e16e9d5-59f3-54da-a661-8abeabff4230'; +``` + +### 3.2 중기 해결책 (사용자 재인증 유도) + +#### 프론트엔드에 알림 추가 +```javascript +// 로그인 시 Gmail 토큰 체크 +useEffect(() => { + checkGmailToken().then(hasToken => { + if (!hasToken) { + showNotification({ + title: "Gmail 재인증 필요", + message: "일일 브리핑을 위해 Gmail을 다시 연결해주세요", + action: "연결하기", + onClick: () => window.location.href = '/settings/gmail' + }); + } + }); +}, []); +``` + +#### Slack 메시지로 재인증 안내 +```python +# rb8001의 dm_skill.py에 추가 +async def notify_reauth_needed(user_slack_id: str): + await slack_client.chat_postMessage( + channel=user_slack_id, + text="🔔 Gmail 재인증이 필요합니다", + blocks=[{ + "type": "section", + "text": { + "type": "mrkdwn", + "text": "일일 브리핑을 위해 Gmail을 다시 연결해주세요:\nhttps://ro-being.com/settings/gmail" + } + }] + ) +``` + +### 3.3 장기 해결책 (자동 복구) + +#### 토큰 자동 갱신 구현 +```python +# skill-email 서비스에 추가 +async def auto_refresh_token(user_id: str): + """만료된 토큰 자동 갱신""" + try: + # 1. refresh_token으로 새 access_token 획득 + response = await httpx.post( + f"http://auth-server:9000/api/gmail/refresh/{user_id}" + ) + + if response.status_code == 200: + logger.info(f"Token refreshed for {user_id}") + return True + elif response.status_code == 401: + # refresh_token도 만료 → 재인증 필요 + await notify_reauth_needed(user_id) + return False + except Exception as e: + logger.error(f"Token refresh failed: {e}") + return False +``` + +--- + +## 4. 실행 단계별 가이드 + +### Step 1: 현재 상태 확인 (5분) +```bash +# 1. 토큰 상태 확인 +export PGPASSWORD=robeings +psql -h localhost -U robeings -d main_db -c \ + "SELECT u.username, gt.token_data IS NOT NULL as has_token, + gt.is_equipped, gt.metadata->>'email' as email + FROM users u + LEFT JOIN gmail_tokens gt ON u.id = gt.user_id + WHERE u.username IN ('happybell80', '0914eagle', 'cdctfm');" + +# 2. 크론잡 확인 +docker exec robeing-gateway crontab -l | grep daily + +# 3. 최근 실패 로그 확인 +ssh admin@192.168.219.52 "docker logs rb8001 --tail 100 | grep -E 'daily-summary|gmail|500'" 2>/dev/null || echo "직접 확인 필요" +``` + +### Step 2: 테스트 계정 인증 (15분) +```bash +# 1. OAuth URL 생성 (happybell80 계정) +curl -s "http://localhost:9000/api/gmail/auth?user_id=1e16e9d5-59f3-54da-a661-8abeabff4230" | jq -r '.auth_url' + +# 2. 브라우저에서 URL 열고 Google 계정으로 로그인 +# 3. 권한 승인 (이메일 읽기, 쓰기, 수정) + +# 4. 인증 성공 확인 +psql -h localhost -U robeings -d main_db -c \ + "SELECT token_data->'access_token' IS NOT NULL as success + FROM gmail_tokens + WHERE user_id = '1e16e9d5-59f3-54da-a661-8abeabff4230';" +``` + +### Step 3: 브리핑 테스트 (10분) +```bash +# 1. 수동 브리핑 실행 +curl -X POST http://192.168.219.52:8001/api/cron/daily-summary \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer cron-secret-2024" + +# 2. 실시간 로그 모니터링 +docker logs robeing-gateway -f 2>&1 | grep -E "daily|gmail|email" + +# 3. Slack DM 확인 +# happybell80 사용자의 Slack DM에 브리핑이 도착했는지 확인 +``` + +### Step 4: 다른 사용자 처리 (20분) +```bash +# 0914eagle 계정 인증 +curl -s "http://localhost:9000/api/gmail/auth?user_id=b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0" | jq -r '.auth_url' + +# cdctfm 계정 인증 +curl -s "http://localhost:9000/api/gmail/auth?user_id=69ae4ea9-a15f-5110-9f5d-6568e380fcfb" | jq -r '.auth_url' +``` + +### Step 5: 모니터링 설정 (10분) +```bash +# 매일 8시 50분 토큰 상태 체크 크론 추가 +cat << 'EOF' > /tmp/check_gmail_tokens.sh +#!/bin/bash +PGPASSWORD=robeings psql -h localhost -U robeings -d main_db -c \ + "SELECT username, token_data IS NOT NULL as has_token + FROM users u + JOIN gmail_tokens gt ON u.id = gt.user_id;" | \ +mail -s "Gmail Token Status" admin@ro-being.com +EOF + +chmod +x /tmp/check_gmail_tokens.sh + +# 크론잡 등록 (선택사항) +echo "50 8 * * * /tmp/check_gmail_tokens.sh" | crontab -l | crontab - +``` + +--- + +## 5. 검증 체크리스트 + +- [ ] 모든 사용자의 token_data NOT NULL 확인 +- [ ] 수동 브리핑 실행 시 이메일 포함 확인 +- [ ] Slack DM으로 완전한 브리핑 수신 확인 +- [ ] 내일 9시 자동 실행 모니터링 예약 + +--- + +## 6. 관련 파일 및 참고 문서 + +### 핵심 파일 +- **auth-server**: `/home/admin/auth-server/app/providers/gmail_passport.py` +- **게이트웨이 크론**: `/home/admin/robeing-gateway/app/crontab.py` +- **rb8001 브리핑**: `rb8001/app/skills/dm_skill.py:384` (51124 서버) +- **skill-email**: `skill-email/main.py` (51124 서버) + +### 참고 문서 +- [일일 브리핑 시퀀스 다이어그램](/home/admin/DOCS/300_architecture/sequences/daily_briefing_sequences.md) +- [Gmail 토큰 자동 갱신](/home/admin/DOCS/troubleshooting/250821_gmail_token_auto_refresh.md) +- [rb8001 크론 실패 분석](/home/admin/DOCS/troubleshooting/250824_rb8001_daily_summary_cron_failure.md) + +--- + +## 7. 주의사항 + +### ⚠️ 보안 관련 +- 실제 토큰을 로그에 출력하지 말 것 +- refresh_token은 절대 노출하지 말 것 +- 테스트 후 불필요한 토큰은 삭제 + +### ⚠️ 운영 관련 +- 프로덕션 사용자의 토큰 직접 수정 지양 +- 사용자에게 재인증 안내 우선 +- 크론 실행 시간(9시) 피해서 테스트 + +--- + +## 8. 문제 재발 방지 + +### 단기 조치 +1. 토큰 만료 알림 시스템 구축 +2. 관리자 대시보드에 토큰 상태 표시 + +### 장기 조치 +1. refresh_token 자동 갱신 로직 구현 +2. 토큰 만료 7일 전 사용자 알림 +3. 프론트엔드 Gmail 연결 상태 표시 개선 + +--- + +## 예상 소요 시간: 1시간 + +- Step 1-2: 20분 (기본 인증) +- Step 3: 10분 (테스트) +- Step 4: 20분 (추가 사용자) +- Step 5: 10분 (모니터링) \ No newline at end of file