Fix incorrect table names in documentation

- gmail_tokens → gmail_token (33 files)
- companies → company (17 files)
- conversation_logs → conversation_log (27 files)
- workspace_members → workspace_member (28 files)

All table names now match the actual PostgreSQL schema
This commit is contained in:
happybell80 2025-09-26 00:49:47 +09:00
parent 58153a49a0
commit 8c02b80359
78 changed files with 380 additions and 380 deletions

View File

@ -91,7 +91,7 @@
users: id(UUID), username, email, name, created_at users: id(UUID), username, email, name, created_at
-- Gmail 토큰 (아이템) -- Gmail 토큰 (아이템)
gmail_tokens: id, user_id, email, is_equipped, equipped_to gmail_token: id, user_id, email, is_equipped, equipped_to
-- Gmail 감사 로그 -- Gmail 감사 로그
gmail_audit_logs: id, user_id, robeing_id, action, created_at gmail_audit_logs: id, user_id, robeing_id, action, created_at

View File

@ -213,7 +213,7 @@ async def show_typing(channel: str):
-- 대신 users 테이블에서 직접 관리 -- 대신 users 테이블에서 직접 관리
-- 대화 로그 -- 대화 로그
CREATE TABLE conversation_logs ( CREATE TABLE conversation_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID, -- slack_user_mapping에서 조회한 UUID user_id UUID, -- slack_user_mapping에서 조회한 UUID
robeing_id VARCHAR(50), robeing_id VARCHAR(50),

View File

@ -61,7 +61,7 @@
- PRIMARY KEY (slack_user_id, slack_workspace_id) - PRIMARY KEY (slack_user_id, slack_workspace_id)
``` ```
#### gmail_tokens 테이블 #### gmail_token 테이블
```sql ```sql
- id: UUID - id: UUID
- user_id: UUID (FK → users.id) - user_id: UUID (FK → users.id)
@ -114,7 +114,7 @@ sequenceDiagram
- Slack도 임시 코드 방식으로 맞춤 - Slack도 임시 코드 방식으로 맞춤
2. **DB 스키마 불일치** 2. **DB 스키마 불일치**
- companies vs workspaces 테이블 공존 - company vs workspaces 테이블 공존
- relationship 주석 처리 - relationship 주석 처리
3. **하드코딩된 값** 3. **하드코딩된 값**
@ -138,7 +138,7 @@ sequenceDiagram
### 특징 ### 특징
- **원페이지 복귀**: OAuth 인증 후 원래 있던 페이지로 자동 복귀 - **원페이지 복귀**: OAuth 인증 후 원래 있던 페이지로 자동 복귀
- **상태 관리**: state 파라미터에 return_url 포함하여 전달 - **상태 관리**: state 파라미터에 return_url 포함하여 전달
- **토큰 저장**: gmail_tokens 테이블에 암호화 저장 - **토큰 저장**: gmail_token 테이블에 암호화 저장
## 보안 고려사항 ## 보안 고려사항
@ -185,7 +185,7 @@ DATABASE_URL=postgresql://robeings:password@localhost/main_db
## 향후 개선 계획 ## 향후 개선 계획
### 단기 (1-2주) ### 단기 (1-2주)
1. DB 스키마 통일 (companies → workspaces) 1. DB 스키마 통일 (company → workspaces)
2. Frontend 인증 방식 통일 2. Frontend 인증 방식 통일
3. 환경변수 정리 3. 환경변수 정리

View File

@ -83,7 +83,7 @@ psql -h localhost -p 5432 -U robeings -d main_db
\dt \dt
-- 특정 테이블 구조 보기 -- 특정 테이블 구조 보기
\d gmail_tokens \d gmail_token
\d users \d users
-- 데이터베이스 목록 -- 데이터베이스 목록
@ -160,9 +160,9 @@ CREATE TABLE users (
); );
``` ```
### 2. gmail_tokens 테이블 ### 2. gmail_token 테이블
```sql ```sql
CREATE TABLE gmail_tokens ( CREATE TABLE gmail_token (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
slack_id VARCHAR(100), -- Slack 사용자 ID (새로 추가) slack_id VARCHAR(100), -- Slack 사용자 ID (새로 추가)
@ -184,9 +184,9 @@ CREATE TABLE gmail_tokens (
); );
``` ```
### 3. conversation_logs 테이블 ### 3. conversation_log 테이블
```sql ```sql
CREATE TABLE conversation_logs ( CREATE TABLE conversation_log (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
robeing_id VARCHAR, robeing_id VARCHAR,
channel_id VARCHAR, channel_id VARCHAR,
@ -235,7 +235,7 @@ query = """
g.robeing_id, g.robeing_id,
g.token_data, g.token_data,
g.scopes g.scopes
FROM gmail_tokens g FROM gmail_token g
JOIN users u ON g.user_id = u.id JOIN users u ON g.user_id = u.id
ORDER BY g.created_at DESC ORDER BY g.created_at DESC
""" """
@ -308,7 +308,7 @@ def add_gmail_token(slack_user_id, email, access_token, refresh_token):
ON CONFLICT (id) DO UPDATE SET email = EXCLUDED.email ON CONFLICT (id) DO UPDATE SET email = EXCLUDED.email
""", (user_uuid, email, slack_user_id)) """, (user_uuid, email, slack_user_id))
# 2. gmail_tokens 추가 또는 업데이트 # 2. gmail_token 추가 또는 업데이트
token_data = { token_data = {
"access_token": access_token, "access_token": access_token,
"refresh_token": refresh_token, "refresh_token": refresh_token,
@ -316,7 +316,7 @@ def add_gmail_token(slack_user_id, email, access_token, refresh_token):
} }
cur.execute(""" cur.execute("""
INSERT INTO gmail_tokens (user_id, token_data, is_equipped, robeing_id, created_at) INSERT INTO gmail_token (user_id, token_data, is_equipped, robeing_id, created_at)
VALUES (%s, %s::jsonb, true, 'rb8001', NOW()) VALUES (%s, %s::jsonb, true, 'rb8001', NOW())
ON CONFLICT (user_id) DO UPDATE ON CONFLICT (user_id) DO UPDATE
SET token_data = EXCLUDED.token_data, SET token_data = EXCLUDED.token_data,

View File

@ -42,8 +42,8 @@ psql postgresql://robeings:robeings@localhost:5433/main_db
``` ```
## 최근 변경사항 ## 최근 변경사항
- 2025-08-26: conversation_logs에 slack_user_id 추가 - 2025-08-26: conversation_log에 slack_user_id 추가
- 2025-08-23: gmail_tokens 구조 변경 - 2025-08-23: gmail_token 구조 변경
- 2025-08-20: auth_db → main_db 마이그레이션 - 2025-08-20: auth_db → main_db 마이그레이션
## 최종 업데이트 ## 최종 업데이트

View File

@ -8,22 +8,22 @@
```mermaid ```mermaid
erDiagram erDiagram
users ||--o{ workspace_members : "has many" users ||--o{ workspace_member : "has many"
users ||--o{ slack_user_mapping : "has many" users ||--o{ slack_user_mapping : "has many"
users ||--o{ gmail_tokens : "has" users ||--o{ gmail_token : "has"
users ||--o{ robeing_stats : "has" users ||--o{ robeing_stats : "has"
users ||--o{ conversation_logs : "has many" users ||--o{ conversation_log : "has many"
workspaces ||--o{ workspace_members : "has many" workspaces ||--o{ workspace_member : "has many"
workspaces }o--|| companies : "belongs to" workspaces }o--|| company : "belongs to"
slack_workspaces ||--o{ slack_user_mapping : "has many" slack_workspaces ||--o{ slack_user_mapping : "has many"
workspace_members ||--o{ slack_user_mapping : "references" workspace_member ||--o{ slack_user_mapping : "references"
gmail_tokens }o--|| users : "belongs to" gmail_token }o--|| users : "belongs to"
robeing_stats }o--|| users : "belongs to" robeing_stats }o--|| users : "belongs to"
conversation_logs }o--|| users : "belongs to" conversation_log }o--|| users : "belongs to"
``` ```
--- ---
@ -32,11 +32,11 @@ erDiagram
### 1. User 중심 관계 ### 1. User 중심 관계
``` ```
users (1) ─────┬──── (*) workspace_members users (1) ─────┬──── (*) workspace_member
├──── (*) slack_user_mapping ├──── (*) slack_user_mapping
├──── (0..1) gmail_tokens ├──── (0..1) gmail_token
├──── (0..1) robeing_stats ├──── (0..1) robeing_stats
└──── (*) conversation_logs └──── (*) conversation_log
``` ```
- 한 사용자는 여러 워크스페이스의 멤버가 될 수 있음 - 한 사용자는 여러 워크스페이스의 멤버가 될 수 있음
@ -47,8 +47,8 @@ users (1) ─────┬──── (*) workspace_members
### 2. Workspace 관계 ### 2. Workspace 관계
``` ```
companies (1) ──── (*) workspaces company (1) ──── (*) workspaces
workspaces (1) ──── (*) workspace_members workspaces (1) ──── (*) workspace_member
``` ```
- 한 회사는 여러 워크스페이스를 가질 수 있음 - 한 회사는 여러 워크스페이스를 가질 수 있음
@ -58,11 +58,11 @@ workspaces (1) ──── (*) workspace_members
``` ```
slack_workspaces (1) ──── (*) slack_user_mapping slack_workspaces (1) ──── (*) slack_user_mapping
slack_user_mapping (*) ──── (1) users slack_user_mapping (*) ──── (1) users
slack_user_mapping (*) ──── (0..1) workspace_members slack_user_mapping (*) ──── (0..1) workspace_member
``` ```
- Slack 사용자 ID를 시스템 UUID로 변환하는 브릿지 역할 - Slack 사용자 ID를 시스템 UUID로 변환하는 브릿지 역할
- workspace_members와 선택적 연결 - workspace_member와 선택적 연결
--- ---
@ -79,9 +79,9 @@ SELECT
sum.slack_user_id, sum.slack_user_id,
gt.is_equipped as gmail_equipped gt.is_equipped as gmail_equipped
FROM users u FROM users u
LEFT JOIN workspace_members wm ON u.id = wm.user_id LEFT JOIN workspace_member wm ON u.id = wm.user_id
LEFT JOIN slack_user_mapping sum ON u.id = sum.user_id LEFT JOIN slack_user_mapping sum ON u.id = sum.user_id
LEFT JOIN gmail_tokens gt ON u.id = gt.user_id LEFT JOIN gmail_token gt ON u.id = gt.user_id
WHERE u.username = 'happybell80'; WHERE u.username = 'happybell80';
``` ```
@ -108,7 +108,7 @@ SELECT
rs.level, rs.level,
gt.scopes gt.scopes
FROM users u FROM users u
JOIN gmail_tokens gt ON u.id = gt.user_id JOIN gmail_token gt ON u.id = gt.user_id
LEFT JOIN robeing_stats rs ON u.id = rs.user_id LEFT JOIN robeing_stats rs ON u.id = rs.user_id
WHERE gt.is_equipped = true; WHERE gt.is_equipped = true;
``` ```
@ -122,7 +122,7 @@ SELECT
wm.role, wm.role,
wm.robeing_id wm.robeing_id
FROM workspaces w FROM workspaces w
JOIN workspace_members wm ON w.id = wm.workspace_id JOIN workspace_member wm ON w.id = wm.workspace_id
JOIN users u ON wm.user_id = u.id JOIN users u ON wm.user_id = u.id
WHERE w.name = 'Ivada Robeing'; WHERE w.name = 'Ivada Robeing';
``` ```
@ -148,7 +148,7 @@ user_id (UUID) 획득
``` ```
User 요청 User 요청
user_id (UUID)로 gmail_tokens 조회 user_id (UUID)로 gmail_token 조회
is_equipped 확인 is_equipped 확인
@ -202,10 +202,10 @@ BEGIN;
SELECT level FROM robeing_stats WHERE user_id = ?; SELECT level FROM robeing_stats WHERE user_id = ?;
-- 2. 소유권 확인 -- 2. 소유권 확인
SELECT id FROM gmail_tokens WHERE user_id = ?; SELECT id FROM gmail_token WHERE user_id = ?;
-- 3. 장착 처리 -- 3. 장착 처리
UPDATE gmail_tokens UPDATE gmail_token
SET is_equipped = true, equipped_to = ? SET is_equipped = true, equipped_to = ?
WHERE user_id = ?; WHERE user_id = ?;
@ -223,8 +223,8 @@ BEGIN;
INSERT INTO users (id, email, name, username) INSERT INTO users (id, email, name, username)
VALUES (?, ?, ?, ?); VALUES (?, ?, ?, ?);
-- 2. workspace_members 추가 -- 2. workspace_member 추가
INSERT INTO workspace_members (user_id, workspace_id, role) INSERT INTO workspace_member (user_id, workspace_id, role)
VALUES (?, ?, 'member'); VALUES (?, ?, 'member');
-- 3. robeing_stats 초기화 -- 3. robeing_stats 초기화

View File

@ -315,7 +315,7 @@ stateDiagram-v2
- username (사용자 정의 또는 자동 생성) - username (사용자 정의 또는 자동 생성)
관계: 관계:
- users.id (UUID) ← gmail_tokens.user_id - users.id (UUID) ← gmail_token.user_id
- users.id (UUID) ← slack_user_mapping.user_id - users.id (UUID) ← slack_user_mapping.user_id
- users.id (UUID) ← robeing_stats.user_id - users.id (UUID) ← robeing_stats.user_id

View File

@ -20,10 +20,10 @@ sequenceDiagram
F->>G: GET /gateway/api/history?limit=30<br/>Authorization: Bearer {JWT} F->>G: GET /gateway/api/history?limit=30<br/>Authorization: Bearer {JWT}
G->>G: JWT 검증 (서명, 만료시간) G->>G: JWT 검증 (서명, 만료시간)
G->>G: JWT에서 sub(UUID) 추출 G->>G: JWT에서 sub(UUID) 추출
G->>G: workspace_members 테이블 조회<br/>robeing_id 확인 (rb8001) G->>G: workspace_member 테이블 조회<br/>robeing_id 확인 (rb8001)
G->>R: GET /api/history?limit=30<br/>Authorization: Bearer {JWT}<br/>X-User-Id: {UUID} G->>R: GET /api/history?limit=30<br/>Authorization: Bearer {JWT}<br/>X-User-Id: {UUID}
R->>R: JWT 검증 및 user_id 추출 R->>R: JWT 검증 및 user_id 추출
R->>DB: SELECT * FROM conversation_logs<br/>WHERE user_id = (:user_id)::uuid R->>DB: SELECT * FROM conversation_log<br/>WHERE user_id = (:user_id)::uuid
DB-->>R: 대화 기록 반환 DB-->>R: 대화 기록 반환
R->>R: DB row를 Frontend 형식으로 변환<br/>(user/robeing 메시지 분리) R->>R: DB row를 Frontend 형식으로 변환<br/>(user/robeing 메시지 분리)
R-->>G: {"messages": [...], "has_more": true} R-->>G: {"messages": [...], "has_more": true}
@ -66,8 +66,8 @@ def verify_jwt_token(token: str):
### 3.2 robeing 라우팅 ### 3.2 robeing 라우팅
```python ```python
# workspace_members 테이블 조회 # workspace_member 테이블 조회
SELECT robeing_id FROM workspace_members SELECT robeing_id FROM workspace_member
WHERE user_id = :user_id WHERE user_id = :user_id
# 결과: rb8001 또는 rb10508_micro # 결과: rb8001 또는 rb10508_micro
@ -100,7 +100,7 @@ async def get_current_user(authorization: str = Header(None)):
async def get_paginated_conversations(user_id: str, before: float = None, limit: int = 30): async def get_paginated_conversations(user_id: str, before: float = None, limit: int = 30):
query = """ query = """
SELECT id, message, response, timestamp SELECT id, message, response, timestamp
FROM conversation_logs FROM conversation_log
WHERE user_id = (:user_id)::uuid WHERE user_id = (:user_id)::uuid
AND robeing_id = 'rb8001' AND robeing_id = 'rb8001'
AND (:before::timestamp IS NULL OR timestamp < :before) AND (:before::timestamp IS NULL OR timestamp < :before)
@ -175,18 +175,18 @@ CREATE TABLE users (
); );
``` ```
### 6.2 workspace_members 테이블 ### 6.2 workspace_member 테이블
```sql ```sql
CREATE TABLE workspace_members ( CREATE TABLE workspace_member (
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
workspace_id UUID, workspace_id UUID,
robeing_id VARCHAR(50) -- rb8001, rb10508_micro 등 robeing_id VARCHAR(50) -- rb8001, rb10508_micro 등
); );
``` ```
### 6.3 conversation_logs 테이블 ### 6.3 conversation_log 테이블
```sql ```sql
CREATE TABLE conversation_logs ( CREATE TABLE conversation_log (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
robeing_id VARCHAR, robeing_id VARCHAR,
message VARCHAR, message VARCHAR,

View File

@ -64,13 +64,13 @@ sequenceDiagram
DM->>Email: GET /messages<br/>?user_id={slack_id}<br/>&limit=5<br/>&query=category:primary DM->>Email: GET /messages<br/>?user_id={slack_id}<br/>&limit=5<br/>&query=category:primary
%% 토큰 체크 및 갱신 %% 토큰 체크 및 갱신
Email->>DB: SELECT * FROM gmail_tokens<br/>WHERE user_id = ? Email->>DB: SELECT * FROM gmail_token<br/>WHERE user_id = ?
DB-->>Email: token_data, refresh_token DB-->>Email: token_data, refresh_token
alt 토큰 만료 alt 토큰 만료
Email->>Auth: POST /api/gmail/refresh<br/>Body: {user_id, refresh_token} Email->>Auth: POST /api/gmail/refresh<br/>Body: {user_id, refresh_token}
Auth->>Auth: Google OAuth 토큰 갱신 Auth->>Auth: Google OAuth 토큰 갱신
Auth->>DB: UPDATE gmail_tokens Auth->>DB: UPDATE gmail_token
Auth-->>Email: 새 access_token Auth-->>Email: 새 access_token
end end
@ -127,7 +127,7 @@ sequenceDiagram
rect rgb(255, 230, 230) rect rgb(255, 230, 230)
Note over DM,Email: 이메일 수집 실패 Note over DM,Email: 이메일 수집 실패
DM->>Email: GET /messages DM->>Email: GET /messages
Email->>DB: SELECT gmail_tokens Email->>DB: SELECT gmail_token
DB-->>Email: token_data: NULL ❌ DB-->>Email: token_data: NULL ❌
Email-->>DM: 500 Internal Server Error Email-->>DM: 500 Internal Server Error
Note over DM: 이메일 데이터 = [] Note over DM: 이메일 데이터 = []
@ -199,7 +199,7 @@ docker logs rb8001 --tail 100 | grep daily-summary
docker logs skill-email --tail 50 | grep ERROR docker logs skill-email --tail 50 | grep ERROR
# DB 토큰 상태 # DB 토큰 상태
psql -U robeings -d main_db -c "SELECT user_id, token_data IS NOT NULL as has_token FROM gmail_tokens;" psql -U robeings -d main_db -c "SELECT user_id, token_data IS NOT NULL as has_token FROM gmail_token;"
``` ```
--- ---

View File

@ -53,7 +53,7 @@ sequenceDiagram
Auth->>Google: 토큰 교환 요청 Auth->>Google: 토큰 교환 요청
Google-->>Auth: access_token, refresh_token Google-->>Auth: access_token, refresh_token
Auth->>DB: gmail_tokens 테이블 저장 Auth->>DB: gmail_token 테이블 저장
Note over DB: user_id, email,<br/>tokens (암호화),<br/>scopes, metadata Note over DB: user_id, email,<br/>tokens (암호화),<br/>scopes, metadata
Auth->>Auth: 리다이렉트 URL 결정 Auth->>Auth: 리다이렉트 URL 결정
@ -121,7 +121,7 @@ sequenceDiagram
Gateway->>Monitor: 아이템 목록 요청 Gateway->>Monitor: 아이템 목록 요청
Note over Monitor: X-User-Id 헤더로<br/>UUID 전달 Note over Monitor: X-User-Id 헤더로<br/>UUID 전달
Monitor->>DB: gmail_tokens 조회 Monitor->>DB: gmail_token 조회
Note over DB: user_id로 필터링 Note over DB: user_id로 필터링
Monitor->>DB: robeing_stats 조회 Monitor->>DB: robeing_stats 조회
@ -153,7 +153,7 @@ sequenceDiagram
Gateway-->>Front: 레벨 부족 에러 Gateway-->>Front: 레벨 부족 에러
Front->>Front: "레벨 5가 되면<br/>사용 가능해요!" 표시 Front->>Front: "레벨 5가 되면<br/>사용 가능해요!" 표시
else 레벨 >= 5 else 레벨 >= 5
Monitor->>DB: gmail_tokens 업데이트 Monitor->>DB: gmail_token 업데이트
Note over DB: is_equipped = true<br/>equipped_to = "rb10508_micro" Note over DB: is_equipped = true<br/>equipped_to = "rb10508_micro"
Monitor->>DB: gmail_audit_logs 기록 Monitor->>DB: gmail_audit_logs 기록
@ -203,7 +203,7 @@ sequenceDiagram
RB->>Monitor: GET /api/items/gmail/status RB->>Monitor: GET /api/items/gmail/status
Note over Monitor: 장착 상태 확인 Note over Monitor: 장착 상태 확인
Monitor->>DB: gmail_tokens 조회 Monitor->>DB: gmail_token 조회
DB-->>Monitor: 장착 정보 DB-->>Monitor: 장착 정보
alt Gmail 아이템 미장착 alt Gmail 아이템 미장착
@ -216,7 +216,7 @@ sequenceDiagram
RB->>Skill: POST /send-email RB->>Skill: POST /send-email
Note over Skill: user_id, to,<br/>subject, body Note over Skill: user_id, to,<br/>subject, body
Skill->>DB: gmail_tokens 조회 Skill->>DB: gmail_token 조회
Note over DB: user_id와 is_equipped 확인 Note over DB: user_id와 is_equipped 확인
DB-->>Skill: access_token (복호화) DB-->>Skill: access_token (복호화)
@ -232,7 +232,7 @@ sequenceDiagram
Gmail-->>Skill: 발송 성공 (message_id) Gmail-->>Skill: 발송 성공 (message_id)
Skill->>DB: 발송 로그 저장 Skill->>DB: 발송 로그 저장
Note over DB: conversation_logs 또는<br/>email_send_history Note over DB: conversation_log 또는<br/>email_send_history
Skill-->>RB: 발송 결과 Skill-->>RB: 발송 결과
RB->>DB: robeing_stats 경험치 증가 RB->>DB: robeing_stats 경험치 증가
@ -273,7 +273,7 @@ sequenceDiagram
DB-->>RB: user_id, name: "김종태" DB-->>RB: user_id, name: "김종태"
RB->>Monitor: Gmail 아이템 상태 확인 RB->>Monitor: Gmail 아이템 상태 확인
Monitor->>DB: gmail_tokens 조회 Monitor->>DB: gmail_token 조회
alt 아이템 미장착 alt 아이템 미장착
DB-->>Monitor: is_equipped = false DB-->>Monitor: is_equipped = false
@ -293,7 +293,7 @@ sequenceDiagram
RB->>Skill: POST /send-email RB->>Skill: POST /send-email
Note over Skill: to: goeun2dc@gmail.com<br/>subject: "회의 일정 안내"<br/>body: LLM 생성 내용 Note over Skill: to: goeun2dc@gmail.com<br/>subject: "회의 일정 안내"<br/>body: LLM 생성 내용
Skill->>DB: gmail_tokens 조회 Skill->>DB: gmail_token 조회
DB-->>Skill: access_token DB-->>Skill: access_token
Skill->>Gmail: 이메일 발송 Skill->>Gmail: 이메일 발송
@ -302,7 +302,7 @@ sequenceDiagram
Skill-->>RB: 발송 완료 Skill-->>RB: 발송 완료
RB->>DB: 대화 로그 저장 RB->>DB: 대화 로그 저장
Note over DB: conversation_logs<br/>이메일 발송 기록 Note over DB: conversation_log에<br/>이메일 발송 기록
RB-->>Slack: "종태님께 회의 일정 메일을<br/>발송했습니다." RB-->>Slack: "종태님께 회의 일정 메일을<br/>발송했습니다."
end end
@ -345,7 +345,7 @@ sequenceDiagram
Auth->>Google: 토큰 교환 Auth->>Google: 토큰 교환
Google-->>Auth: 새 access_token, refresh_token Google-->>Auth: 새 access_token, refresh_token
Auth->>DB: gmail_tokens 업데이트 Auth->>DB: gmail_token 업데이트
Note over DB: 새 토큰으로 교체<br/>updated_at 갱신 Note over DB: 새 토큰으로 교체<br/>updated_at 갱신
Auth->>DB: gmail_audit_logs 기록 Auth->>DB: gmail_audit_logs 기록
@ -372,7 +372,7 @@ sequenceDiagram
Slack->>RB: 이메일 요청 Slack->>RB: 이메일 요청
RB->>Monitor: Gmail 아이템 확인 RB->>Monitor: Gmail 아이템 확인
Monitor->>DB: gmail_tokens 조회 Monitor->>DB: gmail_token 조회
DB-->>Monitor: 결과 DB-->>Monitor: 결과
@ -417,7 +417,7 @@ sequenceDiagram
RB->>Skill: GET /list-emails RB->>Skill: GET /list-emails
Note over Skill: maxResults: 10<br/>q: "is:unread" Note over Skill: maxResults: 10<br/>q: "is:unread"
Skill->>DB: gmail_tokens 조회 Skill->>DB: gmail_token 조회
DB-->>Skill: access_token DB-->>Skill: access_token
Skill->>Gmail: GET /gmail/v1/users/me/messages Skill->>Gmail: GET /gmail/v1/users/me/messages
@ -510,7 +510,7 @@ sequenceDiagram
## 데이터베이스 테이블 ## 데이터베이스 테이블
### gmail_tokens ### gmail_token
- id: UUID - id: UUID
- user_id: UUID (users 테이블 참조) - user_id: UUID (users 테이블 참조)
- robeing_id: VARCHAR (통일된 컬럼명) - robeing_id: VARCHAR (통일된 컬럼명)

View File

@ -41,7 +41,7 @@ graph TB
subgraph Database subgraph Database
PG[(PostgreSQL)] PG[(PostgreSQL)]
GT[gmail_tokens] GT[gmail_token]
RS[robeing_stats] RS[robeing_stats]
end end
@ -87,7 +87,7 @@ sequenceDiagram
Note over Gateway: JWT 토큰 검증 Note over Gateway: JWT 토큰 검증
Gateway->>Monitor: GET /api/items/gmail<br/>Header: X-User-Id Gateway->>Monitor: GET /api/items/gmail<br/>Header: X-User-Id
Monitor->>DB: SELECT * FROM gmail_tokens<br/>WHERE user_id = ? Monitor->>DB: SELECT * FROM gmail_token<br/>WHERE user_id = ?
DB-->>Monitor: 토큰 데이터 DB-->>Monitor: 토큰 데이터
Monitor->>Monitor: 아이템 상태 계산 Monitor->>Monitor: 아이템 상태 계산
@ -152,7 +152,7 @@ sequenceDiagram
Auth->>Google: 코드 → 토큰 교환 Auth->>Google: 코드 → 토큰 교환
Google-->>Auth: Access Token + Refresh Token Google-->>Auth: Access Token + Refresh Token
Auth->>DB: INSERT INTO gmail_tokens Auth->>DB: INSERT INTO gmail_token
Note over DB: token_data, scopes,<br/>metadata (email 등) Note over DB: token_data, scopes,<br/>metadata (email 등)
Auth->>Front: 리다이렉트 /inventory?success=true Auth->>Front: 리다이렉트 /inventory?success=true
@ -192,7 +192,7 @@ sequenceDiagram
Monitor->>DB: 토큰 소유권 확인 Monitor->>DB: 토큰 소유권 확인
DB-->>Monitor: 토큰 존재 확인 DB-->>Monitor: 토큰 존재 확인
Monitor->>DB: UPDATE gmail_tokens<br/>SET is_equipped = true,<br/>equipped_to = 'rb10508_micro' Monitor->>DB: UPDATE gmail_token<br/>SET is_equipped = true,<br/>equipped_to = 'rb10508_micro'
DB-->>Monitor: 업데이트 완료 DB-->>Monitor: 업데이트 완료
Monitor->>DB: INSERT INTO gmail_audit_logs Monitor->>DB: INSERT INTO gmail_audit_logs
@ -222,11 +222,11 @@ sequenceDiagram
DB-->>Monitor: level: 3 DB-->>Monitor: level: 3
Monitor-->>Front: {error: "레벨 5 이상 필요"} Monitor-->>Front: {error: "레벨 5 이상 필요"}
else 토큰 없음 else 토큰 없음
Monitor->>DB: SELECT * FROM gmail_tokens Monitor->>DB: SELECT * FROM gmail_token
DB-->>Monitor: null DB-->>Monitor: null
Monitor-->>Front: {error: "Gmail 연결 필요"} Monitor-->>Front: {error: "Gmail 연결 필요"}
else 이미 장착됨 else 이미 장착됨
Monitor->>DB: SELECT is_equipped FROM gmail_tokens Monitor->>DB: SELECT is_equipped FROM gmail_token
DB-->>Monitor: is_equipped: true DB-->>Monitor: is_equipped: true
Monitor-->>Front: {error: "이미 장착됨"} Monitor-->>Front: {error: "이미 장착됨"}
end end
@ -254,7 +254,7 @@ sequenceDiagram
RB->>Monitor: GET /api/items/gmail RB->>Monitor: GET /api/items/gmail
Note over Monitor: Header: X-User-Id Note over Monitor: Header: X-User-Id
Monitor->>DB: SELECT * FROM gmail_tokens<br/>WHERE user_id = ? Monitor->>DB: SELECT * FROM gmail_token<br/>WHERE user_id = ?
DB-->>Monitor: 토큰 데이터 DB-->>Monitor: 토큰 데이터
Monitor-->>RB: {equipped: {is_equipped: true,<br/>capabilities: {send: true, read: true}}} Monitor-->>RB: {equipped: {is_equipped: true,<br/>capabilities: {send: true, read: true}}}
@ -263,7 +263,7 @@ sequenceDiagram
RB->>SE: POST /process RB->>SE: POST /process
Note over SE: message: "최근 이메일 확인" Note over SE: message: "최근 이메일 확인"
SE->>DB: SELECT token_data FROM gmail_tokens SE->>DB: SELECT token_data FROM gmail_token
DB-->>SE: OAuth 토큰 DB-->>SE: OAuth 토큰
SE->>SE: Gmail API 호출 SE->>SE: Gmail API 호출
@ -314,7 +314,7 @@ sequenceDiagram
User->>Front: "해제" 버튼 클릭 User->>Front: "해제" 버튼 클릭
Front->>Monitor: POST /api/items/gmail/{userId}/unequip Front->>Monitor: POST /api/items/gmail/{userId}/unequip
Monitor->>DB: UPDATE gmail_tokens<br/>SET is_equipped = false,<br/>equipped_to = null Monitor->>DB: UPDATE gmail_token<br/>SET is_equipped = false,<br/>equipped_to = null
DB-->>Monitor: 업데이트 완료 DB-->>Monitor: 업데이트 완료
Monitor->>DB: INSERT INTO gmail_audit_logs Monitor->>DB: INSERT INTO gmail_audit_logs
@ -367,7 +367,7 @@ sequenceDiagram
Note over Front: 페이지 새로고침 Note over Front: 페이지 새로고침
Front->>Monitor: GET /api/items/gmail Front->>Monitor: GET /api/items/gmail
Monitor->>DB: SELECT * FROM gmail_tokens Monitor->>DB: SELECT * FROM gmail_token
DB-->>Monitor: is_equipped: true DB-->>Monitor: is_equipped: true
Monitor-->>Front: 장착 상태 반환 Monitor-->>Front: 장착 상태 반환
@ -384,14 +384,14 @@ sequenceDiagram
participant Auth as auth-server participant Auth as auth-server
participant Google as Google OAuth participant Google as Google OAuth
SE->>DB: SELECT token_data FROM gmail_tokens SE->>DB: SELECT token_data FROM gmail_token
DB-->>SE: 토큰 (만료됨) DB-->>SE: 토큰 (만료됨)
SE->>Auth: 토큰 갱신 요청 SE->>Auth: 토큰 갱신 요청
Auth->>Google: Refresh Token 사용 Auth->>Google: Refresh Token 사용
Google-->>Auth: 새 Access Token Google-->>Auth: 새 Access Token
Auth->>DB: UPDATE gmail_tokens<br/>SET token_data = ? Auth->>DB: UPDATE gmail_token<br/>SET token_data = ?
DB-->>Auth: 업데이트 완료 DB-->>Auth: 업데이트 완료
Auth-->>SE: 새 토큰 Auth-->>SE: 새 토큰

View File

@ -221,20 +221,20 @@ for slack_id, name in slack_ids.items():
```sql ```sql
-- 1. 임시 컬럼 추가 -- 1. 임시 컬럼 추가
ALTER TABLE gmail_tokens ADD COLUMN user_uuid UUID; ALTER TABLE gmail_token ADD COLUMN user_uuid UUID;
-- 2. UUID 매핑 -- 2. UUID 매핑
UPDATE gmail_tokens gt UPDATE gmail_token gt
SET user_uuid = u.id SET user_uuid = u.id
FROM users u FROM users u
WHERE gt.user_id = u.username; WHERE gt.user_id = u.username;
-- 3. 기존 컬럼 제거 및 이름 변경 -- 3. 기존 컬럼 제거 및 이름 변경
ALTER TABLE gmail_tokens DROP COLUMN user_id; ALTER TABLE gmail_token DROP COLUMN user_id;
ALTER TABLE gmail_tokens RENAME COLUMN user_uuid TO user_id; ALTER TABLE gmail_token RENAME COLUMN user_uuid TO user_id;
-- 4. 외래키 제약 추가 -- 4. 외래키 제약 추가
ALTER TABLE gmail_tokens ALTER TABLE gmail_token
ADD CONSTRAINT fk_user_id ADD CONSTRAINT fk_user_id
FOREIGN KEY (user_id) REFERENCES users(id); FOREIGN KEY (user_id) REFERENCES users(id);
``` ```

View File

@ -10,10 +10,10 @@ auth-server는 개별 사용자 인증보다는 **회사별 Slack 봇 인증 및
## 테이블 구조 ## 테이블 구조
### 1. companies (회사/조직 관리) ### 1. company (회사/조직 관리)
```sql ```sql
CREATE TABLE companies ( CREATE TABLE company (
id uuid PRIMARY KEY, id uuid PRIMARY KEY,
name varchar(255) NOT NULL, name varchar(255) NOT NULL,
subdomain varchar(100) NOT NULL UNIQUE, subdomain varchar(100) NOT NULL UNIQUE,
@ -41,7 +41,7 @@ CREATE TABLE companies (
```sql ```sql
CREATE TABLE slack_workspaces ( CREATE TABLE slack_workspaces (
id uuid PRIMARY KEY, id uuid PRIMARY KEY,
company_id uuid NOT NULL REFERENCES companies(id), company_id uuid NOT NULL REFERENCES company(id),
team_id varchar(100) NOT NULL UNIQUE, team_id varchar(100) NOT NULL UNIQUE,
team_name varchar(255), team_name varchar(255),
bot_token text, bot_token text,
@ -71,7 +71,7 @@ CREATE TABLE slack_workspaces (
**인덱스**: **인덱스**:
- Primary Key: `id` - Primary Key: `id`
- Unique: `team_id` - Unique: `team_id`
- Foreign Key: `company_id``companies(id)` - Foreign Key: `company_id``company(id)`
## 현재 데이터 현황 ## 현재 데이터 현황

View File

@ -88,7 +88,7 @@
```sql ```sql
-- PostgreSQL@51123:5432 -- PostgreSQL@51123:5432
├── main_db (기존) ├── main_db (기존)
│ ├── companies │ ├── company
│ └── slack_workspaces │ └── slack_workspaces
└── robbing_db (신규 제안) └── robbing_db (신규 제안)
@ -117,7 +117,7 @@ WS /ws/robbing/realtime # 실시간 상태 업데이트
**위치**: auth-server 내장 또는 별도 React 앱 **위치**: auth-server 내장 또는 별도 React 앱
**데이터 저장**: **데이터 저장**:
- 회사 정보: PostgreSQL `main_db.companies` - 회사 정보: PostgreSQL `main_db.company`
- 세션 관리: JWT 토큰 - 세션 관리: JWT 토큰
- OAuth 토큰: 파일시스템 `/tokens/` - OAuth 토큰: 파일시스템 `/tokens/`

View File

@ -119,7 +119,7 @@ async def level_up_user(user_id: str, stat: str, points: int):
```sql ```sql
-- PostgreSQL 스키마 -- PostgreSQL 스키마
CREATE TABLE companies ( CREATE TABLE company (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
name VARCHAR(255), name VARCHAR(255),
slack_workspace_id VARCHAR(50) UNIQUE, slack_workspace_id VARCHAR(50) UNIQUE,
@ -132,7 +132,7 @@ CREATE TABLE companies (
CREATE TABLE users ( CREATE TABLE users (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id), company_id UUID REFERENCES company(id),
slack_user_id VARCHAR(50), slack_user_id VARCHAR(50),
stats JSONB DEFAULT '{"연산": 0, "기억": 0, "공감": 0, "통솔": 0, "반응": 0}', stats JSONB DEFAULT '{"연산": 0, "기억": 0, "공감": 0, "통솔": 0, "반응": 0}',
level INTEGER DEFAULT 1, level INTEGER DEFAULT 1,

View File

@ -299,7 +299,7 @@ email ↔ slack_id ↔ username
##### 현재 테이블 구조 (5개) ##### 현재 테이블 구조 (5개)
- **users**: 사용자 기본 정보 (email, username) - **users**: 사용자 기본 정보 (email, username)
- **workspaces**: 워크스페이스 정보 (로빙 할당) - **workspaces**: 워크스페이스 정보 (로빙 할당)
- **workspace_members**: 사용자-워크스페이스 연결 - **workspace_member**: 사용자-워크스페이스 연결
- **slack_workspaces**: Slack 워크스페이스 정보 - **slack_workspaces**: Slack 워크스페이스 정보
- **slack_user_mapping**: Slack ID ↔ User ID 매핑 (3개만 존재) - **slack_user_mapping**: Slack ID ↔ User ID 매핑 (3개만 존재)

View File

@ -298,16 +298,16 @@ class SlackItem(APIItem):
- 워크스페이스 정보 (robeing_id, robeing_port, robeing_url 포함) - 워크스페이스 정보 (robeing_id, robeing_port, robeing_url 포함)
- 현재 1개 워크스페이스: ivada-robeing (rb10508_micro 사용) - 현재 1개 워크스페이스: ivada-robeing (rb10508_micro 사용)
3. **workspace_members 테이블** 3. **workspace_member 테이블**
- 사용자와 워크스페이스 연결 - 사용자와 워크스페이스 연결
- robeing_id 필드로 각 멤버가 사용할 로빙 지정 가능 - robeing_id 필드로 각 멤버가 사용할 로빙 지정 가능
4. **slack_workspaces 테이블** 4. **slack_workspaces 테이블**
- Slack 워크스페이스 정보 (team_id, bot_token, bot_user_id 등) - Slack 워크스페이스 정보 (team_id, bot_token, bot_user_id 등)
- 2개 Slack 워크스페이스 등록됨 (GoodGang Labs, test) - 2개 Slack 워크스페이스 등록됨 (GoodGang Labs, test)
- companies 테이블과 연결됨 - company 테이블과 연결됨
5. **companies 테이블** 5. **company 테이블**
- 회사 정보 관리 - 회사 정보 관리
- slack_workspaces와 1:N 관계 - slack_workspaces와 1:N 관계
@ -329,7 +329,7 @@ CREATE TABLE slack_user_mapping (
slack_user_id VARCHAR(100) NOT NULL, slack_user_id VARCHAR(100) NOT NULL,
slack_workspace_id INTEGER REFERENCES slack_workspaces(id), slack_workspace_id INTEGER REFERENCES slack_workspaces(id),
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
workspace_member_id UUID REFERENCES workspace_members(id), workspace_member_id UUID REFERENCES workspace_member(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(slack_user_id, slack_workspace_id) UNIQUE(slack_user_id, slack_workspace_id)
@ -345,7 +345,7 @@ slack_user_mapping 조회
user_id 획득 user_id 획득
workspace_members에서 robeing_id 확인 workspace_member에서 robeing_id 확인
해당 로빙으로 라우팅 해당 로빙으로 라우팅

View File

@ -1,4 +1,4 @@
# conversation_logs 및 robeing_stats 테이블 활용 계획 # conversation_log 및 robeing_stats 테이블 활용 계획
작성일: 2025년 8월 18일 작성일: 2025년 8월 18일
작성자: Claude (51123 서버) 작성자: Claude (51123 서버)
@ -9,7 +9,7 @@
### 1.1 미사용 테이블 현황 ### 1.1 미사용 테이블 현황
```sql ```sql
-- main_db에 존재하지만 전혀 사용되지 않는 테이블들 -- main_db에 존재하지만 전혀 사용되지 않는 테이블들
conversation_logs: 0 records (0 KB) conversation_log: 0 records (0 KB)
robeing_stats: 0 records (0 KB) robeing_stats: 0 records (0 KB)
robeing_settings: 0 records (0 KB) robeing_settings: 0 records (0 KB)
``` ```
@ -25,11 +25,11 @@ robeing_settings: 0 records (0 KB)
3. **개인화 불가**: 사용자별 설정이 저장되지 않음 3. **개인화 불가**: 사용자별 설정이 저장되지 않음
4. **분석 불가**: 사용 패턴, 성능 지표 추적 불가 4. **분석 불가**: 사용 패턴, 성능 지표 추적 불가
## 2. conversation_logs 테이블 활용 방안 ## 2. conversation_log 테이블 활용 방안
### 2.1 테이블 구조 (현재) ### 2.1 테이블 구조 (현재)
```sql ```sql
CREATE TABLE conversation_logs ( CREATE TABLE conversation_log (
id INTEGER PRIMARY KEY (auto-increment), id INTEGER PRIMARY KEY (auto-increment),
robeing_id VARCHAR, robeing_id VARCHAR,
channel_id VARCHAR, channel_id VARCHAR,
@ -70,7 +70,7 @@ class ConversationLogger:
"""대화 내용을 DB에 저장""" """대화 내용을 DB에 저장"""
async with asyncpg.connect(self.db_url) as conn: async with asyncpg.connect(self.db_url) as conn:
await conn.execute(""" await conn.execute("""
INSERT INTO conversation_logs INSERT INTO conversation_log
(id, robeing_id, user_id, message, response, created_at, metadata) (id, robeing_id, user_id, message, response, created_at, metadata)
VALUES ($1, $2, $3, $4, $5, $6, $7) VALUES ($1, $2, $3, $4, $5, $6, $7)
""", """,
@ -307,7 +307,7 @@ skill_settings = {
## 5. 구현 우선순위 ## 5. 구현 우선순위
### Phase 1: 즉시 구현 (1주일) ### Phase 1: 즉시 구현 (1주일)
1. **conversation_logs 저장 기능** 1. **conversation_log 저장 기능**
- 모든 대화를 DB에 저장 - 모든 대화를 DB에 저장
- 메타데이터 포함 (채널, 시간 등) - 메타데이터 포함 (채널, 시간 등)
@ -337,12 +337,12 @@ skill_settings = {
### 6.1 인덱스 추가 필요 ### 6.1 인덱스 추가 필요
```sql ```sql
-- conversation_logs 빠른 조회 -- conversation_log 빠른 조회
CREATE INDEX idx_conversation_user_time CREATE INDEX idx_conversation_user_time
ON conversation_logs(user_id, created_at DESC); ON conversation_log(user_id, created_at DESC);
CREATE INDEX idx_conversation_robeing_time CREATE INDEX idx_conversation_robeing_time
ON conversation_logs(robeing_id, created_at DESC); ON conversation_log(robeing_id, created_at DESC);
-- robeing_stats 빠른 조회 -- robeing_stats 빠른 조회
CREATE INDEX idx_robeing_stats_robeing CREATE INDEX idx_robeing_stats_robeing
@ -363,7 +363,7 @@ ON robeing_stats(robeing_id);
```sql ```sql
-- 일일 대화량 -- 일일 대화량
SELECT DATE(created_at), COUNT(*) SELECT DATE(created_at), COUNT(*)
FROM conversation_logs FROM conversation_log
GROUP BY DATE(created_at); GROUP BY DATE(created_at);
-- 로빙별 활동 -- 로빙별 활동
@ -396,7 +396,7 @@ FROM robeing_stats;
## 9. 주의사항 ## 9. 주의사항
### 9.1 개인정보 보호 ### 9.1 개인정보 보호
- conversation_logs에 민감 정보 마스킹 - conversation_log에 민감 정보 마스킹
- GDPR 준수 (삭제 요청 처리) - GDPR 준수 (삭제 요청 처리)
- 암호화 고려 - 암호화 고려

View File

@ -217,7 +217,7 @@ flowchart TD
## 3.4 시간 인식 의도 처리 (신규 추가 2025-09-09) ## 3.4 시간 인식 의도 처리 (신규 추가 2025-09-09)
### 3.4.1 실제 대화 로그 분석 결과 ### 3.4.1 실제 대화 로그 분석 결과
2025년 9월 9일 PostgreSQL conversation_logs 테이블 분석 결과, 로빙의 가장 심각한 문제는 **시간 인식 부재**입니다. 2025년 9월 9일 PostgreSQL conversation_log 테이블 분석 결과, 로빙의 가장 심각한 문제는 **시간 인식 부재**입니다.
``` ```
실제 사례: 실제 사례:
@ -250,7 +250,7 @@ CONTEXT_RETRIEVAL_PATTERNS = {
# DB에서 과거 대화 조회 필수 # DB에서 과거 대화 조회 필수
if pattern_matches(CONTEXT_RETRIEVAL_PATTERNS): if pattern_matches(CONTEXT_RETRIEVAL_PATTERNS):
past_logs = await fetch_conversation_logs( past_logs = await fetch_conversation_log(
user_id=user_id, user_id=user_id,
time_range="-2 hours", time_range="-2 hours",
limit=5 limit=5

View File

@ -26,7 +26,7 @@
1. **실시간 동기화** - WebSocket/폴링 미구현 (영향도 재평가 필요) (2주) [→250827](../troubleshooting/250827_frontend_backend_preferences_API_연동_완료.md) 1. **실시간 동기화** - WebSocket/폴링 미구현 (영향도 재평가 필요) (2주) [→250827](../troubleshooting/250827_frontend_backend_preferences_API_연동_완료.md)
2. **PKCE 적용** - OAuth 보안 강화 미구현 (1주) [→380](../300_architecture/380_authentication_system.md) 2. **PKCE 적용** - OAuth 보안 강화 미구현 (1주) [→380](../300_architecture/380_authentication_system.md)
3. **Refresh Token** - 토큰 재발급 체계 미구현 (1주) [→380](../300_architecture/380_authentication_system.md) 3. **Refresh Token** - 토큰 재발급 체계 미구현 (1주) [→380](../300_architecture/380_authentication_system.md)
4. **DB 스키마 통일** - companies→workspaces 미구현 (2주) [→250831](250831_todo_and_tech_debt.md) 4. **DB 스키마 통일** - company→workspaces 미구현 (2주) [→250831](250831_todo_and_tech_debt.md)
5. **스킬 라우팅 고도화** - Decision Engine 미구현 (3주) [→250811](../ideas/250811_claude_rb10408_vs_rb10508_비교분석.md) 5. **스킬 라우팅 고도화** - Decision Engine 미구현 (3주) [→250811](../ideas/250811_claude_rb10408_vs_rb10508_비교분석.md)
6. **IntentAnalyzer 활성화** - 현재 미사용, LLM 기반 의도분석기 미연동 (1주) [→250914](../troubleshooting/250914_happybell80_IntentAnalyzer_미사용_문제.md) 6. **IntentAnalyzer 활성화** - 현재 미사용, LLM 기반 의도분석기 미연동 (1주) [→250914](../troubleshooting/250914_happybell80_IntentAnalyzer_미사용_문제.md)
7. **전역 ENV 감사** - 레포 전반 미사용/중복 ENV 정리 미구현 (2주) 7. **전역 ENV 감사** - 레포 전반 미사용/중복 ENV 정리 미구현 (2주)

View File

@ -16,12 +16,12 @@
### 2. DB 스키마 불일치 ### 2. DB 스키마 불일치
**문제**: **문제**:
- companies vs workspaces 테이블 공존 - company vs workspaces 테이블 공존
- SlackWorkspace 모델 FK 오류 (company_id를 workspace_id로 참조) - SlackWorkspace 모델 FK 오류 (company_id를 workspace_id로 참조)
**해결 계획**: **해결 계획**:
1. 데이터 백업 1. 데이터 백업
2. companies → workspaces 마이그레이션 2. company → workspaces 마이그레이션
3. FK 관계 재설정 3. FK 관계 재설정
4. 모델 파일 통일 4. 모델 파일 통일
@ -99,7 +99,7 @@ fetch('https://auth.ro-being.com/auth/verify')
## 체크리스트 ## 체크리스트
- [ ] 하드코딩 URL 환경변수화 - [ ] 하드코딩 URL 환경변수화
- [ ] DB 스키마 통일 (companies → workspaces) - [ ] DB 스키마 통일 (company → workspaces)
- [ ] 임시 코드 방식 제거 - [ ] 임시 코드 방식 제거
- [ ] UserIdentityService 구현 - [ ] UserIdentityService 구현
- [ ] Refresh Token 구현 - [ ] Refresh Token 구현

View File

@ -50,9 +50,9 @@ SET user_uuid = (
AND sum.team_id = gp.team_id AND sum.team_id = gp.team_id
); );
-- workspace_members UUID 확인 -- workspace_member UUID 확인
ALTER TABLE workspace_members ALTER TABLE workspace_member
ADD CONSTRAINT fk_workspace_members_user_id ADD CONSTRAINT fk_workspace_member_user_id
FOREIGN KEY (user_id) REFERENCES users(id); FOREIGN KEY (user_id) REFERENCES users(id);
``` ```
@ -406,4 +406,4 @@ if not uuid_mapping:
- [250828_slack_auth_integration_completed.md](../troubleshooting/250828_slack_auth_integration_completed.md) - [250828_slack_auth_integration_completed.md](../troubleshooting/250828_slack_auth_integration_completed.md)
- [250828_slack_integration_level3_plan.md](./250828_slack_integration_level3_plan.md) - [250828_slack_integration_level3_plan.md](./250828_slack_integration_level3_plan.md)
- [250828_conversation_logs_channel_구분_개선.md](../troubleshooting/250828_conversation_logs_channel_구분_개선.md) - [250828_conversation_log_channel_구분_개선.md](../troubleshooting/250828_conversation_log_channel_구분_개선.md)

View File

@ -3,21 +3,21 @@
## 작성일: 2025년 8월 31일 ## 작성일: 2025년 8월 31일
## 🎯 목표 ## 🎯 목표
`companies` 테이블과 `workspaces` 테이블을 `workspaces`로 통합하여 DB 스키마 일관성 확보 `company` 테이블과 `workspaces` 테이블을 `workspaces`로 통합하여 DB 스키마 일관성 확보
## 📊 현재 상황 ## 📊 현재 상황
### 테이블 구조 비교 ### 테이블 구조 비교
| 구분 | companies | workspaces | | 구분 | company | workspaces |
|------|-----------|------------| |------|-----------|------------|
| **포트 컬럼** | `container_port` | `robeing_port` | | **포트 컬럼** | `container_port` | `robeing_port` |
| **FK 참조** | `slack_workspaces.company_id` | `workspace_members.workspace_id` | | **FK 참조** | `slack_workspaces.company_id` | `workspace_member.workspace_id` |
| **추가 컬럼** | - | `robeing_id`, `robeing_url`, `max_members`, `workspace_type` | | **추가 컬럼** | - | `robeing_id`, `robeing_url`, `max_members`, `workspace_type` |
| **데이터** | 4개 레코드 | 2개 레코드 | | **데이터** | 4개 레코드 | 2개 레코드 |
### 문제점 ### 문제점
1. 중복 데이터 (Company-X가 양쪽 테이블에 존재) 1. 중복 데이터 (Company-X가 양쪽 테이블에 존재)
2. FK 관계 불일치 (slack_workspaces → companies, workspace_members → workspaces) 2. FK 관계 불일치 (slack_workspaces → company, workspace_member → workspaces)
3. 코드에서 잘못된 참조 (`workspace.workspace`, `workspace.robeing_port`) 3. 코드에서 잘못된 참조 (`workspace.workspace`, `workspace.robeing_port`)
## 🔧 작업 계획 ## 🔧 작업 계획
@ -25,15 +25,15 @@
### Phase 1: DB 백업 및 준비 ### Phase 1: DB 백업 및 준비
```bash ```bash
# 백업 생성 # 백업 생성
sudo -u postgres pg_dump -t companies -t workspaces -t slack_workspaces main_db > /home/admin/backup_workspace_$(date +%Y%m%d_%H%M%S).sql sudo -u postgres pg_dump -t company -t workspaces -t slack_workspaces main_db > /home/admin/backup_workspace_$(date +%Y%m%d_%H%M%S).sql
``` ```
### Phase 2: DB 스키마 변경 ### Phase 2: DB 스키마 변경
```sql ```sql
-- 1. companies 데이터를 workspaces로 이동 -- 1. company 데이터를 workspaces로 이동
INSERT INTO workspaces (id, name, subdomain, robeing_port, status, created_at, updated_at) INSERT INTO workspaces (id, name, subdomain, robeing_port, status, created_at, updated_at)
SELECT id, name, subdomain, container_port, status, created_at, updated_at SELECT id, name, subdomain, container_port, status, created_at, updated_at
FROM companies FROM company
WHERE id NOT IN (SELECT id FROM workspaces); WHERE id NOT IN (SELECT id FROM workspaces);
-- 2. slack_workspaces 테이블 수정 -- 2. slack_workspaces 테이블 수정
@ -48,8 +48,8 @@ ALTER TABLE slack_workspaces
ADD CONSTRAINT slack_workspaces_workspace_id_fkey ADD CONSTRAINT slack_workspaces_workspace_id_fkey
FOREIGN KEY (workspace_id) REFERENCES workspaces(id); FOREIGN KEY (workspace_id) REFERENCES workspaces(id);
-- 3. companies 테이블 제거 -- 3. company 테이블 제거
DROP TABLE companies CASCADE; DROP TABLE company CASCADE;
``` ```
### Phase 3: 코드 수정 ### Phase 3: 코드 수정
@ -123,7 +123,7 @@ curl -X POST https://auth.ro-being.com/slack/events/router \
```bash ```bash
cd /home/admin/auth-server cd /home/admin/auth-server
git add -A git add -A
git commit -m "refactor: companies 테이블을 workspaces로 통합" git commit -m "refactor: company 테이블을 workspaces로 통합"
git push origin main git push origin main
``` ```
@ -154,9 +154,9 @@ sudo -u postgres psql main_db < /home/admin/backup_workspace_YYYYMMDD_HHMMSS.sql
## 📝 체크리스트 ## 📝 체크리스트
- [ ] DB 백업 완료 - [ ] DB 백업 완료
- [ ] companies → workspaces 데이터 이동 - [ ] company → workspaces 데이터 이동
- [ ] slack_workspaces FK 변경 - [ ] slack_workspaces FK 변경
- [ ] companies 테이블 삭제 - [ ] company 테이블 삭제
- [ ] workspace.py 모델 수정 - [ ] workspace.py 모델 수정
- [ ] slack_router.py 수정 - [ ] slack_router.py 수정
- [ ] slack.py provider 수정 - [ ] slack.py provider 수정

View File

@ -9,7 +9,7 @@
### main_db3 (삭제 예정) ### main_db3 (삭제 예정)
- 총 13개 테이블, 614개 레코드 - 총 13개 테이블, 614개 레코드
- 주요 데이터: users(17), companies(4), conversation_logs(555) - 주요 데이터: users(17), company(4), conversation_log(555)
- Slack/Gmail 연동 데이터 보유 - Slack/Gmail 연동 데이터 보유
### main_db (운영 DB) ### main_db (운영 DB)
@ -22,14 +22,14 @@
| main_db3 | main_db | 비고 | | main_db3 | main_db | 비고 |
|----------|---------|------| |----------|---------|------|
| companies | company | 2개 중복 확인 필요 | | company | company | 2개 중복 확인 필요 |
| users | user | team_id 필수 추가 | | users | user | team_id 필수 추가 |
| workspaces | team | 구조 변환 필요 | | workspaces | team | 구조 변환 필요 |
| workspace_members | workspace_member | user_id 매핑 | | workspace_member | workspace_member | user_id 매핑 |
| slack_workspaces | slack_workspace | team_id 매핑 | | slack_workspaces | slack_workspace | team_id 매핑 |
| user_preferences | user_preference | slack_user_id 제거됨 | | user_preferences | user_preference | slack_user_id 제거됨 |
| conversation_logs | conversation_log | robeing_id 컬럼 없음 | | conversation_log | conversation_log | robeing_id 컬럼 없음 |
| gmail_tokens | gmail_token | 구조 동일 | | gmail_token | gmail_token | 구조 동일 |
| robeing_stats | robeing | product_id, team_id 추가 | | robeing_stats | robeing | product_id, team_id 추가 |
| slack_user_mapping | - | workspace_member로 통합 | | slack_user_mapping | - | workspace_member로 통합 |
@ -39,10 +39,10 @@
### Phase 1: 조직 구조 (필수 선행) ### Phase 1: 조직 구조 (필수 선행)
```sql ```sql
-- 1. companies → company (중복 제외) -- 1. company → company (중복 제외)
INSERT INTO company (id, name, url, created_at, updated_at) INSERT INTO company (id, name, url, created_at, updated_at)
SELECT id, name, domain, created_at, updated_at SELECT id, name, domain, created_at, updated_at
FROM main_db3.companies FROM main_db3.company
WHERE id NOT IN ('28f17b47-33f8-47ac-b1ae-100e77b37edb', '99d22d6b-d327-4fa4-bd2f-d228c11056e2'); WHERE id NOT IN ('28f17b47-33f8-47ac-b1ae-100e77b37edb', '99d22d6b-d327-4fa4-bd2f-d228c11056e2');
-- 2. workspaces → team 변환 -- 2. workspaces → team 변환
@ -58,11 +58,11 @@ SELECT u.id,
COALESCE(wm.workspace_id, '38bdc27d-cb01-4960-867e-41733d2f3529'), -- 기본 팀 COALESCE(wm.workspace_id, '38bdc27d-cb01-4960-867e-41733d2f3529'), -- 기본 팀
u.email, u.name, u.username, u.oauth_provider, u.oauth_id, u.is_active u.email, u.name, u.username, u.oauth_provider, u.oauth_id, u.is_active
FROM main_db3.users u FROM main_db3.users u
LEFT JOIN main_db3.workspace_members wm ON u.id = wm.user_id; LEFT JOIN main_db3.workspace_member wm ON u.id = wm.user_id;
-- 4. workspace_members → workspace_member (slack_user_id 추가 예정) -- 4. workspace_member → workspace_member (slack_user_id 추가 예정)
INSERT INTO workspace_member (id, user_id, role, is_active, joined_at) INSERT INTO workspace_member (id, user_id, role, is_active, joined_at)
SELECT id, user_id, role::user_role, is_active, joined_at FROM main_db3.workspace_members; SELECT id, user_id, role::user_role, is_active, joined_at FROM main_db3.workspace_member;
``` ```
### Phase 3: 통합 데이터 ### Phase 3: 통합 데이터
@ -75,16 +75,16 @@ SELECT id, workspace_id, team_id, bot_token, is_active FROM main_db3.slack_works
INSERT INTO user_preference (user_id, news_keywords, email_filter, briefing_enabled) INSERT INTO user_preference (user_id, news_keywords, email_filter, briefing_enabled)
SELECT user_id, news_keywords, email_filter, briefing_enabled FROM main_db3.user_preferences; SELECT user_id, news_keywords, email_filter, briefing_enabled FROM main_db3.user_preferences;
-- 7. gmail_tokens → gmail_token -- 7. gmail_token → gmail_token
INSERT INTO gmail_token SELECT * FROM main_db3.gmail_tokens; INSERT INTO gmail_token SELECT * FROM main_db3.gmail_token;
``` ```
### Phase 4: 대화 기록 ### Phase 4: 대화 기록
```sql ```sql
-- 8. conversation_logs → conversation_log (컬럼 매핑 주의) -- 8. conversation_log → conversation_log (컬럼 매핑 주의)
INSERT INTO conversation_log (user_id, channel_id, message, response, intent, confidence, timestamp) INSERT INTO conversation_log (user_id, channel_id, message, response, intent, confidence, timestamp)
SELECT user_id, channel_id, message, response, intent, confidence, timestamp SELECT user_id, channel_id, message, response, intent, confidence, timestamp
FROM main_db3.conversation_logs WHERE user_id IS NOT NULL; FROM main_db3.conversation_log WHERE user_id IS NOT NULL;
``` ```
--- ---

View File

@ -9,23 +9,23 @@
### 1.1 현재 테이블 (5개) ### 1.1 현재 테이블 (5개)
- **users**: 사용자 기본 정보 - **users**: 사용자 기본 정보
- **workspaces**: 워크스페이스 정보 (로빙 할당) - **workspaces**: 워크스페이스 정보 (로빙 할당)
- **workspace_members**: 사용자-워크스페이스 연결 - **workspace_member**: 사용자-워크스페이스 연결
- **companies**: 회사 정보 - **company**: 회사 정보
- **slack_workspaces**: Slack 워크스페이스 정보 - **slack_workspaces**: Slack 워크스페이스 정보
### 1.2 현재 구조의 문제점 ### 1.2 현재 구조의 문제점
#### 복잡도 문제 #### 복잡도 문제
1. **과도한 관계 분리** 1. **과도한 관계 분리**
- companies ↔ slack_workspaces ↔ workspaces 3단계 관계 - company ↔ slack_workspaces ↔ workspaces 3단계 관계
- 실제로는 1개 workspace만 사용 중 - 실제로는 1개 workspace만 사용 중
2. **중복 데이터** 2. **중복 데이터**
- robeing_id가 workspaces와 workspace_members 양쪽에 존재 - robeing_id가 workspaces와 workspace_member 양쪽에 존재
- robeing_url도 중복 저장 - robeing_url도 중복 저장
3. **불명확한 개념** 3. **불명확한 개념**
- companies의 실제 필요성 불분명 - company의 실제 필요성 불분명
- workspaces가 실제로 무엇을 나타내는지 모호 - workspaces가 실제로 무엇을 나타내는지 모호
4. **누락된 기능** 4. **누락된 기능**
@ -36,8 +36,8 @@
``` ```
users: 3명 (happybell80, eagle0914, hhyong91) users: 3명 (happybell80, eagle0914, hhyong91)
workspaces: 1개 (ivada-robeing) workspaces: 1개 (ivada-robeing)
workspace_members: 3개 (모두 rb10508_micro 사용) workspace_member: 3개 (모두 rb10508_micro 사용)
companies: 2개 (테스트 회사) company: 2개 (테스트 회사)
slack_workspaces: 2개 (GoodGang Labs, test) slack_workspaces: 2개 (GoodGang Labs, test)
``` ```
@ -197,8 +197,8 @@ CREATE TABLE slack_config (
```sql ```sql
-- 기존 데이터 백업 -- 기존 데이터 백업
CREATE TABLE backup_workspaces AS SELECT * FROM workspaces; CREATE TABLE backup_workspaces AS SELECT * FROM workspaces;
CREATE TABLE backup_workspace_members AS SELECT * FROM workspace_members; CREATE TABLE backup_workspace_member AS SELECT * FROM workspace_member;
CREATE TABLE backup_companies AS SELECT * FROM companies; CREATE TABLE backup_company AS SELECT * FROM company;
``` ```
#### Phase 2: 새 테이블 생성 #### Phase 2: 새 테이블 생성
@ -217,7 +217,7 @@ SELECT
'192.168.219.52' as robeing_host, '192.168.219.52' as robeing_host,
true as is_primary, true as is_primary,
joined_at as created_at joined_at as created_at
FROM workspace_members; FROM workspace_member;
-- slack_users 테이블 생성 (빈 테이블) -- slack_users 테이블 생성 (빈 테이블)
CREATE TABLE slack_users ( CREATE TABLE slack_users (
@ -238,9 +238,9 @@ CREATE TABLE slack_users (
ALTER TABLE slack_workspaces DROP CONSTRAINT slack_workspaces_company_id_fkey; ALTER TABLE slack_workspaces DROP CONSTRAINT slack_workspaces_company_id_fkey;
ALTER TABLE slack_workspaces DROP COLUMN company_id; ALTER TABLE slack_workspaces DROP COLUMN company_id;
DROP TABLE workspace_members; DROP TABLE workspace_member;
DROP TABLE workspaces; DROP TABLE workspaces;
DROP TABLE companies; DROP TABLE company;
``` ```
### 3.3 새로운 데이터 흐름 ### 3.3 새로운 데이터 흐름
@ -272,11 +272,11 @@ ChromaDB에 user_id 기반 저장
- 기존 테이블 영향 없음 - 기존 테이블 영향 없음
2. **단계적 마이그레이션**: user_robeings 테이블 2. **단계적 마이그레이션**: user_robeings 테이블
- workspace_members 데이터 이전 - workspace_member 데이터 이전
- 테스트 후 기존 테이블 제거 - 테스트 후 기존 테이블 제거
3. **정리 작업**: 불필요한 테이블 제거 3. **정리 작업**: 불필요한 테이블 제거
- companies, workspaces 제거 - company, workspaces 제거
- slack_workspaces 단순화 - slack_workspaces 단순화
## 5. API 변경사항 ## 5. API 변경사항
@ -284,7 +284,7 @@ ChromaDB에 user_id 기반 저장
### 5.1 사용자-로빙 조회 ### 5.1 사용자-로빙 조회
```python ```python
# 기존 # 기존
SELECT * FROM workspace_members WHERE user_id = ? SELECT * FROM workspace_member WHERE user_id = ?
# 변경 # 변경
SELECT * FROM user_robeings WHERE user_id = ? AND is_primary = true SELECT * FROM user_robeings WHERE user_id = ? AND is_primary = true

View File

@ -115,7 +115,7 @@ async def get_user_mapping(
wm.robeing_id wm.robeing_id
FROM slack_user_mapping sum FROM slack_user_mapping sum
JOIN users u ON sum.user_id = u.id JOIN users u ON sum.user_id = u.id
LEFT JOIN workspace_members wm ON u.id = wm.user_id LEFT JOIN workspace_member wm ON u.id = wm.user_id
WHERE sum.slack_user_id = :slack_user_id WHERE sum.slack_user_id = :slack_user_id
""" """
@ -230,7 +230,7 @@ CREATE TABLE IF NOT EXISTS slack_user_mapping (
slack_user_id VARCHAR(100) NOT NULL, slack_user_id VARCHAR(100) NOT NULL,
slack_workspace_id INTEGER REFERENCES slack_workspaces(id), slack_workspace_id INTEGER REFERENCES slack_workspaces(id),
user_id UUID REFERENCES users(id) NOT NULL, user_id UUID REFERENCES users(id) NOT NULL,
workspace_member_id UUID REFERENCES workspace_members(id), workspace_member_id UUID REFERENCES workspace_member(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(slack_user_id, slack_workspace_id) UNIQUE(slack_user_id, slack_workspace_id)

View File

@ -16,7 +16,7 @@
- [x] 기본 데이터 삽입 (rb8001, level=5) - [x] 기본 데이터 삽입 (rb8001, level=5)
- [x] 생성 확인 쿼리 실행 - [x] 생성 확인 쿼리 실행
### 1.2 gmail_tokens 테이블 수정 ### 1.2 gmail_token 테이블 수정
- [x] is_equipped 컬럼 추가 - [x] is_equipped 컬럼 추가
- [x] equipped_to 컬럼 추가 - [x] equipped_to 컬럼 추가
- [x] 기존 데이터 is_equipped=false로 초기화 - [x] 기존 데이터 is_equipped=false로 초기화
@ -232,7 +232,7 @@
- [x] Auth 서버 API 호출 - [x] Auth 서버 API 호출
- [x] 5분 캐싱 구현 - [x] 5분 캐싱 구현
- [ ] Gmail용 이메일 매핑 추가 - [ ] Gmail용 이메일 매핑 추가
- [ ] workspace_members 테이블에서 email 조회 - [ ] workspace_member 테이블에서 email 조회
- [ ] "종태님" → goeun2dc@gmail.com 변환 - [ ] "종태님" → goeun2dc@gmail.com 변환
### 4-1.6 테스트 시나리오 ### 4-1.6 테스트 시나리오

View File

@ -19,7 +19,7 @@ Gmail 자격증명을 "API 아이템"으로 정의하고, robeing의 장착형
### 관련 문서 ### 관련 문서
- `/250817_email_skill_integration_status.md` - 현황 분석 - `/250817_email_skill_integration_status.md` - 현황 분석
- `/250818_gmail_tokens_database_setup.md` - DB 구성 완료 - `/250818_gmail_token_database_setup.md` - DB 구성 완료
- `/ideas/250818_claude_robeing_레벨업_체감_시스템_설계.md` - 아이템 시스템 설계 - `/ideas/250818_claude_robeing_레벨업_체감_시스템_설계.md` - 아이템 시스템 설계
--- ---
@ -40,9 +40,9 @@ Gmail 자격증명을 "API 아이템"으로 정의하고, robeing의 장착형
### 데이터 모델 ### 데이터 모델
```typescript ```typescript
type GmailCredentialItem = { type GmailCredentialItem = {
id: string; // gmail_tokens.id id: string; // gmail_token.id
userId: string; // gmail_tokens.user_id userId: string; // gmail_token.user_id
robeing_id?: string; // gmail_tokens.robeing_id (통일) robeing_id?: string; // gmail_token.robeing_id (통일)
type: 'accessory'; type: 'accessory';
slot: 'passport_gmail'; slot: 'passport_gmail';
name: string; // metadata.display_name || email name: string; // metadata.display_name || email
@ -75,14 +75,14 @@ rb8001:8001 (메인 robeing)
skill-email:8501 robeing-monitor:9024 skill-email:8501 robeing-monitor:9024
(이메일 발송) (아이템 상태 관리) (이메일 발송) (아이템 상태 관리)
↓ ↓ ↓ ↓
PostgreSQL (gmail_tokens 테이블) PostgreSQL (gmail_token 테이블)
``` ```
### 서비스별 역할 ### 서비스별 역할
#### auth-server #### auth-server
- Gmail OAuth 플로우 처리 - Gmail OAuth 플로우 처리
- gmail_tokens 테이블 관리 - gmail_token 테이블 관리
- 토큰 갱신 로직 - 토큰 갱신 로직
#### robeing-monitor (9024) #### robeing-monitor (9024)
@ -125,14 +125,14 @@ sequenceDiagram
Gateway-->>Front: UNAUTHORIZED Gateway-->>Front: UNAUTHORIZED
else 인증 성공 else 인증 성공
Gateway->>Monitor: 장착 요청 전달 Gateway->>Monitor: 장착 요청 전달
Monitor->>DB: gmail_tokens 소유권 확인 Monitor->>DB: gmail_token 소유권 확인
Monitor->>DB: robeing_stats 조회 (레벨 체크) Monitor->>DB: robeing_stats 조회 (레벨 체크)
DB-->>Monitor: 레벨 정보 DB-->>Monitor: 레벨 정보
alt 레벨 < 5 alt 레벨 < 5
Monitor-->>Front: INSUFFICIENT_LEVEL Monitor-->>Front: INSUFFICIENT_LEVEL
else 레벨 >= 5 else 레벨 >= 5
Monitor->>DB: gmail_tokens 업데이트 Monitor->>DB: gmail_token 업데이트
Note over DB: is_equipped=true<br/>equipped_to=robeing_id Note over DB: is_equipped=true<br/>equipped_to=robeing_id
Monitor->>DB: gmail_audit_logs 기록 Monitor->>DB: gmail_audit_logs 기록
Monitor-->>RB: 장착 완료 알림 Monitor-->>RB: 장착 완료 알림
@ -155,7 +155,7 @@ sequenceDiagram
User->>Front: "이메일 보내줘" 요청 User->>Front: "이메일 보내줘" 요청
Front->>RB: 메시지 전달 Front->>RB: 메시지 전달
RB->>Monitor: Gmail 아이템 장착 확인 RB->>Monitor: Gmail 아이템 장착 확인
Monitor->>DB: gmail_tokens 조회 Monitor->>DB: gmail_token 조회
DB-->>Monitor: 장착 상태 & 토큰 정보 DB-->>Monitor: 장착 상태 & 토큰 정보
alt 아이템 미장착 alt 아이템 미장착
@ -195,7 +195,7 @@ sequenceDiagram
Google->>Auth: 인증 코드 콜백 Google->>Auth: 인증 코드 콜백
Auth->>Google: 토큰 교환 Auth->>Google: 토큰 교환
Google-->>Auth: access_token, refresh_token Google-->>Auth: access_token, refresh_token
Auth->>DB: gmail_tokens 업데이트 Auth->>DB: gmail_token 업데이트
Auth-->>Front: 인증 완료 Auth-->>Front: 인증 완료
Front->>Front: 아이템 상태 갱신 Front->>Front: 아이템 상태 갱신
``` ```
@ -285,7 +285,7 @@ CREATE TABLE gmail_audit_logs (
- [x] 필요 테이블 생성 - [x] 필요 테이블 생성
- robeing_stats 테이블 (레벨 관리) - robeing_stats 테이블 (레벨 관리)
- gmail_audit_logs 테이블 (감사 로그) - gmail_audit_logs 테이블 (감사 로그)
- gmail_tokens에 is_equipped, equipped_to 컬럼 추가 - gmail_token에 is_equipped, equipped_to 컬럼 추가
- [x] skill-email DB 연결 코드 작성 (희재) - [x] skill-email DB 연결 코드 작성 (희재)
- FileCredentialsProvider → DBCredentialsProvider 전환 - FileCredentialsProvider → DBCredentialsProvider 전환
- PostgreSQL 연결 설정 추가 (localhost:5433 SSH 터널) - PostgreSQL 연결 설정 추가 (localhost:5433 SSH 터널)
@ -361,11 +361,11 @@ DATABASE_URL=postgresql://robeings:robeings@192.168.219.45:5432/main_db
## 8. 데이터 매핑 (추후 구현) ## 8. 데이터 매핑 (추후 구현)
### Slack User ID ↔ Gmail 계정 매핑 ### Slack User ID ↔ Gmail 계정 매핑
- 위치: 추후 결정 (workspace_members 또는 별도 테이블) - 위치: 추후 결정 (workspace_member 또는 별도 테이블)
- 구조: - 구조:
```sql ```sql
-- 옵션 1: workspace_members 확장 -- 옵션 1: workspace_member 확장
ALTER TABLE workspace_members ALTER TABLE workspace_member
ADD COLUMN gmail_account VARCHAR(255); ADD COLUMN gmail_account VARCHAR(255);
-- 옵션 2: 별도 매핑 테이블 -- 옵션 2: 별도 매핑 테이블
@ -443,7 +443,7 @@ CREATE TABLE user_gmail_mapping (
## 12. 참고사항 ## 12. 참고사항
### 현재 상태 (2025-08-19) ### 현재 상태 (2025-08-19)
- gmail_tokens 테이블 생성 완료 - gmail_token 테이블 생성 완료
- rb8001 정상 작동 (GEMINI_USE_CLI=false) - rb8001 정상 작동 (GEMINI_USE_CLI=false)
- skill-email 파일 기반 동작 중 (DB 전환 필요) - skill-email 파일 기반 동작 중 (DB 전환 필요)
- 프론트 인벤토리 UI 미구현 - 프론트 인벤토리 UI 미구현

View File

@ -45,11 +45,11 @@ Gmail 스킬 통합을 담당하는 핵심 모듈
# PostgreSQL에서 직접 장착 상태 확인 # PostgreSQL에서 직접 장착 상태 확인
# 5분 캐싱으로 성능 최적화 # 5분 캐싱으로 성능 최적화
cur.execute(""" cur.execute("""
SELECT COUNT(*) FROM gmail_tokens SELECT COUNT(*) FROM gmail_token
WHERE user_id = %s AND is_equipped = true WHERE user_id = %s AND is_equipped = true
""", (user_id,)) """, (user_id,))
``` ```
- gmail_tokens 테이블의 is_equipped 확인 - gmail_token 테이블의 is_equipped 확인
- 캐시 TTL: 300초 (5분) - 캐시 TTL: 300초 (5분)
- DB 연결 실패 시 False 반환 - DB 연결 실패 시 False 반환
@ -372,7 +372,7 @@ python3 -c "
import psycopg2 import psycopg2
conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/main_db') conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/main_db')
cur = conn.cursor() cur = conn.cursor()
cur.execute('SELECT user_id, is_equipped FROM gmail_tokens') cur.execute('SELECT user_id, is_equipped FROM gmail_token')
for row in cur.fetchall(): for row in cur.fetchall():
print(f'{row[0]}: equipped={row[1]}') print(f'{row[0]}: equipped={row[1]}')
" "

View File

@ -13,7 +13,7 @@ skill-email 서비스가 Gmail OAuth 토큰을 파일이 아닌 PostgreSQL 데
### 배경 ### 배경
- 기존: 파일 기반 토큰 관리 (FileCredentialsProvider) - 기존: 파일 기반 토큰 관리 (FileCredentialsProvider)
- 변경: PostgreSQL gmail_tokens 테이블 기반 관리 (DBCredentialsProvider) - 변경: PostgreSQL gmail_token 테이블 기반 관리 (DBCredentialsProvider)
- 이유: 중앙집중식 토큰 관리, 보안 강화, 다중 서비스 통합 - 이유: 중앙집중식 토큰 관리, 보안 강화, 다중 서비스 통합
--- ---
@ -56,12 +56,12 @@ LOG_LEVEL=INFO
2. **get_credentials() 메서드** 2. **get_credentials() 메서드**
```python ```python
def get_credentials(self, user_id: str) -> Optional[Credentials]: def get_credentials(self, user_id: str) -> Optional[Credentials]:
# gmail_tokens 테이블에서 조회 # gmail_token 테이블에서 조회
# is_equipped=true인 토큰만 조회 # is_equipped=true인 토큰만 조회
# token_data + oauth_config 컬럼 모두 활용 # token_data + oauth_config 컬럼 모두 활용
``` ```
- gmail_tokens 테이블 조회 - gmail_token 테이블 조회
- token_data 컬럼: access_token, refresh_token - token_data 컬럼: access_token, refresh_token
- oauth_config 컬럼: client_id, client_secret, token_uri - oauth_config 컬럼: client_id, client_secret, token_uri
- Google Credentials 객체로 변환 - Google Credentials 객체로 변환
@ -117,9 +117,9 @@ def get_gmail_service() -> GmailService:
## 3. 데이터베이스 구조 ## 3. 데이터베이스 구조
### gmail_tokens 테이블 ### gmail_token 테이블
```sql ```sql
CREATE TABLE gmail_tokens ( CREATE TABLE gmail_token (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
user_id VARCHAR(100), user_id VARCHAR(100),
robeing_id VARCHAR(50), robeing_id VARCHAR(50),
@ -161,7 +161,7 @@ CREATE TABLE gmail_tokens (
cd /home/heejae/skill-email && python3 test_db.py cd /home/heejae/skill-email && python3 test_db.py
``` ```
- ✅ PostgreSQL 연결 성공 - ✅ PostgreSQL 연결 성공
- ✅ gmail_tokens 테이블 접근 가능 - ✅ gmail_token 테이블 접근 가능
- ✅ 3개 토큰 확인 (heejae, test, unknown) - ✅ 3개 토큰 확인 (heejae, test, unknown)
### 4.2 Provider 테스트 ### 4.2 Provider 테스트
@ -212,7 +212,7 @@ sshpass -p "19800508" ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51
- **문제**: 모든 토큰이 is_equipped=false 상태 - **문제**: 모든 토큰이 is_equipped=false 상태
- **해결**: - **해결**:
```sql ```sql
UPDATE gmail_tokens SET is_equipped=true WHERE user_id='heejae' UPDATE gmail_token SET is_equipped=true WHERE user_id='heejae'
``` ```
### 6.3 oauth_config 누락 ### 6.3 oauth_config 누락
@ -221,7 +221,7 @@ sshpass -p "19800508" ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51
```python ```python
cur.execute(""" cur.execute("""
SELECT token_data, oauth_config SELECT token_data, oauth_config
FROM gmail_tokens FROM gmail_token
WHERE user_id = %s AND is_equipped = true WHERE user_id = %s AND is_equipped = true
""") """)
``` ```
@ -299,7 +299,7 @@ sshpass -p "19800508" ssh -f -N -L 5433:localhost:5432 admin@124.55.18.179 -p 51
## 12. 검증 체크리스트 ## 12. 검증 체크리스트
- [x] PostgreSQL 연결 성공 - [x] PostgreSQL 연결 성공
- [x] gmail_tokens 테이블 조회 - [x] gmail_token 테이블 조회
- [x] 토큰 데이터 파싱 - [x] 토큰 데이터 파싱
- [x] Credentials 객체 생성 - [x] Credentials 객체 생성
- [x] oauth_config 통합 - [x] oauth_config 통합
@ -328,7 +328,7 @@ python3 -c "
import psycopg2 import psycopg2
conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/main_db') conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/main_db')
cur = conn.cursor() cur = conn.cursor()
cur.execute('SELECT user_id, is_equipped FROM gmail_tokens') cur.execute('SELECT user_id, is_equipped FROM gmail_token')
for row in cur.fetchall(): for row in cur.fetchall():
print(f'{row[0]}: equipped={row[1]}') print(f'{row[0]}: equipped={row[1]}')
" "

View File

@ -114,7 +114,7 @@ sequenceDiagram
loop 각 사용자별 처리 loop 각 사용자별 처리
Scheduler->>Monitor: Gmail 토큰 확인<br/>(X-User-Id) Scheduler->>Monitor: Gmail 토큰 확인<br/>(X-User-Id)
Monitor->>DB: gmail_tokens 조회 Monitor->>DB: gmail_token 조회
DB-->>Monitor: token_data DB-->>Monitor: token_data
alt 토큰 유효 alt 토큰 유효

View File

@ -55,7 +55,7 @@ Frontend(Slack 아이템 획득) → auth-server → OAuth 2.0 → 봇 토큰
- `GET /auth/slack/passport/install?user_id={user_id}` - 봇 설치 OAuth 시작 - `GET /auth/slack/passport/install?user_id={user_id}` - 봇 설치 OAuth 시작
- 파일: `/home/admin/auth-server/app/providers/slack.py:104` - 파일: `/home/admin/auth-server/app/providers/slack.py:104`
- 변경: workspace_id → user_id 파라미터 - 변경: workspace_id → user_id 파라미터
- 처리: user_id로 workspace_members 테이블에서 workspace_id 조회 - 처리: user_id로 workspace_member 테이블에서 workspace_id 조회
- `GET /auth/slack/passport/callback` - bot_token 저장 - `GET /auth/slack/passport/callback` - bot_token 저장
- `GET /auth/slack/passport/status/{workspace_id}` - 아이템 상태 확인 - `GET /auth/slack/passport/status/{workspace_id}` - 아이템 상태 확인
- `DELETE /auth/slack/passport/uninstall/{workspace_id}` - 아이템 취소 - `DELETE /auth/slack/passport/uninstall/{workspace_id}` - 아이템 취소
@ -91,7 +91,7 @@ Slack Event → rb8001 → DB → SSE/WebSocket → Frontend
- 이벤트: `message.channels`, `message.groups`, `message.im`, `message.mpim`, `app_mention` - 이벤트: `message.channels`, `message.groups`, `message.im`, `message.mpim`, `app_mention`
- 쓰레드: `message.message_replied` 이벤트 존재하나 `thread_ts` 확인 권장 - 쓰레드: `message.message_replied` 이벤트 존재하나 `thread_ts` 확인 권장
2. 채널 타입: C(공개), G(비공개/그룹), D(DM) 2. 채널 타입: C(공개), G(비공개/그룹), D(DM)
3. conversation_logs 저장: team_id, channel_id, thread_ts 3. conversation_log 저장: team_id, channel_id, thread_ts
4. 초기 동기화: `conversations.history`로 백필 4. 초기 동기화: `conversations.history`로 백필
### Phase 4: 명령 처리 ### Phase 4: 명령 처리
@ -116,14 +116,14 @@ Slack → @robeing 또는 /robeing → rb8001 → 응답
- **users**: id(UUID), email, slack_user_id, slack_team_id - **users**: id(UUID), email, slack_user_id, slack_team_id
- **slack_workspaces**: team_id, team_name, bot_token(암호화), installed_by_user_id, scopes, installed_at - **slack_workspaces**: team_id, team_name, bot_token(암호화), installed_by_user_id, scopes, installed_at
- **slack_user_mapping**: team_id + slack_user_id 복합키, user_id(UUID) - **slack_user_mapping**: team_id + slack_user_id 복합키, user_id(UUID)
- **conversation_logs**: team_id, channel_id, channel_type, user_id, text, ts, thread_ts - **conversation_log**: team_id, channel_id, channel_type, user_id, text, ts, thread_ts
### 필요 컬럼 추가 ### 필요 컬럼 추가
```sql ```sql
ALTER TABLE users ADD COLUMN slack_user_id VARCHAR(50); ALTER TABLE users ADD COLUMN slack_user_id VARCHAR(50);
ALTER TABLE users ADD COLUMN slack_team_id VARCHAR(50); ALTER TABLE users ADD COLUMN slack_team_id VARCHAR(50);
ALTER TABLE conversation_logs ADD COLUMN thread_ts VARCHAR(20); ALTER TABLE conversation_log ADD COLUMN thread_ts VARCHAR(20);
ALTER TABLE conversation_logs ADD COLUMN channel_type VARCHAR(20); ALTER TABLE conversation_log ADD COLUMN channel_type VARCHAR(20);
``` ```
--- ---
@ -182,4 +182,4 @@ ALTER TABLE conversation_logs ADD COLUMN channel_type VARCHAR(20);
### 내부 문서 ### 내부 문서
- [auth_login_sequences.md](../300_architecture/sequences/auth_login_sequences.md) - [auth_login_sequences.md](../300_architecture/sequences/auth_login_sequences.md)
- [tables.md](../300_architecture/database/tables.md) - [tables.md](../300_architecture/database/tables.md)
- [250828_conversation_logs_channel_구분_개선.md](../troubleshooting/250828_conversation_logs_channel_구분_개선.md) - [250828_conversation_log_channel_구분_개선.md](../troubleshooting/250828_conversation_log_channel_구분_개선.md)

View File

@ -190,7 +190,7 @@ Claude가 세션 시작 시 규칙을 제대로 따르지 않음. CLAUDE.md 재
- JWT/OAuth 키 관리 상태 점검 - JWT/OAuth 키 관리 상태 점검
2. **PostgreSQL main_db 스키마 분석**: 2. **PostgreSQL main_db 스키마 분석**:
- `companies` 테이블: 회사별 서브도메인 관리 (2개 회사 등록) - `company` 테이블: 회사별 서브도메인 관리 (2개 회사 등록)
- `slack_workspaces` 테이블: Slack 봇 토큰 관리 (2개 워크스페이스 활성) - `slack_workspaces` 테이블: Slack 봇 토큰 관리 (2개 워크스페이스 활성)
- 멀티테넌트 B2B SaaS 구조 확인 - 멀티테넌트 B2B SaaS 구조 확인

View File

@ -69,8 +69,8 @@
**필요한 DB 확장**: **필요한 DB 확장**:
1. `users` 테이블: 개별 사용자 정보 1. `users` 테이블: 개별 사용자 정보
2. `workspace_members` 테이블: 사용자-워크스페이스-로빙 매핑 2. `workspace_member` 테이블: 사용자-워크스페이스-로빙 매핑
3. `workspaces` 테이블: companies 테이블 이름 변경 3. `workspaces` 테이블: company 테이블 이름 변경
**개발 우선순위**: **개발 우선순위**:
1. auth-server 데이터 모델 확장 1. auth-server 데이터 모델 확장
@ -103,8 +103,8 @@ CREATE TABLE users (
... ...
); );
-- workspace_members 테이블 -- workspace_member 테이블
CREATE TABLE workspace_members ( CREATE TABLE workspace_member (
workspace_id UUID REFERENCES workspaces(id), workspace_id UUID REFERENCES workspaces(id),
user_id UUID REFERENCES users(id), user_id UUID REFERENCES users(id),
role VARCHAR(50), role VARCHAR(50),
@ -118,7 +118,7 @@ CREATE TABLE workspace_members (
### Company → Workspace 변환 작업 ### Company → Workspace 변환 작업
**Sequential Design 분석으로 영향받는 파일 파악**: **Sequential Design 분석으로 영향받는 파일 파악**:
1. `app/api/companies.py` → `app/api/workspaces.py` 1. `app/api/company.py` → `app/api/workspaces.py`
2. `app/providers/slack.py` - OAuth 처리 2. `app/providers/slack.py` - OAuth 처리
3. `app/api/slack_router.py` - Slack 이벤트 라우팅 3. `app/api/slack_router.py` - Slack 이벤트 라우팅
4. `app/db/init_db.py` - DB 초기화 4. `app/db/init_db.py` - DB 초기화
@ -132,9 +132,9 @@ CREATE TABLE workspace_members (
**API 엔드포인트 변경**: **API 엔드포인트 변경**:
``` ```
/api/companies → /api/workspaces /api/company → /api/workspaces
/api/companies/{company_id} → /api/workspaces/{workspace_id} /api/company/{company_id} → /api/workspaces/{workspace_id}
/api/companies/subdomain/{subdomain} → /api/workspaces/subdomain/{subdomain} /api/company/subdomain/{subdomain} → /api/workspaces/subdomain/{subdomain}
``` ```
## 교훈 ## 교훈

View File

@ -170,7 +170,7 @@ const ROBEING_API_URL = import.meta.env.VITE_ROBEING_API_URL || 'https://ro-bein
3. **auth-server DB 구축 필요**: 3. **auth-server DB 구축 필요**:
- users 테이블 - users 테이블
- workspace_members 테이블 (user_id, robeing_id 매핑) - workspace_member 테이블 (user_id, robeing_id 매핑)
- 로빙 할당 API 엔드포인트 - 로빙 할당 API 엔드포인트
4. **ChatInterface 수정**: 4. **ChatInterface 수정**:

View File

@ -41,7 +41,7 @@ git pull
``` ```
**주요 변경 내용**: **주요 변경 내용**:
- `companies.py` → `workspaces.py`로 이름 변경 - `company.py` → `workspaces.py`로 이름 변경
- 새로운 마이그레이션 파일 추가 (`add_user_workspace_tables.sql`) - 새로운 마이그레이션 파일 추가 (`add_user_workspace_tables.sql`)
- 사용자 모델 추가 (`user.py`) - 사용자 모델 추가 (`user.py`)
- Slack 통합 개선 - Slack 통합 개선

View File

@ -32,14 +32,14 @@ robeing-gateway/
**핵심 기능**: **핵심 기능**:
1. 사용자-로빙 매핑 관리 1. 사용자-로빙 매핑 관리
2. 메모리 캐시로 성능 최적화 2. 메모리 캐시로 성능 최적화
3. 기존 main_db 테이블 활용 (workspaces, workspace_members) 3. 기존 main_db 테이블 활용 (workspaces, workspace_member)
4. 헬스체크 및 모니터링 엔드포인트 4. 헬스체크 및 모니터링 엔드포인트
### 23서버팀 피드백 ### 23서버팀 피드백
**중요 변경사항 - 새 테이블 불필요**: **중요 변경사항 - 새 테이블 불필요**:
- 기존 main_db의 workspaces, workspace_members 테이블 활용 - 기존 main_db의 workspaces, workspace_member 테이블 활용
- workspace_members에서 user_id로 robeing 정보 조회 - workspace_member에서 user_id로 robeing 정보 조회
- JOIN 쿼리로 사용자 → 워크스페이스 → 로빙 매핑 - JOIN 쿼리로 사용자 → 워크스페이스 → 로빙 매핑
## 오전 11시 00분 - 배포 문제 해결 ## 오전 11시 00분 - 배포 문제 해결
@ -144,7 +144,7 @@ fetch('http://localhost:8100/api/chat', {
### 2. 워크스페이스 할당 테스트 ### 2. 워크스페이스 할당 테스트
```sql ```sql
-- 사용자를 워크스페이스에 할당 -- 사용자를 워크스페이스에 할당
INSERT INTO workspace_members ( INSERT INTO workspace_member (
id, user_id, workspace_id, role, robeing_id, is_active id, user_id, workspace_id, role, robeing_id, is_active
) VALUES ( ) VALUES (
gen_random_uuid(), gen_random_uuid(),
@ -244,8 +244,8 @@ INSERT INTO users (id, ...) VALUES ('user-happybell80', ...);
**두 번째 시도 - role 값 오류**: **두 번째 시도 - role 값 오류**:
```sql ```sql
-- role은 대문자여야 함 -- role은 대문자여야 함
INSERT INTO workspace_members (..., role, ...) VALUES (..., 'owner', ...); -- X INSERT INTO workspace_member (..., role, ...) VALUES (..., 'owner', ...); -- X
INSERT INTO workspace_members (..., role, ...) VALUES (..., 'OWNER', ...); -- O INSERT INTO workspace_member (..., role, ...) VALUES (..., 'OWNER', ...); -- O
``` ```
**최종 수정 버전**: **최종 수정 버전**:
@ -259,7 +259,7 @@ INSERT INTO users (id, email, name) VALUES
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, '0914eagle@gmail.com', '전희재'), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'::uuid, '0914eagle@gmail.com', '전희재'),
('cccccccc-cccc-cccc-cccc-cccccccccccc'::uuid, 'hhyong91@gmail.com', '황한용'); ('cccccccc-cccc-cccc-cccc-cccccccccccc'::uuid, 'hhyong91@gmail.com', '황한용');
INSERT INTO workspace_members (..., role, ...) VALUES INSERT INTO workspace_member (..., role, ...) VALUES
(..., 'OWNER', ...), -- 김종태 (..., 'OWNER', ...), -- 김종태
(..., 'MEMBER', ...); -- 전희재, 황한용 (..., 'MEMBER', ...); -- 전희재, 황한용
``` ```
@ -269,7 +269,7 @@ INSERT INTO workspace_members (..., role, ...) VALUES
**데이터베이스 상태**: **데이터베이스 상태**:
- workspaces: 1개 (ivada-robeing) - workspaces: 1개 (ivada-robeing)
- users: 3개 (김종태, 전희재, 황한용) - users: 3개 (김종태, 전희재, 황한용)
- workspace_members: 3개 (모두 rb10508_micro 할당) - workspace_member: 3개 (모두 rb10508_micro 할당)
**통합 테스트 성공 (오후 2:13)**: **통합 테스트 성공 (오후 2:13)**:
1. **인증 흐름**: 1. **인증 흐름**:
@ -278,7 +278,7 @@ INSERT INTO workspace_members (..., role, ...) VALUES
2. **Gateway 라우팅**: 2. **Gateway 라우팅**:
- X-User-Id 헤더로 사용자 식별 - X-User-Id 헤더로 사용자 식별
- DB 조회: workspace_members → robeing_id 확인 - DB 조회: workspace_member → robeing_id 확인
- rb10508_micro (192.168.219.52:10508)로 라우팅 - rb10508_micro (192.168.219.52:10508)로 라우팅
3. **개인화된 응답**: 3. **개인화된 응답**:
@ -289,7 +289,7 @@ INSERT INTO workspace_members (..., role, ...) VALUES
``` ```
Frontend → nginx(/gateway/) → Gateway(8100) → DB 조회 Frontend → nginx(/gateway/) → Gateway(8100) → DB 조회
workspace_members workspace_member
rb10508_micro → 맞춤 응답 rb10508_micro → 맞춤 응답
``` ```

View File

@ -99,7 +99,7 @@ npm run build
async def get_robeing_info(username: str): async def get_robeing_info(username: str):
query = text(""" query = text("""
SELECT ... SELECT ...
FROM workspace_members wm FROM workspace_member wm
JOIN users u ON wm.user_id = u.id JOIN users u ON wm.user_id = u.id
WHERE u.username = :username WHERE u.username = :username
""") """)

View File

@ -34,7 +34,7 @@ async def get_robeing_info(username: str):
"""username으로 직접 조회""" """username으로 직접 조회"""
query = text(""" query = text("""
SELECT ... SELECT ...
FROM workspace_members wm FROM workspace_member wm
JOIN users u ON wm.user_id = u.id JOIN users u ON wm.user_id = u.id
WHERE u.username = :username WHERE u.username = :username
""") """)

View File

@ -29,7 +29,7 @@ CREATE TABLE slack_user_mapping (
slack_user_id VARCHAR(100) NOT NULL, slack_user_id VARCHAR(100) NOT NULL,
slack_workspace_id UUID REFERENCES slack_workspaces(id), slack_workspace_id UUID REFERENCES slack_workspaces(id),
user_id UUID REFERENCES users(id) NOT NULL, user_id UUID REFERENCES users(id) NOT NULL,
workspace_member_id UUID REFERENCES workspace_members(id), workspace_member_id UUID REFERENCES workspace_member(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(slack_user_id, slack_workspace_id) UNIQUE(slack_user_id, slack_workspace_id)
@ -57,7 +57,7 @@ CREATE TABLE slack_user_mapping (
**해결**: **해결**:
```sql ```sql
GRANT ALL PRIVILEGES ON TABLE slack_user_mapping TO robeings; GRANT ALL PRIVILEGES ON TABLE slack_user_mapping TO robeings;
GRANT SELECT ON users, workspace_members TO robeings; GRANT SELECT ON users, workspace_member TO robeings;
ALTER TABLE slack_user_mapping OWNER TO robeings; ALTER TABLE slack_user_mapping OWNER TO robeings;
``` ```

View File

@ -63,7 +63,7 @@ User → rb8001 → skill-email → Gmail API
### 2.2 제안: PostgreSQL 기반 관리 ### 2.2 제안: PostgreSQL 기반 관리
```sql ```sql
-- Gmail 토큰 테이블 -- Gmail 토큰 테이블
CREATE TABLE gmail_tokens ( CREATE TABLE gmail_token (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
user_id VARCHAR(100) UNIQUE NOT NULL, user_id VARCHAR(100) UNIQUE NOT NULL,
robeing_id VARCHAR(50), robeing_id VARCHAR(50),
@ -268,7 +268,7 @@ class DBCredentialsProvider:
async def get_credentials(self, user_id: str): async def get_credentials(self, user_id: str):
# PostgreSQL에서 토큰 조회 # PostgreSQL에서 토큰 조회
token = await db.query( token = await db.query(
"SELECT * FROM gmail_tokens WHERE user_id = %s", "SELECT * FROM gmail_token WHERE user_id = %s",
user_id user_id
) )
return Credentials(**token) return Credentials(**token)

View File

@ -118,14 +118,14 @@ pgrep -f "ssh.*5433:localhost:5432"
### PostgreSQL 접속 ### PostgreSQL 접속
```bash ```bash
# SSH를 통한 원격 실행 # 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'" 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_token'"
# 로컬 터널을 통한 접속 (psql 클라이언트 필요) # 로컬 터널을 통한 접속 (psql 클라이언트 필요)
PGPASSWORD=robeings psql -h localhost -p 5433 -U robeings -d main_db PGPASSWORD=robeings psql -h localhost -p 5433 -U robeings -d main_db
``` ```
### 주요 테이블 ### 주요 테이블
- `gmail_tokens`: Gmail OAuth 토큰 저장 - `gmail_token`: Gmail OAuth 토큰 저장
- `gmail_audit_logs`: Gmail 작업 감사 로그 - `gmail_audit_logs`: Gmail 작업 감사 로그
- `robeing_stats`: Robeing 레벨 정보 - `robeing_stats`: Robeing 레벨 정보
- `slack_user_mapping`: Slack-UUID 매핑 (현재 미사용) - `slack_user_mapping`: Slack-UUID 매핑 (현재 미사용)

View File

@ -16,10 +16,10 @@ User: robeings
Password: robeings Password: robeings
``` ```
### 1.2 생성된 테이블: `gmail_tokens` ### 1.2 생성된 테이블: `gmail_token`
```sql ```sql
CREATE TABLE gmail_tokens ( CREATE TABLE gmail_token (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
user_id VARCHAR(100) UNIQUE NOT NULL, user_id VARCHAR(100) UNIQUE NOT NULL,
robeing_id VARCHAR(50), robeing_id VARCHAR(50),
@ -83,12 +83,12 @@ CREATE TABLE gmail_tokens (
```sql ```sql
-- 기본 인덱스 -- 기본 인덱스
CREATE INDEX idx_gmail_tokens_user_id ON gmail_tokens(user_id); CREATE INDEX idx_gmail_token_user_id ON gmail_token(user_id);
CREATE INDEX idx_gmail_tokens_robeing_id ON gmail_tokens(robeing_id); CREATE INDEX idx_gmail_token_robeing_id ON gmail_token(robeing_id);
-- JSON 검색용 GIN 인덱스 -- JSON 검색용 GIN 인덱스
CREATE INDEX idx_gmail_tokens_token_data ON gmail_tokens USING GIN (token_data); CREATE INDEX idx_gmail_token_token_data ON gmail_token USING GIN (token_data);
CREATE INDEX idx_gmail_tokens_oauth_config ON gmail_tokens USING GIN (oauth_config); CREATE INDEX idx_gmail_token_oauth_config ON gmail_token USING GIN (oauth_config);
``` ```
--- ---
@ -106,8 +106,8 @@ END;
$$ LANGUAGE plpgsql; $$ LANGUAGE plpgsql;
-- 트리거 생성 -- 트리거 생성
CREATE TRIGGER update_gmail_tokens_updated_at CREATE TRIGGER update_gmail_token_updated_at
BEFORE UPDATE ON gmail_tokens BEFORE UPDATE ON gmail_token
FOR EACH ROW FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column(); EXECUTE FUNCTION update_updated_at_column();
``` ```
@ -144,19 +144,19 @@ EXECUTE FUNCTION update_updated_at_column();
```sql ```sql
-- 특정 사용자 토큰 조회 -- 특정 사용자 토큰 조회
SELECT token_data->>'access_token' as access_token SELECT token_data->>'access_token' as access_token
FROM gmail_tokens FROM gmail_token
WHERE user_id = 'test'; WHERE user_id = 'test';
-- 이메일 발송 가능한 사용자 찾기 -- 이메일 발송 가능한 사용자 찾기
SELECT user_id, robeing_id SELECT user_id, robeing_id
FROM gmail_tokens FROM gmail_token
WHERE scopes @> '["https://www.googleapis.com/auth/gmail.send"]'; WHERE scopes @> '["https://www.googleapis.com/auth/gmail.send"]';
``` ```
### 6.2 토큰 업데이트 ### 6.2 토큰 업데이트
```sql ```sql
-- 액세스 토큰 갱신 -- 액세스 토큰 갱신
UPDATE gmail_tokens UPDATE gmail_token
SET token_data = jsonb_set( SET token_data = jsonb_set(
token_data, token_data,
'{access_token}', '{access_token}',
@ -165,7 +165,7 @@ SET token_data = jsonb_set(
WHERE user_id = 'test'; WHERE user_id = 'test';
-- 스코프 추가 -- 스코프 추가
UPDATE gmail_tokens UPDATE gmail_token
SET scopes = scopes || '["https://www.googleapis.com/auth/gmail.send"]'::jsonb SET scopes = scopes || '["https://www.googleapis.com/auth/gmail.send"]'::jsonb
WHERE user_id = 'heejae'; WHERE user_id = 'heejae';
``` ```
@ -173,12 +173,12 @@ WHERE user_id = 'heejae';
### 6.3 메타데이터 활용 ### 6.3 메타데이터 활용
```sql ```sql
-- Slack 사용자와 매핑 -- Slack 사용자와 매핑
SELECT * FROM gmail_tokens SELECT * FROM gmail_token
WHERE metadata->>'slack_user_id' = 'U091UNVE41M'; WHERE metadata->>'slack_user_id' = 'U091UNVE41M';
-- 특정 로빙의 Gmail 계정 찾기 -- 특정 로빙의 Gmail 계정 찾기
SELECT user_id, metadata->>'email' as gmail_account SELECT user_id, metadata->>'email' as gmail_account
FROM gmail_tokens FROM gmail_token
WHERE robeing_id = 'rb8001'; WHERE robeing_id = 'rb8001';
``` ```
@ -207,7 +207,7 @@ with conn.cursor(cursor_factory=RealDictCursor) as cur:
token_data->>'refresh_token' as refresh_token, token_data->>'refresh_token' as refresh_token,
oauth_config, oauth_config,
scopes scopes
FROM gmail_tokens FROM gmail_token
WHERE user_id = %s WHERE user_id = %s
""", ('test',)) """, ('test',))

View File

@ -16,7 +16,7 @@
- `AttributeError: 'str' object has no attribute 'get'` 에러 발생 - `AttributeError: 'str' object has no attribute 'get'` 에러 발생
#### 원인 분석 #### 원인 분석
- gmail_tokens 테이블의 metadata 컬럼이 JSONB 타입이지만, asyncpg가 string으로 반환하는 경우 발생 - gmail_token 테이블의 metadata 컬럼이 JSONB 타입이지만, asyncpg가 string으로 반환하는 경우 발생
- 또는 데이터 저장 시 JSON string으로 저장된 경우 - 또는 데이터 저장 시 JSON string으로 저장된 경우
#### 해결 과정 #### 해결 과정

View File

@ -139,7 +139,7 @@ GMAIL_API_SCOPES = [
# PostgreSQL 직접 연동 # PostgreSQL 직접 연동
await conn.execute(""" await conn.execute("""
INSERT INTO gmail_tokens (...) VALUES (...) INSERT INTO gmail_token (...) VALUES (...)
ON CONFLICT (user_id) DO UPDATE SET ... ON CONFLICT (user_id) DO UPDATE SET ...
""") """)
``` ```
@ -299,7 +299,7 @@ useEffect(() => {
2. "Gmail 연결" 버튼 클릭 2. "Gmail 연결" 버튼 클릭
3. auth-server/auth/gmail/passport/ OAuth 시작 3. auth-server/auth/gmail/passport/ OAuth 시작
4. Google 인증 완료 4. Google 인증 완료
5. PostgreSQL gmail_tokens 저장 (is_equipped = true) 5. PostgreSQL gmail_token 저장 (is_equipped = true)
6. Frontend로 리다이렉트 (?gmail=success) 6. Frontend로 리다이렉트 (?gmail=success)
7. 자동 새로고침 및 성공 메시지 7. 자동 새로고침 및 성공 메시지
``` ```

View File

@ -101,7 +101,7 @@ CREATE INDEX idx_conversations_robeing ON robeing.conversations(robeing_id);
1. 51123 서버에서 서비스 재배포 확인 1. 51123 서버에서 서비스 재배포 확인
2. main_db에 robeing 스키마 및 테이블 생성 2. main_db에 robeing 스키마 및 테이블 생성
3. rb10508_micro에 이름→이메일 변환 로직 구현 3. rb10508_micro에 이름→이메일 변환 로직 구현
4. workspace_members + robeing.contacts 조합 조회 4. workspace_member + robeing.contacts 조합 조회
## 교훈 ## 교훈
- 데이터베이스 이름은 용도를 명확히 반영해야 함 - 데이터베이스 이름은 용도를 명확히 반영해야 함

View File

@ -169,7 +169,7 @@ on:
``` ```
사용자 → rb10508_micro → skill-email(DB 연동) → Gmail API 사용자 → rb10508_micro → skill-email(DB 연동) → Gmail API
↓ ↓ ↓ ↓
PostgreSQL (gmail_tokens 테이블) PostgreSQL (gmail_token 테이블)
robeing-monitor (아이템 관리) robeing-monitor (아이템 관리)
``` ```

View File

@ -23,7 +23,7 @@
users 테이블 (UUID) users 테이블 (UUID)
workspace_members 테이블 workspace_member 테이블
(robeing_id 할당) (robeing_id 할당)
로빙 컨테이너 로빙 컨테이너
@ -35,7 +35,7 @@ workspace_members 테이블
#### 핵심 테이블 관계 #### 핵심 테이블 관계
- **users**: 사용자 UUID 저장 - **users**: 사용자 UUID 저장
- **workspace_members**: user_id → robeing_id 매핑 - **workspace_member**: user_id → robeing_id 매핑
- **robeing_stats**: 각 로빙의 레벨, 경험치, 스탯 - **robeing_stats**: 각 로빙의 레벨, 경험치, 스탯
#### 서비스 구조 #### 서비스 구조
@ -74,7 +74,7 @@ export async function getUserRobeing(): Promise<{
robeing_id: string; robeing_id: string;
robeing_url: string; robeing_url: string;
} | null> { } | null> {
// workspace_members에서 사용자의 로빙 조회 // workspace_member에서 사용자의 로빙 조회
} }
export async function getRobeingStats(robeingId: string): Promise<{ export async function getRobeingStats(robeingId: string): Promise<{
@ -177,7 +177,7 @@ async def get_stats(robeing_id: str):
### 1. 시스템 구조 이해의 중요성 ### 1. 시스템 구조 이해의 중요성
- 단순한 레벨 표시 문제가 아닌 전체 아키텍처 이해 필요 - 단순한 레벨 표시 문제가 아닌 전체 아키텍처 이해 필요
- users → workspace_members → robeing_stats 관계 파악 필수 - users → workspace_member → robeing_stats 관계 파악 필수
- 다중 로빙 체계와 사용자 매핑 구조 이해 - 다중 로빙 체계와 사용자 매핑 구조 이해
### 2. 하드코딩 제거의 필요성 ### 2. 하드코딩 제거의 필요성

View File

@ -21,12 +21,12 @@ Gmail OAuth 토큰의 자동 갱신 기능을 auth-server에 구현하여, 만
### 2.1 현재 DB 정보 ### 2.1 현재 DB 정보
``` ```
Database: main_db (auth_db 아님 주의!) Database: main_db (auth_db 아님 주의!)
Table: gmail_tokens Table: gmail_token
``` ```
### 2.2 gmail_tokens 테이블 구조 ### 2.2 gmail_token 테이블 구조
```sql ```sql
CREATE TABLE gmail_tokens ( CREATE TABLE gmail_token (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
user_id UUID UNIQUE NOT NULL, user_id UUID UNIQUE NOT NULL,
robeing_id VARCHAR(50), robeing_id VARCHAR(50),
@ -222,6 +222,6 @@ docker compose down && docker compose up -d --build
--- ---
## 10. 관련 문서 ## 10. 관련 문서
- `/home/heejae/DOCS/250818_gmail_tokens_database_setup.md` - `/home/heejae/DOCS/250818_gmail_token_database_setup.md`
- `/home/heejae/DOCS/robeing-monitor-integration.md` - `/home/heejae/DOCS/robeing-monitor-integration.md`
- `/home/heejae/DOCS/250817_slack_user_mapping_troubleshooting.md` - `/home/heejae/DOCS/250817_slack_user_mapping_troubleshooting.md`

View File

@ -36,7 +36,7 @@ asyncpg.exceptions.DataError: invalid input for query argument $1: 'U091UNVE41M'
#### 원인 #### 원인
- robeing-monitor가 Slack user_id를 UUID로 변환하지 않고 직접 DB 조회 - robeing-monitor가 Slack user_id를 UUID로 변환하지 않고 직접 DB 조회
- PostgreSQL gmail_tokens 테이블의 user_id 컬럼은 UUID 타입 - PostgreSQL gmail_token 테이블의 user_id 컬럼은 UUID 타입
### 2.2 Gateway 프록시 문제 ### 2.2 Gateway 프록시 문제

View File

@ -24,7 +24,7 @@
### 2.1 테이블 구조 파악 ### 2.1 테이블 구조 파악
```sql ```sql
gmail_tokens 테이블: gmail_token 테이블:
- 구조 혼재: 신규 컬럼(access_token, refresh_token)과 기존 컬럼(token_data, oauth_config) - 구조 혼재: 신규 컬럼(access_token, refresh_token)과 기존 컬럼(token_data, oauth_config)
- 두 가지 형식 모두 지원 필요 - 두 가지 형식 모두 지원 필요
``` ```
@ -77,7 +77,7 @@ async def refresh_gmail_token(user_id: str):
cur.execute(''' cur.execute('''
SELECT access_token, refresh_token, expires_at, SELECT access_token, refresh_token, expires_at,
token_data, oauth_config, metadata, expiry token_data, oauth_config, metadata, expiry
FROM gmail_tokens FROM gmail_token
WHERE user_id = %s::uuid WHERE user_id = %s::uuid
''', (user_id,)) ''', (user_id,))
@ -90,7 +90,7 @@ async def refresh_gmail_token(user_id: str):
# 4. 새 토큰으로 DB 업데이트 # 4. 새 토큰으로 DB 업데이트
cur.execute(''' cur.execute('''
UPDATE gmail_tokens UPDATE gmail_token
SET access_token = %s, expires_at = %s, ... SET access_token = %s, expires_at = %s, ...
''') ''')
``` ```
@ -108,7 +108,7 @@ oauth_config = {
# INSERT 쿼리에 oauth_config 추가 # INSERT 쿼리에 oauth_config 추가
await conn.execute(""" await conn.execute("""
INSERT INTO gmail_tokens ( INSERT INTO gmail_token (
..., oauth_config, ... ..., oauth_config, ...
) VALUES (..., $8, ...) ) VALUES (..., $8, ...)
""", ..., json.dumps(oauth_config), ...) """, ..., json.dumps(oauth_config), ...)
@ -122,7 +122,7 @@ await conn.execute("""
**문제**: `token_data` NOT NULL 제약으로 INSERT 실패 **문제**: `token_data` NOT NULL 제약으로 INSERT 실패
**해결**: 51123 서버에서 직접 컬럼 제약 제거 **해결**: 51123 서버에서 직접 컬럼 제약 제거
```sql ```sql
ALTER TABLE gmail_tokens ALTER COLUMN token_data DROP NOT NULL; ALTER TABLE gmail_token ALTER COLUMN token_data DROP NOT NULL;
``` ```
### 4.2 UUID 타입 오류 ### 4.2 UUID 타입 오류
@ -144,14 +144,14 @@ database_url = os.getenv("DATABASE_URL", "postgresql://robeings:robeings@192.168
**문제**: `access_token` 컬럼 누락으로 "column does not exist" 에러 **문제**: `access_token` 컬럼 누락으로 "column does not exist" 에러
**해결**: 필요한 컬럼들 추가 **해결**: 필요한 컬럼들 추가
```sql ```sql
ALTER TABLE gmail_tokens ALTER TABLE gmail_token
ADD COLUMN IF NOT EXISTS access_token TEXT, ADD COLUMN IF NOT EXISTS access_token TEXT,
ADD COLUMN IF NOT EXISTS refresh_token TEXT, ADD COLUMN IF NOT EXISTS refresh_token TEXT,
ADD COLUMN IF NOT EXISTS token_type VARCHAR DEFAULT 'Bearer', ADD COLUMN IF NOT EXISTS token_type VARCHAR DEFAULT 'Bearer',
ADD COLUMN IF NOT EXISTS expires_at FLOAT; ADD COLUMN IF NOT EXISTS expires_at FLOAT;
-- 기존 token_data에서 새 컬럼으로 데이터 마이그레이션 -- 기존 token_data에서 새 컬럼으로 데이터 마이그레이션
UPDATE gmail_tokens UPDATE gmail_token
SET SET
access_token = token_data->>'access_token', access_token = token_data->>'access_token',
refresh_token = token_data->>'refresh_token' refresh_token = token_data->>'refresh_token'

View File

@ -24,7 +24,7 @@
-- 현재 DB 상태: 모든 사용자 토큰 만료 -- 현재 DB 상태: 모든 사용자 토큰 만료
SELECT username, access_token IS NOT NULL as has_token, SELECT username, access_token IS NOT NULL as has_token,
expires_at < extract(epoch from now()) as is_expired expires_at < extract(epoch from now()) as is_expired
FROM gmail_tokens; FROM gmail_token;
결과: 결과:
happybell80 | t (있음) | t (만료됨) happybell80 | t (있음) | t (만료됨)
@ -49,7 +49,7 @@ sequenceDiagram
Cron->>RB: POST /api/cron/daily-summary Cron->>RB: POST /api/cron/daily-summary
RB->>Email: GET /messages?user_id={slack_id} RB->>Email: GET /messages?user_id={slack_id}
Email->>DB: SELECT token_data FROM gmail_tokens Email->>DB: SELECT token_data FROM gmail_token
DB-->>Email: access_token: expired ❌ DB-->>Email: access_token: expired ❌
Email-->>RB: 500 Internal Server Error Email-->>RB: 500 Internal Server Error
RB->>Slack: 불완전한 브리핑 전송<br/>(이메일 없이 뉴스만) RB->>Slack: 불완전한 브리핑 전송<br/>(이메일 없이 뉴스만)
@ -73,7 +73,7 @@ sequenceDiagram
Google->>Auth: Callback with code Google->>Auth: Callback with code
Auth->>Google: Exchange code for tokens Auth->>Google: Exchange code for tokens
Google-->>Auth: access_token, refresh_token Google-->>Auth: access_token, refresh_token
Auth->>DB: UPDATE gmail_tokens<br/>SET access_token, refresh_token Auth->>DB: UPDATE gmail_token<br/>SET access_token, refresh_token
Auth-->>Front: 인증 완료 Auth-->>Front: 인증 완료
``` ```
@ -95,7 +95,7 @@ curl -X GET "http://localhost:9000/api/gmail/auth?user_id=1e16e9d5-59f3-54da-a66
# 4. 토큰 저장 확인 # 4. 토큰 저장 확인
export PGPASSWORD=robeings export PGPASSWORD=robeings
psql -h localhost -U robeings -d main_db -c \ psql -h localhost -U robeings -d main_db -c \
"SELECT user_id, access_token IS NOT NULL FROM gmail_tokens WHERE user_id = '1e16e9d5-59f3-54da-a661-8abeabff4230';" "SELECT user_id, access_token IS NOT NULL FROM gmail_token WHERE user_id = '1e16e9d5-59f3-54da-a661-8abeabff4230';"
# 5. 브리핑 수동 테스트 # 5. 브리핑 수동 테스트
curl -X POST http://192.168.219.52:8001/api/cron/daily-summary \ curl -X POST http://192.168.219.52:8001/api/cron/daily-summary \
@ -123,7 +123,7 @@ psql -h localhost -U robeings -d main_db -c \
gt.expires_at < extract(epoch from now()) as is_expired, gt.expires_at < extract(epoch from now()) as is_expired,
gt.metadata->>'email' as email gt.metadata->>'email' as email
FROM users u FROM users u
LEFT JOIN gmail_tokens gt ON u.id = gt.user_id LEFT JOIN gmail_token gt ON u.id = gt.user_id
WHERE u.username IN ('happybell80', '0914eagle', 'cdctfm');" WHERE u.username IN ('happybell80', '0914eagle', 'cdctfm');"
``` ```
@ -141,7 +141,7 @@ curl -s "http://localhost:9000/api/gmail/auth?user_id=1e16e9d5-59f3-54da-a661-8a
# 4. 인증 성공 확인 # 4. 인증 성공 확인
psql -h localhost -U robeings -d main_db -c \ psql -h localhost -U robeings -d main_db -c \
"SELECT access_token IS NOT NULL as success "SELECT access_token IS NOT NULL as success
FROM gmail_tokens FROM gmail_token
WHERE user_id = '1e16e9d5-59f3-54da-a661-8abeabff4230';" WHERE user_id = '1e16e9d5-59f3-54da-a661-8abeabff4230';"
``` ```
@ -190,7 +190,7 @@ curl -s "http://localhost:9000/api/gmail/auth?user_id=69ae4ea9-a15f-5110-9f5d-65
### 7.1 문제 상황 ### 7.1 문제 상황
- **증상**: Slack DM 대화가 PostgreSQL에 저장 안됨 - **증상**: Slack DM 대화가 PostgreSQL에 저장 안됨
- **ChromaDB**: ✅ 정상 저장 - **ChromaDB**: ✅ 정상 저장
- **PostgreSQL**: ❌ 저장 실패 (conversation_logs 테이블 0 rows) - **PostgreSQL**: ❌ 저장 실패 (conversation_log 테이블 0 rows)
### 7.2 대화 저장 플로우 (현재: State Service 사용 안 함) ### 7.2 대화 저장 플로우 (현재: State Service 사용 안 함)
```mermaid ```mermaid
@ -207,7 +207,7 @@ sequenceDiagram
RB->>Chroma: store_memory() RB->>Chroma: store_memory()
Chroma-->>RB: ✅ 저장 성공<br/>ID: 2a776b26... Chroma-->>RB: ✅ 저장 성공<br/>ID: 2a776b26...
RB->>PG: 직접 INSERT INTO conversation_logs RB->>PG: 직접 INSERT INTO conversation_log
PG-->>RB: ✅ 저장 성공 PG-->>RB: ✅ 저장 성공
Note over RB: State Service 없이<br/>직접 DB 연결 Note over RB: State Service 없이<br/>직접 DB 연결

View File

@ -22,7 +22,7 @@ username: happybell80
email: goeun2dc@gmail.com email: goeun2dc@gmail.com
UUID: 1e16e9d5-59f3-54da-a661-8abeabff4230 UUID: 1e16e9d5-59f3-54da-a661-8abeabff4230
-- workspace_members 테이블 -- workspace_member 테이블
happybell80 → rb8001 (포트 8001) happybell80 → rb8001 (포트 8001)
eagle0914 → rb10508_micro (포트 10508) eagle0914 → rb10508_micro (포트 10508)
``` ```

View File

@ -203,7 +203,7 @@ WHERE robeing_id = 'rb8001';
- **DB 테이블**: - **DB 테이블**:
- `robeing_stats` - 실제 스탯 저장 - `robeing_stats` - 실제 스탯 저장
- `workspace_members` - 사용자-로빙 매핑 - `workspace_member` - 사용자-로빙 매핑
--- ---

View File

@ -142,7 +142,7 @@ Stats request from user: happybell80 for robeing: undefined
### 4.3 대화 저장 확인 ❌ ### 4.3 대화 저장 확인 ❌
```sql ```sql
SELECT COUNT(*) FROM conversation_logs; -- 0건 SELECT COUNT(*) FROM conversation_log; -- 0건
-- rb8001에서 DB 저장 로직 미구현 -- rb8001에서 DB 저장 로직 미구현
``` ```

View File

@ -100,7 +100,7 @@ async def message_endpoint(request: MessageRequest):
user_id=request.user_id or "web_user", user_id=request.user_id or "web_user",
task_type="chat", task_type="chat",
context=request.context, context=request.context,
channel="web" # ⚠️ Slack DM과 동일 (개선: 250828_conversation_logs_channel_구분_개선.md) channel="web" # ⚠️ Slack DM과 동일 (개선: 250828_conversation_log_channel_구분_개선.md)
) )
# Frontend가 기대하는 응답 형식 # Frontend가 기대하는 응답 형식
@ -195,7 +195,7 @@ rb8001(8001) → /api/message 처리
- Gateway 로그: 라우팅 성공 - Gateway 로그: 라우팅 성공
- rb8001 로그: 메시지 처리 - rb8001 로그: 메시지 처리
- ChromaDB: 대화 저장 - ChromaDB: 대화 저장
- PostgreSQL: conversation_logs 기록 - PostgreSQL: conversation_log 기록
--- ---

View File

@ -43,7 +43,7 @@ grep -r "save_conversation\|store_memory" rb8001/
PGPASSWORD=robeings psql -h 192.168.219.45 -U robeings -d main_db -c "\dt" PGPASSWORD=robeings psql -h 192.168.219.45 -U robeings -d main_db -c "\dt"
# 결과: # 결과:
# - conversation_logs 테이블 존재 ✅ # - conversation_log 테이블 존재 ✅
# - robeing_stats 테이블 존재 ✅ # - robeing_stats 테이블 존재 ✅
# - 직접 연결 가능 # - 직접 연결 가능
``` ```
@ -158,18 +158,18 @@ graph LR
``` ```
2. **원인 분석** 2. **원인 분석**
- conversation_logs 테이블의 user_id가 UUID 타입 - conversation_log 테이블의 user_id가 UUID 타입
- rb8001은 Slack user ID (문자열) 전송 - rb8001은 Slack user ID (문자열) 전송
- UUID 변환 불가능한 값으로 저장 실패 - UUID 변환 불가능한 값으로 저장 실패
### DB 스키마 수정 (51123 서버) ### DB 스키마 수정 (51123 서버)
```sql ```sql
-- slack_user_id 컬럼 추가 -- slack_user_id 컬럼 추가
ALTER TABLE conversation_logs ALTER TABLE conversation_log
ADD COLUMN slack_user_id VARCHAR(255); ADD COLUMN slack_user_id VARCHAR(255);
-- user_id를 nullable로 변경 -- user_id를 nullable로 변경
ALTER TABLE conversation_logs ALTER TABLE conversation_log
ALTER COLUMN user_id DROP NOT NULL; ALTER COLUMN user_id DROP NOT NULL;
``` ```
@ -191,7 +191,7 @@ conversation_log = ConversationLog(
```python ```python
class ConversationLog(Base): class ConversationLog(Base):
"""대화 로그 테이블""" """대화 로그 테이블"""
__tablename__ = "conversation_logs" __tablename__ = "conversation_log"
id = Column(Integer, primary_key=True, index=True) id = Column(Integer, primary_key=True, index=True)
robeing_id = Column(String, index=True) robeing_id = Column(String, index=True)
@ -223,13 +223,13 @@ docker logs rb8001 --tail 100 | grep -E "ChromaDB|PostgreSQL|saved"
### DB 확인 ### DB 확인
```sql ```sql
-- PostgreSQL 저장 확인 -- PostgreSQL 저장 확인
SELECT COUNT(*) FROM conversation_logs SELECT COUNT(*) FROM conversation_log
WHERE robeing_id = 'rb8001' WHERE robeing_id = 'rb8001'
AND timestamp > NOW() - INTERVAL '1 hour'; AND timestamp > NOW() - INTERVAL '1 hour';
-- 최근 대화 확인 -- 최근 대화 확인
SELECT user_id, message, response, timestamp SELECT user_id, message, response, timestamp
FROM conversation_logs FROM conversation_log
WHERE robeing_id = 'rb8001' WHERE robeing_id = 'rb8001'
ORDER BY timestamp DESC ORDER BY timestamp DESC
LIMIT 5; LIMIT 5;

View File

@ -1,4 +1,4 @@
# ID 체계 정리 및 conversation_logs 저장 문제 해결 # ID 체계 정리 및 conversation_log 저장 문제 해결
## 작성일: 2025-08-26 (수정: 2025-08-28) ## 작성일: 2025-08-26 (수정: 2025-08-28)
## 작성자: 서버 관리자 ## 작성자: 서버 관리자
@ -12,12 +12,12 @@
### 1.1 발견된 문제들 ### 1.1 발견된 문제들
1. **user_id UUID 타입 문제** 1. **user_id UUID 타입 문제**
- PostgreSQL의 conversation_logs 테이블에서 user_id가 UUID 타입 - PostgreSQL의 conversation_log 테이블에서 user_id가 UUID 타입
- "system"이나 Slack user_id (예: U0925SXQFDK)는 UUID가 아님 - "system"이나 Slack user_id (예: U0925SXQFDK)는 UUID가 아님
- UUID 변환 시도 시 오류 발생 - UUID 변환 시도 시 오류 발생
2. **대화 저장 전혀 안됨** 2. **대화 저장 전혀 안됨**
- conversation_logs 테이블 완전히 비어있음 (0 rows) - conversation_log 테이블 완전히 비어있음 (0 rows)
- 게이트웨이 로그에 저장 API 호출 없음 - 게이트웨이 로그에 저장 API 호출 없음
- DB 설정 오류 (auth_db → main_db) - DB 설정 오류 (auth_db → main_db)
@ -27,28 +27,28 @@
### 2.1 문제점 ### 2.1 문제점
#### 다양한 ID 타입 혼재 #### 다양한 ID 타입 혼재
- **UUID**: users.id, workspace_members.id, slack_user_mapping.user_id - **UUID**: users.id, workspace_member.id, slack_user_mapping.user_id
- **VARCHAR**: robeing_id, slack_user_id, slack_id, channel_id - **VARCHAR**: robeing_id, slack_user_id, slack_id, channel_id
- **INTEGER**: conversation_logs.id, gmail_tokens.id - **INTEGER**: conversation_log.id, gmail_token.id
#### 2025-08-28 확인 #### 2025-08-28 확인
- gmail_tokens.slack_user_id: VARCHAR(100) - gmail_token.slack_user_id: VARCHAR(100)
- gmail_tokens.user_id: UUID (FK → users.id) - gmail_token.user_id: UUID (FK → users.id)
- Slack ID와 UUID 동시 저장 - Slack ID와 UUID 동시 저장
#### 변환 문제 #### 변환 문제
- UUID ↔ Slack ID 변환 로직 불일치 - UUID ↔ Slack ID 변환 로직 불일치
- conversation_logs의 user_id(UUID)에 Slack ID 저장 시도로 실패 - conversation_log의 user_id(UUID)에 Slack ID 저장 시도로 실패
### 2.2 테이블별 ID 현황 ### 2.2 테이블별 ID 현황
| 테이블 | Primary Key | 외래 키/참조 ID | | 테이블 | Primary Key | 외래 키/참조 ID |
|--------|------------|---------------| |--------|------------|---------------|
| **users** | id (UUID) | oauth_id (VARCHAR) | | **users** | id (UUID) | oauth_id (VARCHAR) |
| **workspace_members** | id (UUID) | user_id (UUID), workspace_id (UUID), robeing_id (VARCHAR) | | **workspace_member** | id (UUID) | user_id (UUID), workspace_id (UUID), robeing_id (VARCHAR) |
| **gmail_tokens** | id (INT) | user_id (UUID), slack_id (VARCHAR), robeing_id (VARCHAR) | | **gmail_token** | id (INT) | user_id (UUID), slack_id (VARCHAR), robeing_id (VARCHAR) |
| **slack_user_mapping** | id (UUID) | user_id (UUID), slack_user_id (VARCHAR), workspace_member_id (UUID) | | **slack_user_mapping** | id (UUID) | user_id (UUID), slack_user_id (VARCHAR), workspace_member_id (UUID) |
| **conversation_logs** | id (INT) | user_id (UUID), slack_user_id (VARCHAR)*, robeing_id (VARCHAR) | | **conversation_log** | id (INT) | user_id (UUID), slack_user_id (VARCHAR)*, robeing_id (VARCHAR) |
*slack_user_id는 이 문제 해결을 위해 새로 추가한 컬럼 *slack_user_id는 이 문제 해결을 위해 새로 추가한 컬럼
@ -60,13 +60,13 @@
``` ```
[Slack ID] ──┬──> [slack_user_mapping] ──> [users.id (UUID)] [Slack ID] ──┬──> [slack_user_mapping] ──> [users.id (UUID)]
├──> [gmail_tokens.slack_id] ├──> [gmail_token.slack_id]
└──> [conversation_logs.slack_user_id] └──> [conversation_log.slack_user_id]
[users.id] ──┬──> [workspace_members.user_id] [users.id] ──┬──> [workspace_member.user_id]
├──> [gmail_tokens.user_id] ├──> [gmail_token.user_id]
├──> [conversation_logs.user_id] ├──> [conversation_log.user_id]
└──> [robeing_stats.user_id] └──> [robeing_stats.user_id]
``` ```
@ -86,10 +86,10 @@ U092F7FQ55L | cdctfm
### 4.1 단기 해결책 (즉시 적용) ✅ 완료 ### 4.1 단기 해결책 (즉시 적용) ✅ 완료
```sql ```sql
-- 1. user_id를 nullable로 변경 -- 1. user_id를 nullable로 변경
ALTER TABLE conversation_logs ALTER COLUMN user_id DROP NOT NULL; ALTER TABLE conversation_log ALTER COLUMN user_id DROP NOT NULL;
-- 2. slack_user_id 컬럼 추가 -- 2. slack_user_id 컬럼 추가
ALTER TABLE conversation_logs ADD COLUMN slack_user_id VARCHAR(100); ALTER TABLE conversation_log ADD COLUMN slack_user_id VARCHAR(100);
``` ```
### 4.2 중기 해결책 (표준화) ### 4.2 중기 해결책 (표준화)
@ -100,8 +100,8 @@ ALTER TABLE conversation_logs ADD COLUMN slack_user_id VARCHAR(100);
**작업 필요**: **작업 필요**:
```sql ```sql
-- gmail_tokens 테이블 컬럼명 변경 -- gmail_token 테이블 컬럼명 변경
ALTER TABLE gmail_tokens RENAME COLUMN slack_id TO slack_user_id; ALTER TABLE gmail_token RENAME COLUMN slack_id TO slack_user_id;
``` ```
### 4.3 장기 해결책 (구조 개선) ### 4.3 장기 해결책 (구조 개선)
@ -124,7 +124,7 @@ SELECT DISTINCT
wm.robeing_id wm.robeing_id
FROM users u FROM users u
LEFT JOIN slack_user_mapping sum ON u.id = sum.user_id LEFT JOIN slack_user_mapping sum ON u.id = sum.user_id
LEFT JOIN workspace_members wm ON u.id = wm.user_id; LEFT JOIN workspace_member wm ON u.id = wm.user_id;
``` ```
--- ---
@ -163,8 +163,8 @@ DATABASE_URL=postgresql://robeings:robeings@localhost:5433/main_db
## 6. 작업 완료 현황 ## 6. 작업 완료 현황
1. ✅ conversation_logs에 slack_user_id 추가 (완료) 1. ✅ conversation_log에 slack_user_id 추가 (완료)
2. ✅ gmail_tokens의 slack_user_id 컬럼 확인 (이미 존재, slack_id 컬럼 없음 - 2025-08-27 확인) 2. ✅ gmail_token의 slack_user_id 컬럼 확인 (이미 존재, slack_id 컬럼 없음 - 2025-08-27 확인)
3. ✅ **뉴스 키워드 하드코딩 제거** (2025-08-26 21:00 완료) 3. ✅ **뉴스 키워드 하드코딩 제거** (2025-08-26 21:00 완료)
- rb8001/app/skills/dm_skill.py 수정 (커밋: aed931e) - rb8001/app/skills/dm_skill.py 수정 (커밋: aed931e)
- user_preferences 테이블 활용 - user_preferences 테이블 활용
@ -180,7 +180,7 @@ DATABASE_URL=postgresql://robeings:robeings@localhost:5433/main_db
### 7.1 대화 저장 확인 ### 7.1 대화 저장 확인
```sql ```sql
-- 저장된 대화 확인 -- 저장된 대화 확인
SELECT COUNT(*) FROM conversation_logs; SELECT COUNT(*) FROM conversation_log;
-- 최근 대화 로그 -- 최근 대화 로그
SELECT SELECT
@ -189,7 +189,7 @@ SELECT
cl.message, cl.message,
cl.response, cl.response,
cl.timestamp cl.timestamp
FROM conversation_logs cl FROM conversation_log cl
ORDER BY cl.timestamp DESC ORDER BY cl.timestamp DESC
LIMIT 5; LIMIT 5;
``` ```
@ -204,7 +204,7 @@ SELECT
wm.robeing_id wm.robeing_id
FROM slack_user_mapping sum FROM slack_user_mapping sum
JOIN users u ON sum.user_id = u.id JOIN users u ON sum.user_id = u.id
LEFT JOIN workspace_members wm ON u.id = wm.user_id; LEFT JOIN workspace_member wm ON u.id = wm.user_id;
``` ```
--- ---

View File

@ -3,7 +3,7 @@
## 작성일: 2025-08-26 ## 작성일: 2025-08-26
## 작성자: 51124 서버 담당 ## 작성자: 51124 서버 담당
## 상태: ✅ 완전 해결 (ChromaDB ✅, PostgreSQL ✅, Foreign key ✅) ## 상태: ✅ 완전 해결 (ChromaDB ✅, PostgreSQL ✅, Foreign key ✅)
## 영향: PostgreSQL conversation_logs 정상 저장 ## 영향: PostgreSQL conversation_log 정상 저장
## 최종 업데이트: 2025-08-26 19:30 ## 최종 업데이트: 2025-08-26 19:30
--- ---
@ -51,12 +51,12 @@ LINE 1: ... VALUES ('rb8001', 'test_user_123', 'web', ...
### 2.3 PostgreSQL 스키마 문제 ### 2.3 PostgreSQL 스키마 문제
```sql ```sql
-- conversation_logs 테이블 스키마 -- conversation_log 테이블 스키마
user_id UUID -- 문제: UUID 타입만 허용 user_id UUID -- 문제: UUID 타입만 허용
slack_user_id VARCHAR(100) -- 추가됨 (250826 문서) slack_user_id VARCHAR(100) -- 추가됨 (250826 문서)
-- 실제 저장 시도 -- 실제 저장 시도
INSERT INTO conversation_logs (user_id, ...) INSERT INTO conversation_log (user_id, ...)
VALUES ('test_user_123', ...) -- 실패: UUID 형식 아님 VALUES ('test_user_123', ...) -- 실패: UUID 형식 아님
``` ```
@ -168,7 +168,7 @@ curl -X POST http://localhost:8001/api/message \
# PostgreSQL 확인 # PostgreSQL 확인
psql -h localhost -p 5433 -U robeings -d main_db \ psql -h localhost -p 5433 -U robeings -d main_db \
-c "SELECT * FROM conversation_logs ORDER BY timestamp DESC LIMIT 1;" -c "SELECT * FROM conversation_log ORDER BY timestamp DESC LIMIT 1;"
# ChromaDB 확인 (수정 후) # ChromaDB 확인 (수정 후)
docker exec rb8001 python -c " docker exec rb8001 python -c "
@ -204,7 +204,7 @@ print('Collections:', client.list_collections())
### 데이터 위치 ### 데이터 위치
- ChromaDB: `/home/admin/ivada_project/rb8001/chroma_db/` - ChromaDB: `/home/admin/ivada_project/rb8001/chroma_db/`
- PostgreSQL: `main_db.conversation_logs` 테이블 - PostgreSQL: `main_db.conversation_log` 테이블
--- ---
@ -231,7 +231,7 @@ print('Collections:', client.list_collections())
### 12.2 ⚠️ 남은 문제 ### 12.2 ⚠️ 남은 문제
1. **51124 서버 rb8001**: 1. **51124 서버 rb8001**:
- conversation_logs 저장 안됨 (최근 데이터 14시간 전) - conversation_log 저장 안됨 (최근 데이터 14시간 전)
- UUID 타입 에러로 PostgreSQL 저장 실패 중 - UUID 타입 에러로 PostgreSQL 저장 실패 중
- ChromaDB만 저장되고 PostgreSQL은 실패 - ChromaDB만 저장되고 PostgreSQL은 실패

View File

@ -15,7 +15,7 @@
|--------|------------|------------|------| |--------|------------|------------|------|
| slack_user_mapping | slack_user_id | slack_user_id | VARCHAR(100) | | slack_user_mapping | slack_user_id | slack_user_id | VARCHAR(100) |
| gmail_token | ~~slack_id~~ ❌ | **slack_user_id** ✅ | VARCHAR(100) | | gmail_token | ~~slack_id~~ ❌ | **slack_user_id** ✅ | VARCHAR(100) |
| conversation_logs | slack_user_id | slack_user_id | VARCHAR(100) | | conversation_log | slack_user_id | slack_user_id | VARCHAR(100) |
### ✅ 적용 완료 ### ✅ 적용 완료

View File

@ -83,7 +83,7 @@ const userId = localStorage.getItem('user_id') || 'default_user';
### 2.5 Database ### 2.5 Database
```sql ```sql
-- conversation_logs 테이블 -- conversation_log 테이블
user_id UUID -- UUID 타입 요구 user_id UUID -- UUID 타입 요구
slack_user_id VARCHAR(100) -- username이 여기 저장됨 slack_user_id VARCHAR(100) -- username이 여기 저장됨
@ -179,14 +179,14 @@ SELECT id, username FROM users WHERE username='happybell80';
# database.py:184: SELECT id::text as user_id # database.py:184: SELECT id::text as user_id
# username → UUID 변환 정상 작동 # username → UUID 변환 정상 작동
# conversation_logs 확인 (51124 보고) # conversation_log 확인 (51124 보고)
# user_id: NULL, slack_user_id: 'happybell80' # user_id: NULL, slack_user_id: 'happybell80'
# "Non-UUID user_id" 에러 발생 # "Non-UUID user_id" 에러 발생
``` ```
### 5.2 수정 후 검증 ### 5.2 수정 후 검증
- JWT `sub` 필드가 UUID 형식 (36자) - JWT `sub` 필드가 UUID 형식 (36자)
- conversation_logs.user_id에 UUID 저장 - conversation_log.user_id에 UUID 저장
- ChromaDB metadata.user_id가 UUID - ChromaDB metadata.user_id가 UUID
--- ---
@ -204,7 +204,7 @@ SELECT id, username FROM users WHERE username='happybell80';
## 7. 관련 문서 ## 7. 관련 문서
- [ID 체계 정리 (250826)](./250826_id_체계_정리_및_conversation_logs_문제_해결.md) - [ID 체계 정리 (250826)](./250826_id_체계_정리_및_conversation_log_문제_해결.md)
- [JWT 인증 구현 (250827)](./250827_JWT_인증_구현_COMPLETED.md) - [JWT 인증 구현 (250827)](./250827_JWT_인증_구현_COMPLETED.md)
- [Slack ID 표준화 (250826)](./250826_slack_id_column_standardization.md) - [Slack ID 표준화 (250826)](./250826_slack_id_column_standardization.md)

View File

@ -82,7 +82,7 @@ const activities: ActivityLog[] = [...]; // 하드코딩
const scheduledTasks: ScheduledTask[] = [...]; // 하드코딩 const scheduledTasks: ScheduledTask[] = [...]; // 하드코딩
``` ```
- 실제 데이터 조회 API 없음 - 실제 데이터 조회 API 없음
- conversation_logs 테이블 조회 엔드포인트 필요 - conversation_log 테이블 조회 엔드포인트 필요
### 3.4 ✅ 해결됨: API 구현 ### 3.4 ✅ 해결됨: API 구현
- **robeing-monitor**: preferences API 완전 구현 - **robeing-monitor**: preferences API 완전 구현

View File

@ -148,7 +148,7 @@ rb8001이 모든 사용자 대화를 하나의 ChromaDB 컬렉션에 저장:
- 자동 갱신 실패 시 알림 - 자동 갱신 실패 시 알림
2. **DB 컬럼명 표준화** 2. **DB 컬럼명 표준화**
- gmail_tokens.slack_id → slack_user_id - gmail_token.slack_id → slack_user_id
- 서버 작업 필요 - 서버 작업 필요
--- ---

View File

@ -67,7 +67,7 @@ workspace (로빙 1개)
### Phase 3: 사용자 격리 ✅ 완료 (2025-08-28 18:15) ### Phase 3: 사용자 격리 ✅ 완료 (2025-08-28 18:15)
- [x] ChromaDB 사용자별 컬렉션 (rb8001_{user_uuid}) - [x] ChromaDB 사용자별 컬렉션 (rb8001_{user_uuid})
- [x] conversation_logs UUID 저장 구현 - [x] conversation_log UUID 저장 구현
- [x] 완전한 사용자 대화 격리 - [x] 완전한 사용자 대화 격리
--- ---
@ -108,7 +108,7 @@ async def get_uuid(slack_id: str):
- rb8001: JWT 검증 구현 ✅ - rb8001: JWT 검증 구현 ✅
- rb8001: Slack ID → UUID 자동 변환 ✅ - rb8001: Slack ID → UUID 자동 변환 ✅
- ChromaDB: 사용자별 컬렉션 격리 ✅ - ChromaDB: 사용자별 컬렉션 격리 ✅
- conversation_logs: UUID 저장 ✅ - conversation_log: UUID 저장 ✅
--- ---
@ -117,7 +117,7 @@ async def get_uuid(slack_id: str):
### ✅ 검증 완료 ### ✅ 검증 완료
1. **JWT 인증**: UUID 추출 성공 (1e16e9d5-59f3-54da-a661-8abeabff4230) 1. **JWT 인증**: UUID 추출 성공 (1e16e9d5-59f3-54da-a661-8abeabff4230)
2. **ChromaDB 격리**: rb8001_{user_uuid} 컬렉션 생성 확인 2. **ChromaDB 격리**: rb8001_{user_uuid} 컬렉션 생성 확인
3. **PostgreSQL**: conversation_logs에 UUID 저장 확인 3. **PostgreSQL**: conversation_log에 UUID 저장 확인
4. **Slack 변환**: Slack ID → UUID 자동 변환 작동 4. **Slack 변환**: Slack ID → UUID 자동 변환 작동
5. **metadata None 처리**: Frontend 접속 시 slack_user_id=None 필터링 구현 ([250828_ChromaDB_metadata_None_error.md](250828_ChromaDB_metadata_None_error.md)) 5. **metadata None 처리**: Frontend 접속 시 slack_user_id=None 필터링 구현 ([250828_ChromaDB_metadata_None_error.md](250828_ChromaDB_metadata_None_error.md))

View File

@ -1,7 +1,7 @@
# conversation_logs 채널 구분 개선 # conversation_log 채널 구분 개선
**날짜**: 2025-08-28 **날짜**: 2025-08-28
**테이블**: conversation_logs **테이블**: conversation_log
**상태**: ✅ 해결 완료 (2025-08-28) **상태**: ✅ 해결 완료 (2025-08-28)
## 현재 상황 (DB 확인) ## 현재 상황 (DB 확인)
@ -39,7 +39,7 @@ Slack DM은 여전히 "D..."로 정상 저장됩니다.
### Phase 2: DB 스키마 개선 (선택) ### Phase 2: DB 스키마 개선 (선택)
```sql ```sql
ALTER TABLE conversation_logs ALTER TABLE conversation_log
ADD COLUMN thread_ts VARCHAR(20), -- 쓰레드 구분 ADD COLUMN thread_ts VARCHAR(20), -- 쓰레드 구분
ADD COLUMN channel_type VARCHAR(20); -- public_channel/private_channel/im/mpim ADD COLUMN channel_type VARCHAR(20); -- public_channel/private_channel/im/mpim
``` ```

View File

@ -70,7 +70,7 @@ async def get_user_mapping(identifier: str, db: Session = Depends(get_db)):
query = text(""" query = text("""
SELECT u.id as user_id, u.username, u.email, wm.robeing_id SELECT u.id as user_id, u.username, u.email, wm.robeing_id
FROM users u FROM users u
LEFT JOIN workspace_members wm ON u.id = wm.user_id LEFT JOIN workspace_member wm ON u.id = wm.user_id
WHERE u.id = :user_id WHERE u.id = :user_id
""") """)
result = db.execute(query, {"user_id": identifier}).first() result = db.execute(query, {"user_id": identifier}).first()
@ -144,7 +144,7 @@ async def route_message(self, message: str, user_id: str, ...):
### 2.7 Gateway 기본값 변경 ### 2.7 Gateway 기본값 변경
**파일**: `robeing-gateway/app/database.py` **파일**: `robeing-gateway/app/database.py`
```python ```python
# spaceboum 등 workspace_members 없는 사용자를 위해 # spaceboum 등 workspace_member 없는 사용자를 위해
DEFAULT_ROBEING_PORT = int(os.getenv('DEFAULT_ROBEING_PORT', '8001')) # 10508 → 8001 DEFAULT_ROBEING_PORT = int(os.getenv('DEFAULT_ROBEING_PORT', '8001')) # 10508 → 8001
DEFAULT_ROBEING_ID = os.getenv('DEFAULT_ROBEING_ID', 'rb8001') # rb10508_test → rb8001 DEFAULT_ROBEING_ID = os.getenv('DEFAULT_ROBEING_ID', 'rb8001') # rb10508_test → rb8001
``` ```

View File

@ -10,7 +10,7 @@
## 1. 문제 상황 ## 1. 문제 상황
- **현상**: rb8001이 ChromaDB 벡터 검색만 참조하여 엉뚱한 답변 - **현상**: rb8001이 ChromaDB 벡터 검색만 참조하여 엉뚱한 답변
- **원인**: PostgreSQL conversation_logs의 최근 대화 미참조 - **원인**: PostgreSQL conversation_log의 최근 대화 미참조
- **영향**: 직전 대화 컨텍스트 손실, 일관성 없는 응답 - **영향**: 직전 대화 컨텍스트 손실, 일관성 없는 응답
- **테스트**: "내 이름은 김종태" → "내 이름은?" → "모릅니다" 응답 - **테스트**: "내 이름은 김종태" → "내 이름은?" → "모릅니다" 응답

View File

@ -12,8 +12,8 @@
Slack OAuth로 로그인한 사용자가 Robeing 워크스페이스에 자동 할당되지 않음 Slack OAuth로 로그인한 사용자가 Robeing 워크스페이스에 자동 할당되지 않음
### 테스트 케이스 ### 테스트 케이스
- spaceboum (Google 로그인): workspace_members 없음 → 기본값 rb8001 - spaceboum (Google 로그인): workspace_member 없음 → 기본값 rb8001
- 홍태주 (Slack 로그인): workspace_members 없음 → 기본값 rb8001 - 홍태주 (Slack 로그인): workspace_member 없음 → 기본값 rb8001
--- ---
@ -34,7 +34,7 @@ Slack OAuth로 로그인한 사용자가 Robeing 워크스페이스에 자동
- 매핑 있으면: 기존 User 사용 - 매핑 있으면: 기존 User 사용
- 매핑 없으면: 새 User 생성 - 매핑 없으면: 새 User 생성
4. **workspace_members 추가**: ❌ **없음** 4. **workspace_member 추가**: ❌ **없음**
- Robeing 워크스페이스 할당 로직 부재 - Robeing 워크스페이스 할당 로직 부재
- SlackWorkspace ≠ Robeing Workspace (별개 개념) - SlackWorkspace ≠ Robeing Workspace (별개 개념)
@ -45,7 +45,7 @@ Slack OAuth로 로그인한 사용자가 Robeing 워크스페이스에 자동
### 핵심 개념: team_id = workspace_id ### 핵심 개념: team_id = workspace_id
- **Slack team_id**: Slack 워크스페이스의 고유 ID (T로 시작) - **Slack team_id**: Slack 워크스페이스의 고유 ID (T로 시작)
- **workspace_members.workspace_id**: team_id를 저장하는 필드 - **workspace_member.workspace_id**: team_id를 저장하는 필드
- **즉, team_id가 그대로 workspace_id로 사용됨** - **즉, team_id가 그대로 workspace_id로 사용됨**
#### slack_workspaces 테이블 #### slack_workspaces 테이블
@ -53,7 +53,7 @@ Slack OAuth로 로그인한 사용자가 Robeing 워크스페이스에 자동
- **주요 컬럼**: team_id (Slack ID), team_name, bot_token - **주요 컬럼**: team_id (Slack ID), team_name, bot_token
- **현재 데이터**: GoodGang Labs (T035VFRKCN6), test (T097FCTDVEX) - **현재 데이터**: GoodGang Labs (T035VFRKCN6), test (T097FCTDVEX)
#### workspace_members 테이블 #### workspace_member 테이블
- **용도**: 사용자와 Slack 워크스페이스 연결 - **용도**: 사용자와 Slack 워크스페이스 연결
- **주요 컬럼**: user_id, workspace_id (=team_id), robeing_id - **주요 컬럼**: user_id, workspace_id (=team_id), robeing_id
- **문제**: Slack 로그인 시 자동 추가 안 됨 - **문제**: Slack 로그인 시 자동 추가 안 됨
@ -64,13 +64,13 @@ Slack OAuth로 로그인한 사용자가 Robeing 워크스페이스에 자동
| users | 사용자 정보 | ✅ 생성됨 | | users | 사용자 정보 | ✅ 생성됨 |
| slack_user_mapping | Slack ID ↔ UUID | ✅ 생성됨 | | slack_user_mapping | Slack ID ↔ UUID | ✅ 생성됨 |
| slack_workspaces | Slack 팀 정보 | ✅ 조회됨 | | slack_workspaces | Slack 팀 정보 | ✅ 조회됨 |
| workspace_members | **사용자-워크스페이스 멤버십** | ❌ 추가 안 됨 | | workspace_member | **사용자-워크스페이스 멤버십** | ❌ 추가 안 됨 |
--- ---
## 4. 영향 ## 4. 영향
- 모든 Slack 로그인 사용자가 workspace_members 없음 - 모든 Slack 로그인 사용자가 workspace_member 없음
- Gateway가 기본값(rb8001 또는 rb10508) 사용 - Gateway가 기본값(rb8001 또는 rb10508) 사용
- 사용자별 Robeing 할당 불가능 - 사용자별 Robeing 할당 불가능
@ -82,10 +82,10 @@ Slack OAuth로 로그인한 사용자가 Robeing 워크스페이스에 자동
304번 줄 이후 추가: 304번 줄 이후 추가:
```python ```python
# workspace_members 추가 (team_id = workspace_id) # workspace_member 추가 (team_id = workspace_id)
if team_id and user: if team_id and user:
db.execute(text(""" db.execute(text("""
INSERT INTO workspace_members (user_id, workspace_id, robeing_id) INSERT INTO workspace_member (user_id, workspace_id, robeing_id)
VALUES (:user_id, :workspace_id, :robeing_id) VALUES (:user_id, :workspace_id, :robeing_id)
ON CONFLICT (user_id, workspace_id) DO UPDATE ON CONFLICT (user_id, workspace_id) DO UPDATE
SET robeing_id = :robeing_id SET robeing_id = :robeing_id
@ -98,8 +98,8 @@ if team_id and user:
### 수동 해결 SQL (참고용) ### 수동 해결 SQL (참고용)
```sql ```sql
-- happybell80 사용자 workspace_members 추가 -- happybell80 사용자 workspace_member 추가
INSERT INTO workspace_members (user_id, workspace_id, robeing_id) INSERT INTO workspace_member (user_id, workspace_id, robeing_id)
VALUES ( VALUES (
'1e16e9d5-59f3-54da-a661-8abeabff4230', -- happybell80 user_id '1e16e9d5-59f3-54da-a661-8abeabff4230', -- happybell80 user_id
'T035VFRKCN6', -- GoodGang Labs team_id 'T035VFRKCN6', -- GoodGang Labs team_id

View File

@ -23,7 +23,7 @@
### 1. DB 스키마 불일치 ⚠️ ### 1. DB 스키마 불일치 ⚠️
**문제점**: **문제점**:
- `companies` 테이블과 `workspaces` 테이블이 공존 - `company` 테이블과 `workspaces` 테이블이 공존
- `SlackWorkspace` 모델이 `company_id`를 참조하는데 모델은 `workspace_id` 기대 - `SlackWorkspace` 모델이 `company_id`를 참조하는데 모델은 `workspace_id` 기대
- Relationship 주석 처리로 임시 해결 - Relationship 주석 처리로 임시 해결
@ -31,13 +31,13 @@
```python ```python
# app/models/workspace.py # app/models/workspace.py
class SlackWorkspace(Base): class SlackWorkspace(Base):
company_id = Column(UUID, ForeignKey("companies.id")) # 실제 DB company_id = Column(UUID, ForeignKey("company.id")) # 실제 DB
# workspace_id로 되어야 하는데 companies 테이블 참조 중 # workspace_id로 되어야 하는데 company 테이블 참조 중
# relationship 주석 처리됨 # relationship 주석 처리됨
``` ```
**해결 방안**: **해결 방안**:
1. `companies` 테이블 데이터를 `workspaces`로 마이그레이션 1. `company` 테이블 데이터를 `workspaces`로 마이그레이션
2. FK 관계 정리 2. FK 관계 정리
3. 모델 통일 3. 모델 통일
@ -101,7 +101,7 @@ login_callback_url = "https://auth.ro-being.com/auth/slack/login/callback" #
## 향후 작업 ## 향후 작업
### 긴급도 높음 🔴 ### 긴급도 높음 🔴
1. DB 스키마 통일 (companies → workspaces) 1. DB 스키마 통일 (company → workspaces)
2. Frontend 인증 방식 통일 2. Frontend 인증 방식 통일
### 중간 우선순위 🟡 ### 중간 우선순위 🟡

View File

@ -17,7 +17,7 @@
```sql ```sql
-- 확인 결과: 모두 0 rows -- 확인 결과: 모두 0 rows
SELECT * FROM slack_user_mapping WHERE user_id = '237494f7-061c-484c-a4f7-f500611e32f1'; -- ❌ SELECT * FROM slack_user_mapping WHERE user_id = '237494f7-061c-484c-a4f7-f500611e32f1'; -- ❌
SELECT * FROM workspace_members WHERE user_id = '237494f7-061c-484c-a4f7-f500611e32f1'; -- ❌ SELECT * FROM workspace_member WHERE user_id = '237494f7-061c-484c-a4f7-f500611e32f1'; -- ❌
SELECT * FROM user_preferences WHERE user_id = '237494f7-061c-484c-a4f7-f500611e32f1'; -- ❌ SELECT * FROM user_preferences WHERE user_id = '237494f7-061c-484c-a4f7-f500611e32f1'; -- ❌
``` ```
@ -25,11 +25,11 @@ SELECT * FROM user_preferences WHERE user_id = '237494f7-061c-484c-a4f7-f500611e
auth-server의 Slack OAuth 콜백(`/auth/slack/login/callback`)에서: auth-server의 Slack OAuth 콜백(`/auth/slack/login/callback`)에서:
1. ✅ users 테이블 생성/업데이트 1. ✅ users 테이블 생성/업데이트
2. ❌ slack_user_mapping 생성 안 함 2. ❌ slack_user_mapping 생성 안 함
3. ❌ workspace_members 추가 안 함 3. ❌ workspace_member 추가 안 함
4. ❌ user_preferences 초기화 안 함 4. ❌ user_preferences 초기화 안 함
## 4. 영향 ## 4. 영향
- workspace_members 자동 추가 구현됨(9/11), 기본 robeing 할당 정상 - workspace_member 자동 추가 구현됨(9/11), 기본 robeing 할당 정상
- slack_user_mapping 의존 제거(9/11) → 이메일/기타 식별 병행 - slack_user_mapping 의존 제거(9/11) → 이메일/기타 식별 병행
- 상세: [/DOCS/troubleshooting/250831_slack_login_workspace_assignment_issue.md](./250831_slack_login_workspace_assignment_issue.md) - 상세: [/DOCS/troubleshooting/250831_slack_login_workspace_assignment_issue.md](./250831_slack_login_workspace_assignment_issue.md)
@ -38,8 +38,8 @@ auth-server의 Slack OAuth 콜백(`/auth/slack/login/callback`)에서:
-- 1. username 설정 -- 1. username 설정
UPDATE users SET username = 'hongtj' WHERE id = '237494f7-061c-484c-a4f7-f500611e32f1'; UPDATE users SET username = 'hongtj' WHERE id = '237494f7-061c-484c-a4f7-f500611e32f1';
-- 2. workspace_members 추가 (team_id 확인 필요) -- 2. workspace_member 추가 (team_id 확인 필요)
INSERT INTO workspace_members (user_id, workspace_id, robeing_id) INSERT INTO workspace_member (user_id, workspace_id, robeing_id)
VALUES ('237494f7-061c-484c-a4f7-f500611e32f1', 'T035VFRKCN6', 'rb8001'); VALUES ('237494f7-061c-484c-a4f7-f500611e32f1', 'T035VFRKCN6', 'rb8001');
``` ```

View File

@ -127,7 +127,7 @@ state={RANDOM_STATE}
**관련 테이블:** **관련 테이블:**
- workspaces: robeing_port, subdomain 컬럼 보유 - workspaces: robeing_port, subdomain 컬럼 보유
- workspace_members: workspace_id, user_id, robeing_id 매핑 - workspace_member: workspace_id, user_id, robeing_id 매핑
- slack_user_mapping: 사용자 매핑 - slack_user_mapping: 사용자 매핑
### 5.2 Slack 설정 정보 ### 5.2 Slack 설정 정보
@ -144,7 +144,7 @@ im:read, im:history, users:read, team:read, files:read, app_mentions:read
**workspace_id 하드코딩 문제 ✅ 해결:** **workspace_id 하드코딩 문제 ✅ 해결:**
- ~~TODO 주석만 있고 구현 안 됨~~ - ~~TODO 주석만 있고 구현 안 됨~~
- ~~모든 사용자가 동일 workspace_id 사용 (550e8400-e29b-41d4-a716-446655440000)~~ - ~~모든 사용자가 동일 workspace_id 사용 (550e8400-e29b-41d4-a716-446655440000)~~
- ~~workspace_members 조회 코드 주석 처리됨~~ - ~~workspace_member 조회 코드 주석 처리됨~~
**해결 내용:** **해결 내용:**
1. ~~WorkspaceMember 조회 로직 활성화 (Line 411-419)~~ → sqlalchemy text() 직접 쿼리로 변경 1. ~~WorkspaceMember 조회 로직 활성화 (Line 411-419)~~ → sqlalchemy text() 직접 쿼리로 변경
@ -161,9 +161,9 @@ im:read, im:history, users:read, team:read, files:read, app_mentions:read
**WorkspaceMember 모델 이슈:** **WorkspaceMember 모델 이슈:**
- app/models/workspace.py에 WorkspaceMember 클래스 정의 없음 - app/models/workspace.py에 WorkspaceMember 클래스 정의 없음
- workspace_members 테이블 직접 쿼리로 해결 - workspace_member 테이블 직접 쿼리로 해결
- ~~`SELECT workspace_id FROM workspace_members WHERE user_id = :user_id::uuid AND is_active = true`~~ - ~~`SELECT workspace_id FROM workspace_member WHERE user_id = :user_id::uuid AND is_active = true`~~
- `SELECT workspace_id FROM workspace_members WHERE user_id = (:user_id)::uuid AND is_active = true` (괄호 필수) - `SELECT workspace_id FROM workspace_member WHERE user_id = (:user_id)::uuid AND is_active = true` (괄호 필수)
### 5.4 추가 이슈 해결 (2025-09-02 저녁) ### 5.4 추가 이슈 해결 (2025-09-02 저녁)
@ -172,10 +172,10 @@ im:read, im:history, users:read, team:read, files:read, app_mentions:read
- Slack App 설정: 3개 URL 모두 등록 (callback, login/callback, passport/callback) - Slack App 설정: 3개 URL 모두 등록 (callback, login/callback, passport/callback)
**SlackWorkspace 모델 불일치:** **SlackWorkspace 모델 불일치:**
- 모델 정의: `company_id` (Line 44, ForeignKey는 companies.id) - 모델 정의: `company_id` (Line 44, ForeignKey는 company.id)
- 코드 사용: `workspace_id` 시도 → `company_id`로 수정 필요 - 코드 사용: `workspace_id` 시도 → `company_id`로 수정 필요
- Line 542, 557, 631, 674, 713: workspace_id → company_id - Line 542, 557, 631, 674, 713: workspace_id → company_id
- DB 현실: companies 테이블 없음, workspaces 테이블 사용 중 - DB 현실: company 테이블 없음, workspaces 테이블 사용 중
**Company-X 설정 상태:** **Company-X 설정 상태:**
- workspace_id: 99d22d6b-d327-4fa4-bd2f-d228c11056e2 - workspace_id: 99d22d6b-d327-4fa4-bd2f-d228c11056e2
@ -186,15 +186,15 @@ im:read, im:history, users:read, team:read, files:read, app_mentions:read
### 5.5 최종 이슈 (✅ 해결 완료) ### 5.5 최종 이슈 (✅ 해결 완료)
**Foreign Key 문제 해결:** **Foreign Key 문제 해결:**
- 모델: `ForeignKey("companies.id")` 참조 - 모델: `ForeignKey("company.id")` 참조
- ~~실제 DB: companies 테이블 없음~~ → **companies 테이블 존재 확인됨** - ~~실제 DB: company 테이블 없음~~ → **company 테이블 존재 확인됨**
- 해결 내용: - 해결 내용:
1. companies 테이블에 Company-X 레코드 추가 (ID: 99d22d6b-d327-4fa4-bd2f-d228c11056e2) 1. company 테이블에 Company-X 레코드 추가 (ID: 99d22d6b-d327-4fa4-bd2f-d228c11056e2)
2. 기존 slack_workspaces 레코드의 company_id 업데이트 완료 2. 기존 slack_workspaces 레코드의 company_id 업데이트 완료
3. Foreign Key 제약 조건 정상 작동 3. Foreign Key 제약 조건 정상 작동
**최종 DB 상태 (2025-09-02 20:40):** **최종 DB 상태 (2025-09-02 20:40):**
- companies 테이블: Company-X (8001 포트, active 상태) - company 테이블: Company-X (8001 포트, active 상태)
- workspaces 테이블: Company-X (8명 멤버) - workspaces 테이블: Company-X (8명 멤버)
- slack_workspaces: T097FCTDVEX → Company-X 연결 완료 - slack_workspaces: T097FCTDVEX → Company-X 연결 완료
- 봇 설치 준비 완료 - 봇 설치 준비 완료

View File

@ -68,6 +68,6 @@ response.messages // reverse() 없이 그대로 사용
- **결과**: 과거→최신 시간순 + 사용자→로빙 대화쌍 순서 모두 정상 - **결과**: 과거→최신 시간순 + 사용자→로빙 대화쌍 순서 모두 정상
## 7. 추가 확인 사항 ## 7. 추가 확인 사항
- **DB 저장 문제**: conversation_logs 테이블에 Slack 대화 저장 안 됨 (별개 이슈) - **DB 저장 문제**: conversation_log 테이블에 Slack 대화 저장 안 됨 (별개 이슈)
- **API 엔드포인트**: /api/conversations/recent 404 에러 (51124 서버) - **API 엔드포인트**: /api/conversations/recent 404 에러 (51124 서버)
- **테이블 컬럼**: message, response, timestamp 사용 (user_message 컬럼 없음) - **테이블 컬럼**: message, response, timestamp 사용 (user_message 컬럼 없음)

View File

@ -20,7 +20,7 @@
## 3. 확인된 사실 (문서 기반) ## 3. 확인된 사실 (문서 기반)
- 2025-08-25 DB 기반 토큰 저장으로 전환 완료 (마이그레이션 문서) - 2025-08-25 DB 기반 토큰 저장으로 전환 완료 (마이그레이션 문서)
- auth-server는 gmail_tokens 테이블에만 토큰 저장 (시퀀스 다이어그램) - auth-server는 gmail_token 테이블에만 토큰 저장 (시퀀스 다이어그램)
- skill-email은 DB에서 토큰 조회하도록 설계됨 (서비스 간 직접 전달 없음) - skill-email은 DB에서 토큰 조회하도록 설계됨 (서비스 간 직접 전달 없음)
- Docker 컨테이너 /code 디렉토리 쓰기 권한 없음 (에러 메시지) - Docker 컨테이너 /code 디렉토리 쓰기 권한 없음 (에러 메시지)
- access_token, refresh_token은 TEXT 타입으로 암호화 저장 (테이블 스키마) - access_token, refresh_token은 TEXT 타입으로 암호화 저장 (테이블 스키마)
@ -67,5 +67,5 @@
1. ✅ 프론트엔드 Gmail OAuth 성공 1. ✅ 프론트엔드 Gmail OAuth 성공
2. ✅ Permission denied 오류 없음 2. ✅ Permission denied 오류 없음
3. ✅ unknown_gmail.json 생성 시도 없음 3. ✅ unknown_gmail.json 생성 시도 없음
4. ✅ gmail_tokens 테이블 정상 저장 4. ✅ gmail_token 테이블 정상 저장
5. ✅ 파일 시스템 의존성 완전 제거 5. ✅ 파일 시스템 의존성 완전 제거

View File

@ -4,7 +4,7 @@
프론트엔드 대화창에서 슬랙/웹 대화 구분 불가 프론트엔드 대화창에서 슬랙/웹 대화 구분 불가
## 해결 방법 ## 해결 방법
conversation_logs 테이블의 slack_user_id 필드(VARCHAR(100))를 활용하여 슬랙 대화 시각적 표시 conversation_log 테이블의 slack_user_id 필드(VARCHAR(100))를 활용하여 슬랙 대화 시각적 표시
## 수정된 파일 및 코드 ## 수정된 파일 및 코드
@ -15,7 +15,7 @@ conversation_logs 테이블의 slack_user_id 필드(VARCHAR(100))를 활용하
# get_recent_conversations() 함수 # get_recent_conversations() 함수
query = text(""" query = text("""
SELECT message, response, timestamp, slack_user_id # slack_user_id 추가 SELECT message, response, timestamp, slack_user_id # slack_user_id 추가
FROM conversation_logs FROM conversation_log
WHERE user_id = :user_id WHERE user_id = :user_id
ORDER BY timestamp DESC ORDER BY timestamp DESC
LIMIT :limit LIMIT :limit

View File

@ -40,7 +40,7 @@ HanYong: token_data 비어있음 (8/30 이후)
-- 166번: token_data = jsonb_set(...) 사용 -- 166번: token_data = jsonb_set(...) 사용
-- 개별 컬럼 삭제 (코드 수정 후) -- 개별 컬럼 삭제 (코드 수정 후)
ALTER TABLE gmail_tokens ALTER TABLE gmail_token
DROP COLUMN access_token, DROP COLUMN access_token,
DROP COLUMN refresh_token, DROP COLUMN refresh_token,
DROP COLUMN token_type, DROP COLUMN token_type,

View File

@ -35,7 +35,7 @@
| auth-server/app/models/user.py | 21-35 | 테이블 모델 정의 (picture 컬럼) | | auth-server/app/models/user.py | 21-35 | 테이블 모델 정의 (picture 컬럼) |
| auth-server/app/providers/gmail_passport.py | 178 | SELECT id FROM users WHERE username = $1 | | auth-server/app/providers/gmail_passport.py | 178 | SELECT id FROM users WHERE username = $1 |
| auth-server/app/providers/gmail_passport.py | 187 | SELECT username FROM users WHERE id = $1 | | auth-server/app/providers/gmail_passport.py | 187 | SELECT username FROM users WHERE id = $1 |
| robeing-gateway/app/database.py | 98-112 | JOIN 쿼리 (workspace_members, workspaces와 함께) | | robeing-gateway/app/database.py | 98-112 | JOIN 쿼리 (workspace_member, workspaces와 함께) |
| robeing-gateway/app/database.py | 182-191 | SELECT * FROM users WHERE username = $1 | | robeing-gateway/app/database.py | 182-191 | SELECT * FROM users WHERE username = $1 |
| robeing-gateway/app/database.py | 321-328 | SELECT * FROM users (전체 조회) | | robeing-gateway/app/database.py | 321-328 | SELECT * FROM users (전체 조회) |
| robeing-gateway/app/models.py | 11-24 | 테이블 모델 정의 (avatar_url 컬럼) | | robeing-gateway/app/models.py | 11-24 | 테이블 모델 정의 (avatar_url 컬럼) |
@ -48,14 +48,14 @@
|-----------|-----------|-----------| |-----------|-----------|-----------|
| auth-server/app/models/workspace.py | 15-31 | 테이블 모델 정의 | | auth-server/app/models/workspace.py | 15-31 | 테이블 모델 정의 |
| auth-server/scripts/run_migration.py | 54 | SELECT id, name, subdomain, robeing_id FROM workspaces | | auth-server/scripts/run_migration.py | 54 | SELECT id, name, subdomain, robeing_id FROM workspaces |
| auth-server/migrations/add_user_workspace_tables.sql | 25-28 | ALTER TABLE companies RENAME TO workspaces | | auth-server/migrations/add_user_workspace_tables.sql | 25-28 | ALTER TABLE company RENAME TO workspaces |
| robeing-gateway/app/database.py | 98-112 | JOIN 쿼리 | | robeing-gateway/app/database.py | 98-112 | JOIN 쿼리 |
| robeing-gateway/app/database.py | 216-230 | JOIN 쿼리 (workspace_members와 함께) | | robeing-gateway/app/database.py | 216-230 | JOIN 쿼리 (workspace_member와 함께) |
| robeing-gateway/app/models.py | 26-44 | 테이블 모델 정의 | | robeing-gateway/app/models.py | 26-44 | 테이블 모델 정의 |
### 2.3 workspace_member 테이블 (실제 DB: workspace_member, 레코드 0건) ### 2.3 workspace_member 테이블 (실제 DB: workspace_member, 레코드 0건)
**실제 DB 컬럼**: user_id(FK), role(user_role enum), created_at, updated_at **실제 DB 컬럼**: user_id(FK), role(user_role enum), created_at, updated_at
**코드 모델**: workspace_members 테이블 참조 (복수형 차이) **코드 모델**: workspace_member 테이블 참조 (복수형 차이)
| 파일 경로 | 라인 번호 | 작업 내용 | | 파일 경로 | 라인 번호 | 작업 내용 |
|-----------|-----------|-----------| |-----------|-----------|-----------|
@ -90,13 +90,13 @@
### 2.6 gmail_token 테이블 (실제 DB: gmail_token, 3건) ### 2.6 gmail_token 테이블 (실제 DB: gmail_token, 3건)
**실제 DB 컬럼**: user_id(FK), token_data(jsonb), oauth_config(jsonb), created_at, updated_at **실제 DB 컬럼**: user_id(FK), token_data(jsonb), oauth_config(jsonb), created_at, updated_at
**코드 혼용**: auth-server는 gmail_tokens(복수)와 gmail_token(단수) 혼용 **코드 혼용**: auth-server는 gmail_token(복수)와 gmail_token(단수) 혼용
| 파일 경로 | 라인 번호 | 작업 내용 | | 파일 경로 | 라인 번호 | 작업 내용 |
|-----------|-----------|-----------| |-----------|-----------|-----------|
| auth-server/app/providers/gmail_passport.py | 193 | INSERT INTO gmail_token (단수) | | auth-server/app/providers/gmail_passport.py | 193 | INSERT INTO gmail_token (단수) |
| auth-server/app/providers/gmail_passport.py | 264, 313, 354, 370 | gmail_token (단수) 사용 | | auth-server/app/providers/gmail_passport.py | 264, 313, 354, 370 | gmail_token (단수) 사용 |
| auth-server/app/api/gmail_refresh.py | 36, 43, 115, 124, 173, 179 | gmail_tokens (복수) 사용 | | auth-server/app/api/gmail_refresh.py | 36, 43, 115, 124, 173, 179 | gmail_token (복수) 사용 |
| skill-email/services/db_credentials_provider.py | 60-70 | SELECT 쿼리 (is_equipped 필터) | | skill-email/services/db_credentials_provider.py | 60-70 | SELECT 쿼리 (is_equipped 필터) |
| skill-email/services/db_credentials_provider.py | 163-177 | UPDATE 쿼리 | | skill-email/services/db_credentials_provider.py | 163-177 | UPDATE 쿼리 |
| skill-email/services/db_credentials_provider.py | 231-235 | COUNT 쿼리 | | skill-email/services/db_credentials_provider.py | 231-235 | COUNT 쿼리 |
@ -214,7 +214,7 @@
## 4. 위험 요소 - 치명적 불일치 ## 4. 위험 요소 - 치명적 불일치
1. **테이블명 전체 불일치**: 1. **테이블명 전체 불일치**:
- 코드: users, workspaces, workspace_members (복수형) - 코드: users, workspaces, workspace_member (복수형)
- DB: user, company/team, workspace_member (단수형) - DB: user, company/team, workspace_member (단수형)
2. **존재하지 않는 테이블 참조**: 2. **존재하지 않는 테이블 참조**:

View File

@ -11,7 +11,7 @@
```python ```python
# 현재 코드 (잘못됨) # 현재 코드 (잘못됨)
cur.execute(""" cur.execute("""
SELECT ... FROM gmail_tokens SELECT ... FROM gmail_token
WHERE slack_user_id = %s # 존재하지 않는 컬럼 WHERE slack_user_id = %s # 존재하지 않는 컬럼
""", (user_id,)) """, (user_id,))

View File

@ -13,7 +13,7 @@
### 발견된 위반 사항 ### 발견된 위반 사항
#### 1. rb8001/app/skills/email_integration.py #### 1. rb8001/app/skills/email_integration.py
- **잘못된 테이블명**: `gmail_tokens` → 실제는 `gmail_token` - **잘못된 테이블명**: `gmail_token` → 실제는 `gmail_token`
- **Slack ID 사용**: skill-email 호출 시 Slack ID 전달 - **Slack ID 사용**: skill-email 호출 시 Slack ID 전달
- **미정의 변수**: `user_uuid` 변수 정의 없이 사용 (136줄) - **미정의 변수**: `user_uuid` 변수 정의 없이 사용 (136줄)
@ -44,7 +44,7 @@ else:
``` ```
### 테이블명 수정 (완료) ### 테이블명 수정 (완료)
- `gmail_tokens` → `gmail_token` - `gmail_token` → `gmail_token`
- `SELECT COUNT(*) FROM gmail_token WHERE user_id = %s` - `SELECT COUNT(*) FROM gmail_token WHERE user_id = %s`
## 남은 작업 ## 남은 작업