From 80613a02bfde69a827b668b5b74296ca5fc8ab71 Mon Sep 17 00:00:00 2001 From: 0914eagle <0914eagle@gmail.com> Date: Tue, 19 Aug 2025 18:13:35 +0900 Subject: [PATCH] =?UTF-8?q?Email=20=EA=B4=80=EB=A0=A8=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plans/250819_gmail_item_detailed_tasks.md | 61 ++-- .../250819_gmail_item_implementation_plan.md | 9 +- ...819_skill_email_db_connection_completed.md | 340 ++++++++++++++++++ 3 files changed, 376 insertions(+), 34 deletions(-) create mode 100644 plans/completed/250819_skill_email_db_connection_completed.md diff --git a/plans/250819_gmail_item_detailed_tasks.md b/plans/250819_gmail_item_detailed_tasks.md index e404cb3..de4f20c 100644 --- a/plans/250819_gmail_item_detailed_tasks.md +++ b/plans/250819_gmail_item_detailed_tasks.md @@ -95,42 +95,43 @@ ## 3. skill-email DB 연결 (희재) ### 3.1 DB 연결 설정 -- [ ] psycopg2 의존성 추가 -- [ ] .env 파일 수정 - - [ ] TOKEN_PROVIDER=database - - [ ] POSTGRES_CONNECTION_STRING 설정 -- [ ] 데이터베이스 연결 테스트 스크립트 작성 -- [ ] 연결 확인 +- [x] psycopg2 의존성 추가 +- [x] .env 파일 수정 + - [x] TOKEN_PROVIDER=database + - [x] POSTGRES_CONNECTION_STRING 설정 +- [x] 데이터베이스 연결 테스트 스크립트 작성 +- [x] 연결 확인 ### 3.2 DBCredentialsProvider 구현 -- [ ] services/db_credentials_provider.py 생성 -- [ ] __init__ 메서드 구현 - - [ ] DB 연결 초기화 - - [ ] 연결 풀 설정 -- [ ] get_credentials 메서드 구현 - - [ ] SQL 쿼리 작성 - - [ ] 토큰 조회 로직 - - [ ] JSON 파싱 - - [ ] Credentials 객체 생성 -- [ ] refresh_token 메서드 구현 - - [ ] 토큰 갱신 로직 - - [ ] DB 업데이트 -- [ ] 에러 처리 추가 - - [ ] 토큰 없음 처리 - - [ ] DB 연결 실패 처리 - - [ ] 파싱 에러 처리 +- [x] services/db_credentials_provider.py 생성 +- [x] __init__ 메서드 구현 + - [x] DB 연결 초기화 + - [x] 연결 풀 설정 +- [x] get_credentials 메서드 구현 + - [x] SQL 쿼리 작성 + - [x] 토큰 조회 로직 + - [x] JSON 파싱 + - [x] Credentials 객체 생성 + - [x] oauth_config에서 client_id/secret 가져오기 +- [x] save_credentials 메서드 구현 (토큰 업데이트용) + - [x] 토큰 갱신 로직 + - [x] DB 업데이트 +- [x] 에러 처리 추가 + - [x] 토큰 없음 처리 + - [x] DB 연결 실패 처리 + - [x] 파싱 에러 처리 ### 3.3 기존 코드 수정 -- [ ] main.py 수정 - - [ ] FileCredentialsProvider import 제거 - - [ ] DBCredentialsProvider import 추가 - - [ ] Provider 초기화 코드 변경 -- [ ] 환경변수 체크 로직 수정 -- [ ] 테스트 코드 작성 -- [ ] 로컬 테스트 +- [x] main.py 수정 + - [x] FileCredentialsProvider 유지 (조건부 사용) + - [x] DBCredentialsProvider import 추가 + - [x] Provider 초기화 코드 변경 (TOKEN_PROVIDER 환경변수 기반) +- [x] 환경변수 체크 로직 수정 +- [x] 테스트 코드 작성 +- [x] 로컬 테스트 ### 3.4 배포 준비 -- [ ] requirements.txt 업데이트 +- [x] requirements.txt 업데이트 - [ ] Dockerfile 확인 - [ ] 빌드 테스트 - [ ] 커밋 및 푸시 diff --git a/plans/250819_gmail_item_implementation_plan.md b/plans/250819_gmail_item_implementation_plan.md index ae4d90b..ad85529 100644 --- a/plans/250819_gmail_item_implementation_plan.md +++ b/plans/250819_gmail_item_implementation_plan.md @@ -282,14 +282,15 @@ CREATE TABLE gmail_audit_logs ( ## 6. 구현 단계 ### Phase 1: 백엔드 기초 -- [ ] 필요 테이블 생성 +- [x] 필요 테이블 생성 - robeing_stats 테이블 (레벨 관리) - gmail_audit_logs 테이블 (감사 로그) - gmail_tokens에 is_equipped, equipped_to 컬럼 추가 -- [ ] skill-email DB 연결 코드 작성 +- [x] skill-email DB 연결 코드 작성 (희재) - FileCredentialsProvider → DBCredentialsProvider 전환 - - PostgreSQL 연결 설정 추가 (192.168.219.45) -- [ ] robeing-monitor 서비스 구축 + - PostgreSQL 연결 설정 추가 (localhost:5433 SSH 터널) + - oauth_config 컬럼에서 client_id/secret 가져오기 구현 +- [x] robeing-monitor 서비스 구축 - 기존 robeing-state-service 확장 - 포트 9024로 변경 - 아이템 관리 API 추가 diff --git a/plans/completed/250819_skill_email_db_connection_completed.md b/plans/completed/250819_skill_email_db_connection_completed.md new file mode 100644 index 0000000..a781d88 --- /dev/null +++ b/plans/completed/250819_skill_email_db_connection_completed.md @@ -0,0 +1,340 @@ +# skill-email DB 연결 구현 완료 보고서 + +## 작업일: 2025-08-19 +## 작업자: 희재 +## 상태: 완료 + +--- + +## 1. 작업 개요 + +### 목적 +skill-email 서비스가 Gmail OAuth 토큰을 파일이 아닌 PostgreSQL 데이터베이스에서 직접 조회하도록 변경 + +### 배경 +- 기존: 파일 기반 토큰 관리 (FileCredentialsProvider) +- 변경: PostgreSQL gmail_tokens 테이블 기반 관리 (DBCredentialsProvider) +- 이유: 중앙집중식 토큰 관리, 보안 강화, 다중 서비스 통합 + +--- + +## 2. 구현 내용 + +### 2.1 의존성 추가 +**파일**: `/home/heejae/skill-email/requirements.txt` +```python +psycopg2-binary>=2.9.9 # PostgreSQL 연결용 +``` + +### 2.2 환경 설정 +**파일**: `/home/heejae/skill-email/.env` +```bash +# Skill Email Configuration +PORT=8501 + +# Token Provider Configuration +TOKEN_PROVIDER=database + +# PostgreSQL Connection (SSH tunnel) +POSTGRES_CONNECTION_STRING=postgresql://robeings:robeings@localhost:5433/auth_db + +# Service Name +SERVICE_NAME=skill-email + +# Log Level +LOG_LEVEL=INFO +``` + +### 2.3 DBCredentialsProvider 구현 +**파일**: `/home/heejae/skill-email/services/db_credentials_provider.py` + +#### 주요 기능 +1. **연결 풀 관리** + - SimpleConnectionPool 사용 (최소 1개, 최대 5개 연결) + - 연결 재사용으로 성능 최적화 + +2. **get_credentials() 메서드** + ```python + def get_credentials(self, user_id: str) -> Optional[Credentials]: + # gmail_tokens 테이블에서 조회 + # is_equipped=true인 토큰만 조회 + # token_data + oauth_config 컬럼 모두 활용 + ``` + + - gmail_tokens 테이블 조회 + - token_data 컬럼: access_token, refresh_token + - oauth_config 컬럼: client_id, client_secret, token_uri + - Google Credentials 객체로 변환 + +3. **save_credentials() 메서드** + ```python + def save_credentials(self, user_id: str, creds: Credentials) -> bool: + # 갱신된 토큰을 DB에 저장 + # access_token 필드로 저장 (DB 구조 호환) + ``` + +4. **check_token_exists() 메서드** + ```python + def check_token_exists(self, user_id: str) -> bool: + # 사용자의 장착된 토큰 존재 여부 확인 + ``` + +#### 특별 처리 사항 +- **토큰 필드 매핑**: DB의 `access_token` ↔ Credentials의 `token` +- **oauth_config 파싱**: JSON 형식의 client credentials 추출 +- **is_equipped 필터**: 장착된 토큰만 조회 + +### 2.4 main.py 수정 +**파일**: `/home/heejae/skill-email/main.py` + +#### 변경 내용 +```python +# 전역 credentials provider +credentials_provider = None + +def get_gmail_service() -> GmailService: + global credentials_provider + + if credentials_provider is None: + if TOKEN_PROVIDER == "database": + if not POSTGRES_CONNECTION_STRING: + logger.error("POSTGRES_CONNECTION_STRING not set") + raise ValueError("Database connection string required") + logger.info("Using database credentials provider") + credentials_provider = DBCredentialsProvider(POSTGRES_CONNECTION_STRING) + else: + logger.info("Using file credentials provider") + credentials_provider = FileCredentialsProvider(TOKEN_BASE) + + return GmailService(credentials_provider) +``` + +- TOKEN_PROVIDER 환경변수로 File/DB 모드 선택 +- 의존성 주입 패턴 사용 (Depends) +- 기존 FileCredentialsProvider 호환성 유지 + +--- + +## 3. 데이터베이스 구조 + +### gmail_tokens 테이블 +```sql +CREATE TABLE gmail_tokens ( + id SERIAL PRIMARY KEY, + user_id VARCHAR(100), + robeing_id VARCHAR(50), + token_data JSONB, -- access_token, refresh_token + oauth_config JSONB, -- client_id, client_secret, token_uri + scopes TEXT[], + metadata JSONB, + expiry TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + is_equipped BOOLEAN DEFAULT FALSE, + equipped_to VARCHAR(50) +); +``` + +### 데이터 예시 +```json +// token_data +{ + "token_type": "Bearer", + "access_token": "ya29.a0AS3H6NxLyI1Ss1VJ-_WUkpR...", + "refresh_token": "1//0eZrT..." +} + +// oauth_config +{ + "client_id": "1044056803209-0h8n5kcl22rvl740mdgpejp58v27mlro.apps.googleusercontent.com", + "client_secret": "GOCSPX-...", + "token_uri": "https://oauth2.googleapis.com/token" +} +``` + +--- + +## 4. 테스트 결과 + +### 4.1 연결 테스트 +```bash +cd /home/heejae/skill-email && python3 test_db.py +``` +- ✅ PostgreSQL 연결 성공 +- ✅ gmail_tokens 테이블 접근 가능 +- ✅ 3개 토큰 확인 (heejae, test, unknown) + +### 4.2 Provider 테스트 +```bash +cd /home/heejae/skill-email && python3 test_integration.py +``` +- ✅ DBCredentialsProvider 생성 성공 +- ✅ heejae 토큰 로드 성공 + - Access Token: ya29.a0AS3H6NxLyI1Ss1VJ-_WUkpR... + - Refresh Token: 있음 + - Client ID: 1044056803209-0h8n5kcl22rvl740... + - Client Secret: 있음 + - Token URI: https://oauth2.googleapis.com/token + - Scopes: ['https://www.googleapis.com/auth/gmail.send'] + +### 4.3 통합 테스트 스크립트 +**파일**: `/home/heejae/skill-email/test_integration.py` +- DBCredentialsProvider 테스트 +- GmailService 통합 테스트 +- 토큰 존재 여부 확인 + +--- + +## 5. SSH 터널 설정 + +### 연결 구성 +```bash +# SSH 터널 생성 (51123 서버 → 51124 서버) +sshpass -p "19800508" ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123 +``` + +- 로컬 포트 5433 → 원격 PostgreSQL 5432 +- 51123 서버 (124.55.18.179)의 PostgreSQL 접근 +- auth_db 데이터베이스 사용 + +--- + +## 6. 문제 해결 + +### 6.1 토큰 필드 불일치 +- **문제**: DB는 `access_token`, Credentials는 `token` 사용 +- **해결**: + ```python + token=token_data.get('access_token') or token_data.get('token') + ``` + +### 6.2 is_equipped 필터 +- **문제**: 모든 토큰이 is_equipped=false 상태 +- **해결**: + ```sql + UPDATE gmail_tokens SET is_equipped=true WHERE user_id='heejae' + ``` + +### 6.3 oauth_config 누락 +- **문제**: client_id, client_secret이 없어 API 호출 불가 +- **해결**: oauth_config 컬럼에서 추가로 조회 + ```python + cur.execute(""" + SELECT token_data, oauth_config + FROM gmail_tokens + WHERE user_id = %s AND is_equipped = true + """) + ``` + +### 6.4 인코딩 오류 +- **문제**: .env 파일 UTF-8 디코딩 오류 +- **해결**: .env 파일 재작성 + +--- + +## 7. 코드 구조 + +``` +/home/heejae/skill-email/ +├── .env # 환경변수 설정 +├── requirements.txt # 의존성 목록 +├── main.py # FastAPI 앱 (수정됨) +├── services/ +│ ├── gmail_service.py # 기존 Gmail 서비스 +│ ├── db_credentials_provider.py # 새로 추가된 DB Provider +│ └── ... +├── test_db.py # DB 연결 테스트 +├── test_integration.py # 통합 테스트 +└── ... +``` + +--- + +## 8. 보안 고려사항 + +1. **토큰 암호화**: DB에 평문 저장 (추후 암호화 고려) +2. **연결 풀**: 최대 5개 연결로 제한 +3. **에러 처리**: 토큰 정보 로그에 노출 방지 +4. **SSH 터널**: PostgreSQL 직접 노출 방지 + +--- + +## 9. 성능 최적화 + +1. **연결 풀링**: SimpleConnectionPool 사용 +2. **JSON 파싱**: 한 번만 파싱하여 재사용 +3. **로깅 레벨**: INFO로 설정 (프로덕션에서는 WARNING 권장) + +--- + +## 10. 남은 작업 + +### 즉시 필요 +- [ ] 51124 서버 배포 +- [ ] Docker 이미지 빌드 및 테스트 +- [ ] rb8001과 통합 테스트 + +### 추후 개선 +- [ ] 토큰 암호화 저장 +- [ ] 토큰 자동 갱신 로직 +- [ ] 연결 풀 모니터링 +- [ ] 캐싱 레이어 추가 + +--- + +## 11. 호환성 + +### 기존 시스템과 호환 +- ✅ FileCredentialsProvider 유지 (TOKEN_PROVIDER=file) +- ✅ 기존 API 인터페이스 변경 없음 +- ✅ 동일한 GmailService 사용 + +### 새로운 기능 +- ✅ 중앙집중식 토큰 관리 +- ✅ 다중 robeing 지원 (equipped_to 필드) +- ✅ 감사 로그 준비 (gmail_audit_logs 테이블과 연동 가능) + +--- + +## 12. 검증 체크리스트 + +- [x] PostgreSQL 연결 성공 +- [x] gmail_tokens 테이블 조회 +- [x] 토큰 데이터 파싱 +- [x] Credentials 객체 생성 +- [x] oauth_config 통합 +- [x] 에러 처리 동작 +- [x] 연결 풀 정상 작동 +- [x] 환경변수 기반 Provider 선택 +- [ ] 실제 Gmail API 호출 +- [ ] 토큰 갱신 플로우 + +--- + +## 13. 참고 명령어 + +```bash +# DB 연결 테스트 +cd /home/heejae/skill-email && python3 test_db.py + +# Provider 테스트 +cd /home/heejae/skill-email && python3 test_integration.py + +# SSH 터널 재연결 +sshpass -p "19800508" ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123 + +# 토큰 상태 확인 +python3 -c " +import psycopg2 +conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/auth_db') +cur = conn.cursor() +cur.execute('SELECT user_id, is_equipped FROM gmail_tokens') +for row in cur.fetchall(): + print(f'{row[0]}: equipped={row[1]}') +" +``` + +--- + +**작업 완료: 2025-08-19** +**다음 단계: rb8001 통합 (4번 작업)** \ No newline at end of file