283 lines
11 KiB
Markdown
283 lines
11 KiB
Markdown
# Slack 봇 설치 URL 및 Event 수신 구조 분석
|
|
|
|
## 작성일: 2025-09-02
|
|
## 작성자: 51123 서버 관리자
|
|
## 이슈: Slack 봇 설치 URL 직접 생성 및 Event URL 자동 설정 방안
|
|
|
|
---
|
|
|
|
## 1. 현황 분석
|
|
|
|
### 1.1 구현 완료 항목
|
|
- **OAuth 엔드포인트**: `/auth/slack/passport/install` (app/providers/slack.py:387)
|
|
- **콜백 처리**: `/auth/slack/passport/callback` (app/providers/slack.py:456)
|
|
- **Event 라우터**: `/slack/events/router` (app/api/slack_router.py:49)
|
|
- **환경변수**: SLACK_CLIENT_ID, SLACK_CLIENT_SECRET 설정됨
|
|
- **DB 테이블**: slack_workspaces, slack_user_mapping 정상 존재
|
|
|
|
### 1.2 문제점
|
|
- **State 저장**: 메모리 딕셔너리(oauth_states) 사용 → 직접 URL 생성 시 검증 실패
|
|
- **workspace_id**: 하드코딩 (app/providers/slack.py:422) → 실제 매핑 구현 필요
|
|
- **Event URL**: 봇 설치와 별개로 Slack App 설정에서 수동 등록 필요
|
|
|
|
## 2. 해결 방안
|
|
|
|
### 2.1 State 관리 개선
|
|
```python
|
|
# 현재: 메모리 딕셔너리
|
|
oauth_states[state] = {...} # line 436
|
|
|
|
# 개선: Redis 사용 (이미 구현된 Redis 활용)
|
|
await redis_client.setex(f"oauth:state:{state}", 300, json.dumps(state_data))
|
|
```
|
|
|
|
### 2.2 Event URL 자동 설정 (Manifest API)
|
|
```python
|
|
# 필요 환경변수
|
|
SLACK_APP_ID = "A..." # Slack App ID
|
|
SLACK_APP_CONFIG_TOKEN = "xapp-..." # apps.manifest:write 스코프
|
|
|
|
# Manifest 업데이트 (봇 설치 콜백 후 1회)
|
|
manifest = {
|
|
"settings": {
|
|
"event_subscriptions": {
|
|
"request_url": "https://auth.ro-being.com/slack/events/router",
|
|
"bot_events": ["app_mention", "message.channels", "message.groups", "message.im"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2.3 직접 URL 생성
|
|
```bash
|
|
# OAuth URL 구조
|
|
https://slack.com/oauth/v2/authorize?
|
|
client_id=9073915808149.9085704341778&
|
|
scope=chat:write,channels:read,channels:history,groups:read,groups:history,im:read,im:history,users:read,team:read,files:read,app_mentions:read&
|
|
redirect_uri=https://auth.ro-being.com/auth/slack/passport/callback&
|
|
state={RANDOM_STATE}
|
|
```
|
|
|
|
## 3. 실행 계획
|
|
|
|
### 3.1 즉시 적용 가능
|
|
1. Slack App 설정에서 Event URL 수동 등록 (1회)
|
|
2. 현재 코드로 봇 설치 진행 가능
|
|
|
|
### 3.2 코드 개선 필요
|
|
1. Redis로 state 저장 로직 변경
|
|
2. workspace_id 하드코딩 제거 (Line 422: 550e8400-e29b-41d4-a716-446655440000)
|
|
3. Manifest API 자동화 스크립트 추가 (현재 구현 없음)
|
|
|
|
### 3.3 Redis 사용 현황 (2025-09-02 추가)
|
|
**엔드포인트별 state 저장 방식:**
|
|
- `/auth/slack/login/` (Line 78-89): Redis 사용 (`await redis_client.setex()`)
|
|
- `/auth/slack/passport/install` (Line 436): 메모리 dict 사용 (`oauth_states[state] = {}`)
|
|
- `/auth/slack/passport/callback` (Line 490-494): 메모리 dict 읽기 (`oauth_states.pop(state)`)
|
|
|
|
**전체 엔드포인트 목록 (7개):**
|
|
1. `/login/` - 사용자 로그인 (Redis)
|
|
2. `/login/callback` - 로그인 콜백 (Redis)
|
|
3. `/passport/install` - 봇 설치 (메모리 dict)
|
|
4. `/passport/callback` - 봇 설치 콜백 (메모리 dict)
|
|
5. `/passport/status/{workspace_id}` - 상태 확인
|
|
6. `/passport/uninstall/{workspace_id}` - 봇 제거
|
|
7. `/passport/token/{workspace_id}` - 토큰 조회
|
|
|
|
**문제점:**
|
|
- 동일 파일(slack.py)에서 Redis와 메모리 dict 혼용
|
|
- 서버 재시작 시 영향:
|
|
- 로그인 state: Redis에 저장되어 유지됨
|
|
- 봇 설치 state: 메모리에서 소실됨
|
|
- 봇 설치 진행 중 서버 재시작 시 콜백 실패
|
|
|
|
## 4. 중요 발견사항
|
|
|
|
### 4.1 Event URL 관련
|
|
- OAuth 설치 ≠ Event URL 설정 (별개 프로세스)
|
|
- Manifest API로 자동 업데이트 가능 (apps.manifest.update)
|
|
- Event 처리 엔드포인트: `/slack/events/router` (app/api/slack_router.py)
|
|
- url_verification 처리 구현됨 (Line 76-77)
|
|
- team_id로 workspace 식별
|
|
- Slack App 설정에서 수동 등록 필요
|
|
|
|
### 4.2 Event 처리 흐름 (2025-09-02 확인)
|
|
1. Slack → `/slack/events/router` (중앙 수신)
|
|
2. team_id로 slack_workspaces 테이블 조회
|
|
3. workspace.robeing_port로 컨테이너 라우팅
|
|
4. `http://localhost:{port}/events`로 전달 (rb8001 아님)
|
|
5. 헤더 추가: X-Workspace-Id, X-Workspace-Subdomain
|
|
|
|
### 4.3 아키텍처
|
|
- 컨테이너별 라우팅: 각 workspace는 고유 robeing_port 보유
|
|
- 단일 Event URL로 모든 워크스페이스 처리 후 분산
|
|
- Interactivity/Slash Commands 없이도 기본 기능 충분
|
|
|
|
## 5. 참고사항
|
|
- Event URL: `https://auth.ro-being.com/slack/events/router` (api 접두사 없음)
|
|
- DB 스키마: DOCS 문서 업데이트 완료 (tables.md)
|
|
- Python 모델: SlackWorkspace의 workspace_id는 실제로 company_id 사용
|
|
|
|
### 5.1 PostgreSQL 테이블 현황 (2025-09-02)
|
|
**slack_workspaces 테이블:**
|
|
- 2개 워크스페이스 등록 (T035VFRKCN6, T097FCTDVEX)
|
|
- 동일 app_id 사용: A092HLQA1NW
|
|
- bot_user_id 저장됨 (U0988K1BLQY, U097FFP4QQ3)
|
|
- 모두 is_active = true
|
|
|
|
**관련 테이블:**
|
|
- workspaces: robeing_port, subdomain 컬럼 보유
|
|
- workspace_members: workspace_id, user_id, robeing_id 매핑
|
|
- slack_user_mapping: 사용자 매핑
|
|
|
|
### 5.2 Slack 설정 정보
|
|
**환경변수:**
|
|
- SLACK_CLIENT_ID: 9073915808149.9085704341778
|
|
- SLACK_CLIENT_SECRET: 5f8701a3c97a9e0b406817f44f5dbb5f
|
|
- SLACK_REDIRECT_URI: https://auth.ro-being.com/auth/slack/callback
|
|
|
|
**Bot Scopes (11개):**
|
|
chat:write, channels:read, channels:history, groups:read, groups:history,
|
|
im:read, im:history, users:read, team:read, files:read, app_mentions:read
|
|
|
|
### 5.3 주요 문제점 (2025-09-02 해결됨)
|
|
**workspace_id 하드코딩 문제 ✅ 해결:**
|
|
- ~~TODO 주석만 있고 구현 안 됨~~
|
|
- ~~모든 사용자가 동일 workspace_id 사용 (550e8400-e29b-41d4-a716-446655440000)~~
|
|
- ~~workspace_members 조회 코드 주석 처리됨~~
|
|
|
|
**해결 내용:**
|
|
1. ~~WorkspaceMember 조회 로직 활성화 (Line 411-419)~~ → sqlalchemy text() 직접 쿼리로 변경
|
|
2. 하드코딩 UUID 제거 (Line 422) ✅
|
|
3. is_active 체크 추가 ✅
|
|
4. ~~WorkspaceMember import 추가~~ → 모델 없어서 제거, text() 사용
|
|
5. info@company-x.partners가 Company-X workspace (99d22d6b-d327-4fa4-bd2f-d228c11056e2)로 연결됨 ✅
|
|
|
|
**추가 해결 (2025-09-02):**
|
|
- oauth_states 메모리 dict → Redis 전환 완료
|
|
- Line 436-446: `await redis_client.setex(f"oauth:passport:{state}", 300, ...)`
|
|
- Line 490-502: Redis get/delete 사용
|
|
- 서버 재시작 시에도 state 유지됨
|
|
|
|
**WorkspaceMember 모델 이슈:**
|
|
- app/models/workspace.py에 WorkspaceMember 클래스 정의 없음
|
|
- workspace_members 테이블 직접 쿼리로 해결
|
|
- ~~`SELECT workspace_id FROM workspace_members 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` (괄호 필수)
|
|
|
|
### 5.4 추가 이슈 해결 (2025-09-02 저녁)
|
|
|
|
**SLACK_REDIRECT_URI 불일치:**
|
|
- .env: `/auth/slack/callback` → `/auth/slack/passport/callback` 수정
|
|
- Slack App 설정: 3개 URL 모두 등록 (callback, login/callback, passport/callback)
|
|
|
|
**SlackWorkspace 모델 불일치:**
|
|
- 모델 정의: `company_id` (Line 44, ForeignKey는 companies.id)
|
|
- 코드 사용: `workspace_id` 시도 → `company_id`로 수정 필요
|
|
- Line 542, 557, 631, 674, 713: workspace_id → company_id
|
|
- DB 현실: companies 테이블 없음, workspaces 테이블 사용 중
|
|
|
|
**Company-X 설정 상태:**
|
|
- workspace_id: 99d22d6b-d327-4fa4-bd2f-d228c11056e2
|
|
- 총 8명: company-x.partners(6) + sigong-ip.com(2)
|
|
- info@company-x.partners: OWNER role
|
|
- ~~**봇 설치 실패**: Foreign Key 에러~~ → **해결 완료**
|
|
|
|
### 5.5 최종 이슈 (✅ 해결 완료)
|
|
|
|
**Foreign Key 문제 해결:**
|
|
- 모델: `ForeignKey("companies.id")` 참조
|
|
- ~~실제 DB: companies 테이블 없음~~ → **companies 테이블 존재 확인됨**
|
|
- 해결 내용:
|
|
1. companies 테이블에 Company-X 레코드 추가 (ID: 99d22d6b-d327-4fa4-bd2f-d228c11056e2)
|
|
2. 기존 slack_workspaces 레코드의 company_id 업데이트 완료
|
|
3. Foreign Key 제약 조건 정상 작동
|
|
|
|
**최종 DB 상태 (2025-09-02 20:40):**
|
|
- companies 테이블: Company-X (8001 포트, active 상태)
|
|
- workspaces 테이블: Company-X (8명 멤버)
|
|
- slack_workspaces: T097FCTDVEX → Company-X 연결 완료
|
|
- 봇 설치 준비 완료
|
|
|
|
## 6. Company-X 봇 설치 완료 및 토큰 문제 (2025-09-02 23:00)
|
|
|
|
### 6.1 설치 완료 정보
|
|
**Company-X 봇 설치 성공:**
|
|
- Team ID: T097FCTDVEX
|
|
- Team Name: COMPANY X
|
|
- Bot User ID: U09DWLARFQQ
|
|
- App ID: A092HLQA1NW
|
|
- Bot Token: xoxb-9253435471507-9253533160819-2zZNbJaxdJcQjhJAG8t0TQFi
|
|
- 11개 권한 모두 부여됨
|
|
|
|
### 6.2 멀티 워크스페이스 토큰 관리 문제
|
|
|
|
**문제 발견:**
|
|
- rb8001 컨테이너가 단일 SLACK_BOT_TOKEN 환경변수 사용
|
|
- 디지털비잉_로빙 토큰: xoxb-9073915808149-...
|
|
- Company-X 토큰: xoxb-9253435471507-...
|
|
- 현재 rb8001에 디지털비잉_로빙 토큰만 설정되어 있음
|
|
|
|
**증상:**
|
|
- Company-X에서 메시지 수신은 정상
|
|
- 응답 시도 시 실패: `Failed to send final response: The request to the Slack API failed`
|
|
- 원인: 잘못된 토큰으로 응답 시도
|
|
|
|
**실제 에러 로그:**
|
|
```
|
|
ERROR:app.router.slack_handler:Failed to send final response: The request to the Slack API failed. (url: https://www.slack.com/api/chat.postMessage)
|
|
The server responded with: {'ok': False, 'error': 'channel_not_found'}
|
|
```
|
|
|
|
**상세 분석:**
|
|
1. 이벤트 수신 ✅
|
|
- U09BQSN72UT(김종태)가 봇 멘션: `<@U09DWLARFQQ> 하이`
|
|
- app_mention 이벤트 정상 수신
|
|
|
|
2. 처리 과정 ✅
|
|
- UUID 변환 성공 (U09BQSN72UT → 1a7ebe8c-1cfd-4acf-a821-2cc526313706)
|
|
- 응답 생성 성공: "김종태님, 안녕하세요! 로빙입니다. 무엇을 도와드릴까요?"
|
|
- ChromaDB/PostgreSQL 저장 성공
|
|
|
|
3. 응답 전송 실패 ❌
|
|
- `channel_not_found` 에러
|
|
- 디지털비잉_로빙 토큰으로 Company-X 채널 접근 불가
|
|
|
|
### 6.3 Event URL 라우팅 구조
|
|
|
|
**정상 작동 확인:**
|
|
1. Slack → `https://ro-being.com/rb8001/api/slack/events`
|
|
2. nginx 프록시 → `192.168.219.52:8001/api/slack/events`
|
|
3. rb8001 컨테이너가 이벤트 수신 및 처리
|
|
|
|
**문제점:**
|
|
- rb8001이 단일 토큰만 사용 가능한 구조
|
|
- 멀티 워크스페이스 지원을 위해서는 토큰 동적 선택 로직 필요
|
|
|
|
### 6.4 해결 방안
|
|
|
|
**단기 해결:**
|
|
- 워크스페이스별로 별도 컨테이너 운영
|
|
- 또는 team_id 기반으로 토큰 동적 선택 로직 구현
|
|
|
|
**장기 해결:**
|
|
- auth-server의 중앙 라우터 활용
|
|
- `/slack/events/router`가 team_id별로 적절한 컨테이너로 라우팅
|
|
- 각 컨테이너는 해당 워크스페이스 토큰만 보유
|
|
|
|
### 6.5 Slack App Manifest Event URL 설정
|
|
|
|
**올바른 Event URL (Slack App에 설정해야 함):**
|
|
```json
|
|
"event_subscriptions": {
|
|
"request_url": "https://ro-being.com/rb8001/api/slack/events"
|
|
}
|
|
```
|
|
- rb8001이 직접 이벤트 수신
|
|
- URL verification 정상 응답 확인됨
|
|
- nginx 프록시 → 51124:8001 정상 작동
|
|
|
|
**주의사항:**
|
|
- Slack App 설정에서 직접 Event URL 등록 필요
|
|
- 봇 설치와 Event URL 등록은 별개 프로세스
|
|
- team_id 기반 토큰 동적 선택 로직 구현 필요 |