From a77adaf3654030f9f70d7a50d91471cff7d73d45 Mon Sep 17 00:00:00 2001 From: 0914eagle <0914eagle@gmail.com> Date: Mon, 25 Aug 2025 23:59:33 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0=20=EC=A0=84=EC=97=90=20refresh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gmail-token-slack-id-migration.md | 200 ++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 gmail-token-slack-id-migration.md diff --git a/gmail-token-slack-id-migration.md b/gmail-token-slack-id-migration.md new file mode 100644 index 0000000..dc53915 --- /dev/null +++ b/gmail-token-slack-id-migration.md @@ -0,0 +1,200 @@ +# Gmail Token 시스템 Slack ID 직접 사용 마이그레이션 + +## 개요 +2025년 8월 25일, Gmail 토큰 조회 시스템을 UUID 변환 방식에서 Slack ID 직접 사용 방식으로 마이그레이션했습니다. + +## 문제 상황 + +### 기존 시스템의 문제점 +1. **UUID 불일치 문제** + - auth-server: `uuid.uuid4()` (랜덤 UUID) 사용 + - skill-email/rb8001: `uuid.uuid5(namespace, slack_id)` (네임스페이스 기반 UUID) 사용 + - 결과: 동일한 사용자에 대해 서로 다른 UUID가 생성되어 토큰 조회 실패 + +2. **데이터베이스 스키마 변경** + - 기존: `token_data` JSON 컬럼에 모든 토큰 정보 저장 + - 변경: 개별 컬럼으로 분리 (`access_token`, `refresh_token`, `oauth_config`, `scopes`, `expiry`) + - 새 컬럼 추가: `slack_id` (Slack 사용자 ID 직접 저장) + +## 해결 방안 + +### 1. Slack ID 직접 사용 +UUID 변환을 완전히 제거하고 Slack ID를 직접 사용하도록 변경 + +### 2. 변경된 파일 + +#### `/home/heejae/skill-email/services/db_credentials_provider.py` + +**제거된 코드:** +```python +import uuid + +def __init__(self, connection_string: str): + # ... + self.uuid_namespace = uuid.NAMESPACE_DNS # 제거됨 + +def _convert_to_uuid(self, user_id: str) -> str: # 메서드 전체 제거 + """Slack user_id를 UUID로 변환""" + # ... +``` + +**변경된 쿼리:** +```python +# 이전 +user_uuid = self._convert_to_uuid(user_id) +cur.execute(""" + SELECT ... FROM gmail_tokens + WHERE user_id = %s AND is_equipped = true +""", (user_uuid,)) + +# 현재 +cur.execute(""" + SELECT ... FROM gmail_tokens + WHERE slack_id = %s AND is_equipped = true +""", (user_id,)) # slack_id 직접 사용 +``` + +#### `/home/heejae/skill-email/services/gmail_service.py` + +**추가된 자동 토큰 갱신:** +```python +def _get_gmail_service(self, user_id: str): + # ... + # 토큰이 만료되었거나 곧 만료될 경우 자동 refresh + if creds and creds.expired and creds.refresh_token: + logger.info(f"Refreshing expired token for user: {user_id}") + from google.auth.transport.requests import Request + creds.refresh(Request()) + + # 갱신된 토큰 저장 + if hasattr(self.creds_provider, 'save_credentials'): + self.creds_provider.save_credentials(user_id, creds) + logger.info(f"Refreshed token saved for user: {user_id}") +``` + +**401 에러 시 재시도 로직:** +```python +except HttpError as e: + if e.resp.status == 401: + logger.info(f"Got 401 error, refreshing token for user: {user_id}") + # 캐시 삭제 후 재시도 + if user_id in self._service_cache: + del self._service_cache[user_id] + # 토큰 refresh 포함하여 다시 시도 +``` + +### 3. 데이터베이스 스키마 + +**gmail_tokens 테이블 구조:** +```sql +CREATE TABLE gmail_tokens ( + id UUID PRIMARY KEY, + slack_id VARCHAR(20) NOT NULL, -- Slack 사용자 ID 직접 저장 + email VARCHAR(255), + access_token TEXT, + refresh_token TEXT, + oauth_config JSONB, + scopes JSONB, + expiry TIMESTAMP, + is_equipped BOOLEAN DEFAULT false, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 인덱스 +CREATE INDEX idx_gmail_tokens_slack_id ON gmail_tokens(slack_id); +CREATE INDEX idx_gmail_tokens_equipped ON gmail_tokens(slack_id, is_equipped); +``` + +## 영향 받는 서비스 + +1. **skill-email** (포트 8501) + - db_credentials_provider.py 수정 + - gmail_service.py 자동 refresh 로직 추가 + +2. **rb8001** (포트 8001) + - dm_skill.py의 일일 요약 기능 + - Gmail 이메일 조회 시 skill-email API 사용 + +3. **auth-server** (포트 8088) + - 변경 없음 (이미 slack_id 저장 중) + +4. **robeing-monitor** (포트 9024) + - 이미 수정 완료 (새 컬럼 구조 읽기) + +## 테스트 및 검증 + +### 1. 이메일 조회 테스트 +```bash +# 개별 사용자 이메일 조회 +curl -X GET "http://localhost:8501/messages?user_id=U091UNVE41M&limit=5&query=category:primary" +``` + +### 2. 일일 요약 DM 테스트 +```bash +# Cron API 호출로 전체 사용자에게 DM 전송 +curl -X POST "http://localhost:8001/api/cron/daily-summary" \ + -H "Authorization: Bearer cron-secret-2024" \ + -H "Content-Type: application/json" +``` + +### 3. 토큰 자동 갱신 확인 +```bash +# skill-email 로그에서 자동 refresh 확인 +docker logs skill-email | grep "Refreshing" +``` + +## 주요 개선 사항 + +1. **UUID 불일치 문제 해결** + - 모든 서비스가 동일한 식별자(slack_id) 사용 + - UUID 변환 과정 제거로 오류 가능성 감소 + +2. **자동 토큰 갱신** + - 만료된 토큰 자동 감지 및 갱신 + - 401 에러 시 자동 재시도 + - 갱신된 토큰 DB 자동 저장 + +3. **코드 단순화** + - UUID 변환 로직 제거 + - 직관적인 slack_id 사용 + - 디버깅 용이성 향상 + +## 배포 절차 + +```bash +# 1. skill-email 재빌드 및 재시작 +cd /home/heejae/skill-email +docker compose down && docker compose up -d --build + +# 2. 로그 확인 +docker logs skill-email --tail 50 + +# 3. 헬스체크 +curl http://localhost:8501/health +``` + +## 트러블슈팅 + +### SSL SYSCALL error: EOF detected +- 원인: PostgreSQL 연결 풀 타임아웃 +- 해결: 재시도 시 정상 작동 (연결 풀 자동 재생성) + +### 토큰 조회 실패 +- 확인사항: + 1. `slack_id` 컬럼에 올바른 Slack 사용자 ID 저장 여부 + 2. `is_equipped = true` 설정 여부 + 3. SSH 터널 연결 상태 (포트 5433) + +### 자동 갱신 실패 +- 확인사항: + 1. `refresh_token` 존재 여부 + 2. Google OAuth 클라이언트 ID/Secret 설정 + 3. 네트워크 연결 상태 + +## 참고 사항 + +- SSH 터널: `localhost:5433 → 124.55.18.179:5432` +- 데이터베이스: `main_db` +- 환경 변수: `/home/heejae/skill-email/.env` +- Docker 네트워크: `network_mode: host` \ No newline at end of file