Email 관련 업데이트
This commit is contained in:
parent
dc37907546
commit
80613a02bf
@ -95,42 +95,43 @@
|
|||||||
## 3. skill-email DB 연결 (희재)
|
## 3. skill-email DB 연결 (희재)
|
||||||
|
|
||||||
### 3.1 DB 연결 설정
|
### 3.1 DB 연결 설정
|
||||||
- [ ] psycopg2 의존성 추가
|
- [x] psycopg2 의존성 추가
|
||||||
- [ ] .env 파일 수정
|
- [x] .env 파일 수정
|
||||||
- [ ] TOKEN_PROVIDER=database
|
- [x] TOKEN_PROVIDER=database
|
||||||
- [ ] POSTGRES_CONNECTION_STRING 설정
|
- [x] POSTGRES_CONNECTION_STRING 설정
|
||||||
- [ ] 데이터베이스 연결 테스트 스크립트 작성
|
- [x] 데이터베이스 연결 테스트 스크립트 작성
|
||||||
- [ ] 연결 확인
|
- [x] 연결 확인
|
||||||
|
|
||||||
### 3.2 DBCredentialsProvider 구현
|
### 3.2 DBCredentialsProvider 구현
|
||||||
- [ ] services/db_credentials_provider.py 생성
|
- [x] services/db_credentials_provider.py 생성
|
||||||
- [ ] __init__ 메서드 구현
|
- [x] __init__ 메서드 구현
|
||||||
- [ ] DB 연결 초기화
|
- [x] DB 연결 초기화
|
||||||
- [ ] 연결 풀 설정
|
- [x] 연결 풀 설정
|
||||||
- [ ] get_credentials 메서드 구현
|
- [x] get_credentials 메서드 구현
|
||||||
- [ ] SQL 쿼리 작성
|
- [x] SQL 쿼리 작성
|
||||||
- [ ] 토큰 조회 로직
|
- [x] 토큰 조회 로직
|
||||||
- [ ] JSON 파싱
|
- [x] JSON 파싱
|
||||||
- [ ] Credentials 객체 생성
|
- [x] Credentials 객체 생성
|
||||||
- [ ] refresh_token 메서드 구현
|
- [x] oauth_config에서 client_id/secret 가져오기
|
||||||
- [ ] 토큰 갱신 로직
|
- [x] save_credentials 메서드 구현 (토큰 업데이트용)
|
||||||
- [ ] DB 업데이트
|
- [x] 토큰 갱신 로직
|
||||||
- [ ] 에러 처리 추가
|
- [x] DB 업데이트
|
||||||
- [ ] 토큰 없음 처리
|
- [x] 에러 처리 추가
|
||||||
- [ ] DB 연결 실패 처리
|
- [x] 토큰 없음 처리
|
||||||
- [ ] 파싱 에러 처리
|
- [x] DB 연결 실패 처리
|
||||||
|
- [x] 파싱 에러 처리
|
||||||
|
|
||||||
### 3.3 기존 코드 수정
|
### 3.3 기존 코드 수정
|
||||||
- [ ] main.py 수정
|
- [x] main.py 수정
|
||||||
- [ ] FileCredentialsProvider import 제거
|
- [x] FileCredentialsProvider 유지 (조건부 사용)
|
||||||
- [ ] DBCredentialsProvider import 추가
|
- [x] DBCredentialsProvider import 추가
|
||||||
- [ ] Provider 초기화 코드 변경
|
- [x] Provider 초기화 코드 변경 (TOKEN_PROVIDER 환경변수 기반)
|
||||||
- [ ] 환경변수 체크 로직 수정
|
- [x] 환경변수 체크 로직 수정
|
||||||
- [ ] 테스트 코드 작성
|
- [x] 테스트 코드 작성
|
||||||
- [ ] 로컬 테스트
|
- [x] 로컬 테스트
|
||||||
|
|
||||||
### 3.4 배포 준비
|
### 3.4 배포 준비
|
||||||
- [ ] requirements.txt 업데이트
|
- [x] requirements.txt 업데이트
|
||||||
- [ ] Dockerfile 확인
|
- [ ] Dockerfile 확인
|
||||||
- [ ] 빌드 테스트
|
- [ ] 빌드 테스트
|
||||||
- [ ] 커밋 및 푸시
|
- [ ] 커밋 및 푸시
|
||||||
|
|||||||
@ -282,14 +282,15 @@ CREATE TABLE gmail_audit_logs (
|
|||||||
## 6. 구현 단계
|
## 6. 구현 단계
|
||||||
|
|
||||||
### Phase 1: 백엔드 기초
|
### Phase 1: 백엔드 기초
|
||||||
- [ ] 필요 테이블 생성
|
- [x] 필요 테이블 생성
|
||||||
- robeing_stats 테이블 (레벨 관리)
|
- robeing_stats 테이블 (레벨 관리)
|
||||||
- gmail_audit_logs 테이블 (감사 로그)
|
- gmail_audit_logs 테이블 (감사 로그)
|
||||||
- gmail_tokens에 is_equipped, equipped_to 컬럼 추가
|
- gmail_tokens에 is_equipped, equipped_to 컬럼 추가
|
||||||
- [ ] skill-email DB 연결 코드 작성
|
- [x] skill-email DB 연결 코드 작성 (희재)
|
||||||
- FileCredentialsProvider → DBCredentialsProvider 전환
|
- FileCredentialsProvider → DBCredentialsProvider 전환
|
||||||
- PostgreSQL 연결 설정 추가 (192.168.219.45)
|
- PostgreSQL 연결 설정 추가 (localhost:5433 SSH 터널)
|
||||||
- [ ] robeing-monitor 서비스 구축
|
- oauth_config 컬럼에서 client_id/secret 가져오기 구현
|
||||||
|
- [x] robeing-monitor 서비스 구축
|
||||||
- 기존 robeing-state-service 확장
|
- 기존 robeing-state-service 확장
|
||||||
- 포트 9024로 변경
|
- 포트 9024로 변경
|
||||||
- 아이템 관리 API 추가
|
- 아이템 관리 API 추가
|
||||||
|
|||||||
340
plans/completed/250819_skill_email_db_connection_completed.md
Normal file
340
plans/completed/250819_skill_email_db_connection_completed.md
Normal file
@ -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번 작업)**
|
||||||
Loading…
x
Reference in New Issue
Block a user