diff --git a/postgresql_ssh_tunnel_guide.md b/postgresql_ssh_tunnel_guide.md new file mode 100644 index 0000000..5e5c67b --- /dev/null +++ b/postgresql_ssh_tunnel_guide.md @@ -0,0 +1,426 @@ +# PostgreSQL SSH 터널 및 데이터베이스 접속 가이드 + +## 📋 목차 +1. [SSH 터널 설정](#ssh-터널-설정) +2. [PostgreSQL 접속](#postgresql-접속) +3. [Python으로 DB 조회](#python으로-db-조회) +4. [주요 테이블 구조](#주요-테이블-구조) +5. [자주 사용하는 쿼리](#자주-사용하는-쿼리) +6. [문제 해결](#문제-해결) + +--- + +## SSH 터널 설정 + +### 1. 현재 SSH 터널 확인 +```bash +# 실행 중인 SSH 터널 확인 +ps aux | grep "ssh.*5433" | grep -v grep + +# 특정 포트 사용 확인 +netstat -tlnp | grep 5433 +lsof -i :5433 +``` + +### 2. SSH 터널 생성 +```bash +# 기본 SSH 터널 생성 (백그라운드 실행) +ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123 + +# StrictHostKeyChecking 비활성화 (테스트용) +ssh -o StrictHostKeyChecking=no -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123 + +# 옵션 설명: +# -f : 백그라운드 실행 +# -N : 원격 명령 실행하지 않음 (포트 포워딩만) +# -L : 로컬 포트 포워딩 (로컬포트:원격호스트:원격포트) +# -p : SSH 포트 지정 +``` + +### 3. SSH 터널 종료 +```bash +# 프로세스 ID 찾기 +ps aux | grep "ssh.*5433" + +# 프로세스 종료 +sudo kill [PID] + +# 또는 한 번에 +sudo pkill -f "ssh.*5433" +``` + +--- + +## PostgreSQL 접속 + +### 1. 연결 정보 +``` +Host: localhost (SSH 터널 사용시) +Port: 5433 (터널 로컬 포트) +Database: auth_db +Username: robeings +Password: robeings +``` + +### 2. psql 명령어로 접속 +```bash +# psql 설치 (Ubuntu/Debian) +sudo apt-get install postgresql-client + +# DB 접속 +psql postgresql://robeings:robeings@localhost:5433/auth_db + +# 또는 +psql -h localhost -p 5433 -U robeings -d auth_db +``` + +### 3. psql 기본 명령어 +```sql +-- 테이블 목록 보기 +\dt + +-- 특정 테이블 구조 보기 +\d gmail_tokens +\d users + +-- 데이터베이스 목록 +\l + +-- 현재 데이터베이스 정보 +\conninfo + +-- 종료 +\q +``` + +--- + +## Python으로 DB 조회 + +### 1. 필요한 패키지 설치 +```bash +pip install psycopg2-binary +``` + +### 2. 기본 연결 및 조회 +```python +import psycopg2 +import json + +# DB 연결 +conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/auth_db') +cur = conn.cursor() + +# 쿼리 실행 +cur.execute("SELECT * FROM users LIMIT 5") +rows = cur.fetchall() + +for row in rows: + print(row) + +# 연결 종료 +cur.close() +conn.close() +``` + +### 3. 안전한 쿼리 실행 (파라미터 바인딩) +```python +# SQL Injection 방지를 위해 항상 파라미터 바인딩 사용 +user_id = 'b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0' +cur.execute( + "SELECT * FROM users WHERE id = %s", + (user_id,) # 튜플로 전달 +) +``` + +--- + +## 주요 테이블 구조 + +### 1. users 테이블 +```sql +CREATE TABLE users ( + id UUID PRIMARY KEY, + email VARCHAR UNIQUE, + name VARCHAR, + picture VARCHAR, + oauth_provider VARCHAR, + oauth_id VARCHAR, + is_active BOOLEAN, + created_at TIMESTAMP, + updated_at TIMESTAMP, + last_login_at TIMESTAMP, + username VARCHAR +); +``` + +### 2. gmail_tokens 테이블 +```sql +CREATE TABLE gmail_tokens ( + id SERIAL PRIMARY KEY, + user_id UUID REFERENCES users(id), + is_equipped BOOLEAN, + token_data JSONB, -- access_token, refresh_token 저장 + oauth_config JSONB, -- OAuth 설정 정보 + scopes JSONB, -- Gmail API 권한 목록 + metadata JSONB, + expiry TIMESTAMP, + created_at TIMESTAMP, + updated_at TIMESTAMP, + robeing_id VARCHAR, -- 로빙 ID (rb8001 등) + equipped_to VARCHAR +); +``` + +--- + +## 자주 사용하는 쿼리 + +### 1. Gmail 토큰 확인 +```python +import psycopg2 +import json + +conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/auth_db') +cur = conn.cursor() + +# Gmail 토큰 상태 확인 +query = """ + SELECT + u.username, + u.email, + g.user_id, + g.is_equipped, + g.robeing_id, + g.token_data, + g.scopes + FROM gmail_tokens g + JOIN users u ON g.user_id = u.id + ORDER BY g.created_at DESC +""" + +cur.execute(query) +rows = cur.fetchall() + +for row in rows: + print(f"\nUser: {row[0]} ({row[1]})") + print(f" UUID: {row[2]}") + print(f" Equipped: {row[3]}") + print(f" Robeing: {row[4]}") + + token_data = row[5] if row[5] else {} + if token_data and 'access_token' in token_data: + access_token = token_data.get('access_token', '') + if access_token.startswith('ya29'): + print(f" Token: 실제 OAuth 토큰 있음") + else: + print(f" Token: 테스트 토큰") + + scopes = row[6] if row[6] else [] + if scopes: + print(f" Scopes: {len(scopes)}개") + +cur.close() +conn.close() +``` + +### 2. Slack User ID를 UUID로 변환 +```python +import uuid + +def slack_id_to_uuid(slack_user_id): + """Slack User ID를 일관된 UUID로 변환""" + namespace = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') + return str(uuid.uuid5(namespace, slack_user_id)) + +# 예시 +slack_id = 'U091UNVE41M' +user_uuid = slack_id_to_uuid(slack_id) +print(f"Slack ID: {slack_id}") +print(f"UUID: {user_uuid}") +# 출력: b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0 +``` + +### 3. 사용자별 Gmail 토큰 추가/업데이트 +```python +import psycopg2 +import json +import uuid + +def add_gmail_token(slack_user_id, email, access_token, refresh_token): + """Gmail 토큰 추가 또는 업데이트""" + + # Slack ID를 UUID로 변환 + namespace = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') + user_uuid = str(uuid.uuid5(namespace, slack_user_id)) + + conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/auth_db') + cur = conn.cursor() + + try: + # 1. users 테이블에 사용자 추가 (없으면) + cur.execute(""" + INSERT INTO users (id, email, username, created_at) + VALUES (%s, %s, %s, NOW()) + ON CONFLICT (id) DO UPDATE SET email = EXCLUDED.email + """, (user_uuid, email, slack_user_id)) + + # 2. gmail_tokens 추가 또는 업데이트 + token_data = { + "access_token": access_token, + "refresh_token": refresh_token, + "token_type": "Bearer" + } + + cur.execute(""" + INSERT INTO gmail_tokens (user_id, token_data, is_equipped, robeing_id, created_at) + VALUES (%s, %s::jsonb, true, 'rb8001', NOW()) + ON CONFLICT (user_id) DO UPDATE + SET token_data = EXCLUDED.token_data, + updated_at = NOW() + """, (user_uuid, json.dumps(token_data))) + + conn.commit() + print(f"✅ Gmail 토큰 저장 완료: {email}") + + except Exception as e: + conn.rollback() + print(f"❌ 오류 발생: {e}") + finally: + cur.close() + conn.close() +``` + +--- + +## 문제 해결 + +### 1. SSH 터널이 연결되지 않을 때 +```bash +# 기존 터널 프로세스 종료 +sudo pkill -f "ssh.*5433" + +# 포트 사용 확인 +lsof -i :5433 + +# 다시 연결 +ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123 +``` + +### 2. psycopg2 설치 오류 +```bash +# Ubuntu/Debian +sudo apt-get install libpq-dev python3-dev +pip install psycopg2 + +# 또는 바이너리 버전 사용 +pip install psycopg2-binary +``` + +### 3. UUID 타입 오류 +```python +# UUID 문자열을 PostgreSQL UUID 타입으로 변환 +import uuid + +# 방법 1: 직접 캐스팅 +cur.execute( + "SELECT * FROM users WHERE id = %s::uuid", + ('b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0',) +) + +# 방법 2: uuid 객체 사용 +user_uuid = uuid.UUID('b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0') +cur.execute( + "SELECT * FROM users WHERE id = %s", + (str(user_uuid),) +) +``` + +### 4. JSONB 필드 처리 +```python +import json + +# JSONB 데이터 삽입 +data = {"key": "value", "nested": {"field": "data"}} +cur.execute( + "INSERT INTO table_name (jsonb_column) VALUES (%s::jsonb)", + (json.dumps(data),) +) + +# JSONB 데이터 조회 +cur.execute("SELECT jsonb_column FROM table_name") +row = cur.fetchone() +json_data = row[0] # 자동으로 Python dict로 변환됨 +``` + +--- + +## 환경 변수 설정 + +`.env` 파일에 추가: +```bash +# PostgreSQL Connection (SSH tunnel) +POSTGRES_CONNECTION_STRING=postgresql://robeings:robeings@localhost:5433/auth_db + +# SSH Tunnel Config +SSH_HOST=124.55.18.179 +SSH_PORT=51123 +SSH_USER=admin +LOCAL_PORT=5433 +REMOTE_PORT=5432 +``` + +Python에서 사용: +```python +import os +from dotenv import load_dotenv + +load_dotenv() + +conn_string = os.getenv('POSTGRES_CONNECTION_STRING') +conn = psycopg2.connect(conn_string) +``` + +--- + +## 자동화 스크립트 + +### SSH 터널 자동 재연결 스크립트 +```bash +#!/bin/bash +# /home/heejae/scripts/ssh_tunnel_monitor.sh + +LOCAL_PORT=5433 +SSH_CMD="ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123" + +# 터널이 살아있는지 확인 +if ! nc -z localhost $LOCAL_PORT 2>/dev/null; then + echo "SSH tunnel is down. Restarting..." + + # 기존 프로세스 종료 + pkill -f "ssh.*$LOCAL_PORT" + + # 새 터널 생성 + $SSH_CMD + + echo "SSH tunnel restarted" +else + echo "SSH tunnel is running" +fi +``` + +crontab에 추가 (5분마다 체크): +```bash +*/5 * * * * /home/heejae/scripts/ssh_tunnel_monitor.sh +``` + +--- + +## 참고 링크 +- [PostgreSQL 공식 문서](https://www.postgresql.org/docs/) +- [psycopg2 문서](https://www.psycopg.org/docs/) +- [SSH 터널링 가이드](https://www.ssh.com/academy/ssh/tunneling) + +--- + +*마지막 업데이트: 2025-08-20* \ No newline at end of file diff --git a/robeing-monitor-integration.md b/robeing-monitor-integration.md new file mode 100644 index 0000000..900dc71 --- /dev/null +++ b/robeing-monitor-integration.md @@ -0,0 +1,196 @@ +# Robeing Monitor 통합 작업 문서 + +## 작업 완료 사항 (2025-08-20) + +### 1. UUID 변환 기능 구현 +- **문제**: Slack User ID (예: U091UNVE41M)와 PostgreSQL의 UUID 형식 불일치로 인한 데이터베이스 조회 실패 +- **해결**: 모든 서비스에 일관된 UUID 변환 로직 적용 + ```python + namespace = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') + user_uuid = str(uuid.uuid5(namespace, slack_id)) + ``` + +#### 수정된 파일들: +- `/home/heejae/robeing-monitor/app/api/items.py` - UUID 변환 헬퍼 함수 추가 +- `/home/heejae/rb8001/app/skills/email_integration.py` - UUID 변환 적용 +- `/home/heejae/skill-email/services/db_credentials_provider.py` - UUID 변환 적용 + +### 2. Robeing Monitor API 확장 +- **새 엔드포인트**: `/api/items/gmail/{user_id}/token` + - skill-email이 Gmail 토큰 데이터를 가져올 수 있도록 지원 + - Slack ID를 자동으로 UUID로 변환 + - scopes를 JSON 배열로 파싱하여 반환 + +### 3. Skill-Email API Provider 구현 +- **새 파일**: `/home/heejae/skill-email/services/api_credentials_provider.py` + - robeing-monitor API를 통해 Gmail 자격증명을 가져오는 Provider + - 기존 DB 직접 연결 방식을 API 호출로 대체 + - Result 타입 시스템과 호환되도록 구현 + +### 4. 환경 설정 변경 +- **skill-email/docker-compose.yml**: + ```yaml + environment: + - TOKEN_PROVIDER=api + - ROBEING_MONITOR_URL=http://localhost:9024 + ``` +- **robeing-monitor/.env**: + ``` + DATABASE_URL=postgresql://robeings:robeings@localhost:5433/main_db + ``` + +## 현재 아키텍처 + +``` +[rb8001] ─────> [skill-email] ─────> [robeing-monitor] ─────> [PostgreSQL] + │ │ │ │ + │ │ │ │ + └─ Slack ID ──────┴───── API 호출 ────────┴─── UUID 변환 ───────┘ +``` + +### 데이터 흐름: +1. rb8001이 Slack User ID로 skill-email 호출 +2. skill-email이 robeing-monitor API 호출 (Slack ID 전달) +3. robeing-monitor가 Slack ID를 UUID로 변환 +4. PostgreSQL에서 데이터 조회 후 반환 + +## 해결된 문제들 + +1. **UUID 타입 에러**: "invalid input syntax for type uuid: 'U091UNVE41M'" + - 모든 서비스에서 일관된 UUID 변환 적용으로 해결 + +2. **데이터베이스 연결 문제**: + - SSH 터널 설정 (localhost:5433 → 124.55.18.179:5432) + - 올바른 데이터베이스 이름 사용 (main_db) + +3. **Scopes 파싱 문제**: + - PostgreSQL에 문자열로 저장된 scopes를 JSON 배열로 변환 + +## 남은 작업 사항 + +### 1. 토큰 갱신 메커니즘 +- **현재 상태**: Gmail access token이 만료됨 +- **필요 작업**: + - OAuth 재인증 플로우 구현 + - Refresh token을 사용한 자동 갱신 로직 + - robeing-monitor에 토큰 업데이트 API 추가 + +### 2. 에러 처리 개선 +- **필요 작업**: + - 토큰 만료 시 사용자에게 재인증 안내 + - Slack에서 "죄송합니다" 대신 구체적인 오류 메시지 표시 + - 재시도 로직 구현 + +### 3. 모니터링 및 로깅 +- **필요 작업**: + - API 호출 성공/실패 메트릭 수집 + - 토큰 만료 예측 및 사전 알림 + - 감사 로그 (gmail_audit_logs 테이블) 활용 + +### 4. 성능 최적화 +- **고려 사항**: + - API 호출 캐싱 + - 연결 풀링 최적화 + - 불필요한 UUID 변환 최소화 + +## SSH 터널 및 PostgreSQL 접속 + +### SSH 터널 설정 +```bash +# SSH 터널 시작 +sshpass -p "19800508" ssh -o StrictHostKeyChecking=no -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51123 + +# 터널 확인 +nc -zv localhost 5433 + +# 터널 프로세스 확인 +pgrep -f "ssh.*5433:localhost:5432" +``` + +### PostgreSQL 접속 +```bash +# SSH를 통한 원격 실행 +sshpass -p "19800508" ssh -o StrictHostKeyChecking=no admin@124.55.18.179 -p 51123 "PGPASSWORD=robeings psql -h localhost -U robeings -d main_db -c 'SELECT * FROM gmail_tokens'" + +# 로컬 터널을 통한 접속 (psql 클라이언트 필요) +PGPASSWORD=robeings psql -h localhost -p 5433 -U robeings -d main_db +``` + +### 주요 테이블 +- `gmail_tokens`: Gmail OAuth 토큰 저장 +- `gmail_audit_logs`: Gmail 작업 감사 로그 +- `robeing_stats`: Robeing 레벨 정보 +- `slack_user_mapping`: Slack-UUID 매핑 (현재 미사용) + +## 테스트 명령어 + +### Robeing Monitor API 테스트 +```bash +# Gmail 아이템 조회 +curl -s http://localhost:9024/api/items/gmail -H "X-User-Id: U091UNVE41M" | python3 -m json.tool + +# 토큰 데이터 조회 +curl -s http://localhost:9024/api/items/gmail/U091UNVE41M/token -H "X-User-Id: U091UNVE41M" | python3 -m json.tool +``` + +### Skill-Email 테스트 +```bash +# 이메일 전송 테스트 +curl -X POST http://localhost:8501/send \ + -H "Content-Type: application/json" \ + -d '{ + "to": "test@example.com", + "subject": "Test", + "body": "Test email", + "user_id": "U091UNVE41M" + }' +``` + +## Docker 관리 + +### Robeing Monitor +```bash +cd /home/heejae/robeing-monitor +docker compose down && docker compose up -d --build +docker logs robeing_monitor --tail 50 +``` + +### Skill-Email +```bash +cd /home/heejae/skill-email +docker compose down && docker compose up -d --build +docker logs skill-email --tail 50 +``` + +## 중요 참고사항 + +1. **UUID Namespace**: 모든 서비스에서 동일한 namespace UUID 사용 필수 + - `6ba7b810-9dad-11d1-80b4-00c04fd430c8` + +2. **데이터베이스**: + - 실제 데이터는 `main_db`에 저장됨 + - `auth_db`는 존재하지 않음 + +3. **포트 정보**: + - robeing-monitor: 9024 + - skill-email: 8501 + - PostgreSQL (SSH tunnel): 5433 + +4. **환경변수 우선순위**: + - Docker 컨테이너는 .env 파일과 docker-compose.yml의 environment 섹션 모두 참조 + - 재빌드 필요 시 `docker compose down && docker compose up -d --build` 실행 + +## 다음 단계 권장사항 + +1. **즉시 필요**: + - Gmail OAuth 토큰 재인증 구현 + - 토큰 자동 갱신 메커니즘 + +2. **단기 개선**: + - 에러 메시지 개선 + - 로깅 시스템 강화 + +3. **장기 개선**: + - 마이크로서비스 간 통신 표준화 + - API Gateway 패턴 고려 + - 중앙 집중식 인증/인가 시스템 \ No newline at end of file