feat: Initialize News Engine Console project
Create comprehensive news pipeline management and monitoring system with backend API structure and detailed implementation roadmap. Core Features (7): 1. Keyword Management - Pipeline keyword CRUD and control 2. Pipeline Monitoring - Processing stats and utilization metrics 3. Pipeline Control - Step-wise start/stop and scheduling 4. Logging System - Pipeline status logs and error tracking 5. User Management - User CRUD with role-based access (Admin/Editor/Viewer) 6. Application Management - OAuth2/JWT-based Application CRUD 7. System Monitoring - Service health checks and resource monitoring Technology Stack: - Backend: FastAPI + Motor (MongoDB async) + Redis - Frontend: React 18 + TypeScript + Material-UI v7 (planned) - Auth: JWT + OAuth2 - Infrastructure: Docker + Kubernetes Project Structure: - backend/app/api/ - 5 API routers (keywords, pipelines, users, applications, monitoring) - backend/app/core/ - Core configurations (config, database, auth) - backend/app/models/ - Data models (planned) - backend/app/services/ - Business logic (planned) - backend/app/schemas/ - Pydantic schemas (planned) - frontend/ - React application (planned) - k8s/ - Kubernetes manifests (planned) Documentation: - README.md - Project overview, current status, API endpoints, DB schema - TODO.md - Detailed implementation plan for next sessions Current Status: ✅ Project structure initialized ✅ Backend basic configuration (config, database, auth) ✅ API router skeletons (5 routers) ✅ Requirements and environment setup 🚧 Models, services, and schemas pending 📋 Frontend implementation pending 📋 Docker and Kubernetes deployment pending Next Steps (See TODO.md): 1. MongoDB schema and indexes 2. Pydantic schemas with validation 3. Service layer implementation 4. Redis integration 5. Login/authentication API 6. Frontend basic setup This provides a solid foundation for building a comprehensive news pipeline management console system. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
289
services/news-engine-console/README.md
Normal file
289
services/news-engine-console/README.md
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
# News Engine Console
|
||||||
|
|
||||||
|
뉴스 파이프라인 관리 및 모니터링 통합 콘솔 시스템
|
||||||
|
|
||||||
|
## 프로젝트 개요
|
||||||
|
|
||||||
|
News Engine Console은 뉴스 파이프라인의 전체 lifecycle을 관리하고 모니터링하는 통합 관리 시스템입니다.
|
||||||
|
|
||||||
|
### 핵심 기능
|
||||||
|
|
||||||
|
1. **키워드 관리** - 파이프라인 키워드 CRUD, 활성화/비활성화
|
||||||
|
2. **파이프라인 모니터링** - 파이프라인별 처리 수량, 활용도 통계
|
||||||
|
3. **파이프라인 제어** - 스텝별 시작/중지, 스케줄링
|
||||||
|
4. **로깅 시스템** - 파이프라인 상태 로그, 에러 추적
|
||||||
|
5. **사용자 관리** - User CRUD, 역할 기반 권한 (Admin/Editor/Viewer)
|
||||||
|
6. **애플리케이션 관리** - OAuth2/JWT 기반 Application CRUD
|
||||||
|
7. **시스템 모니터링** - 서비스 헬스체크, 리소스 사용량
|
||||||
|
|
||||||
|
## 기술 스택
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- FastAPI (Python 3.11)
|
||||||
|
- Motor (MongoDB async driver)
|
||||||
|
- Redis (캐싱, Pub/Sub)
|
||||||
|
- JWT + OAuth2 인증
|
||||||
|
|
||||||
|
### Frontend (예정)
|
||||||
|
- React 18 + TypeScript
|
||||||
|
- Material-UI v7
|
||||||
|
- React Query
|
||||||
|
- Recharts (통계 차트)
|
||||||
|
|
||||||
|
### Infrastructure
|
||||||
|
- Docker
|
||||||
|
- Kubernetes
|
||||||
|
- MongoDB (ai_writer_db)
|
||||||
|
- Redis
|
||||||
|
|
||||||
|
## 프로젝트 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
services/news-engine-console/
|
||||||
|
├── README.md
|
||||||
|
├── TODO.md # 상세 구현 계획
|
||||||
|
├── backend/
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ ├── main.py
|
||||||
|
│ ├── .env.example
|
||||||
|
│ └── app/
|
||||||
|
│ ├── api/ # API 엔드포인트
|
||||||
|
│ │ ├── keywords.py # ✅ 키워드 관리
|
||||||
|
│ │ ├── pipelines.py # ✅ 파이프라인 제어/모니터링
|
||||||
|
│ │ ├── users.py # ✅ 사용자 관리
|
||||||
|
│ │ ├── applications.py # ✅ Application 관리
|
||||||
|
│ │ └── monitoring.py # ✅ 시스템 모니터링
|
||||||
|
│ ├── core/ # 핵심 설정
|
||||||
|
│ │ ├── config.py # ✅ 설정 관리
|
||||||
|
│ │ ├── database.py # ✅ MongoDB 연결
|
||||||
|
│ │ └── auth.py # ✅ JWT/OAuth2 인증
|
||||||
|
│ ├── models/ # 데이터 모델 (TODO)
|
||||||
|
│ ├── services/ # 비즈니스 로직 (TODO)
|
||||||
|
│ └── schemas/ # Pydantic 스키마 (TODO)
|
||||||
|
├── frontend/ # TODO
|
||||||
|
│ └── src/
|
||||||
|
│ ├── api/
|
||||||
|
│ ├── components/
|
||||||
|
│ ├── pages/
|
||||||
|
│ └── types/
|
||||||
|
└── k8s/ # TODO
|
||||||
|
├── namespace.yaml
|
||||||
|
├── backend-deployment.yaml
|
||||||
|
├── frontend-deployment.yaml
|
||||||
|
└── service.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## 현재 구현 상태
|
||||||
|
|
||||||
|
### ✅ 완료
|
||||||
|
- [x] 프로젝트 디렉토리 구조
|
||||||
|
- [x] Backend 기본 설정 (config, database, auth)
|
||||||
|
- [x] API 라우터 기본 구조 (5개 라우터)
|
||||||
|
- Keywords API
|
||||||
|
- Pipelines API
|
||||||
|
- Users API
|
||||||
|
- Applications API
|
||||||
|
- Monitoring API
|
||||||
|
|
||||||
|
### 🚧 진행 중
|
||||||
|
- [ ] Backend 상세 구현 (models, services, schemas)
|
||||||
|
- [ ] MongoDB 컬렉션 및 인덱스 설계
|
||||||
|
- [ ] Redis 연결 및 캐싱 로직
|
||||||
|
|
||||||
|
### 📋 예정
|
||||||
|
- [ ] Frontend 구현
|
||||||
|
- [ ] Dockerfile 작성
|
||||||
|
- [ ] Kubernetes 배포 설정
|
||||||
|
- [ ] CI/CD 파이프라인
|
||||||
|
- [ ] API 문서 (OpenAPI/Swagger)
|
||||||
|
|
||||||
|
## API 엔드포인트
|
||||||
|
|
||||||
|
### Keywords API (`/api/v1/keywords`)
|
||||||
|
- `GET /` - 키워드 목록 조회
|
||||||
|
- `POST /` - 키워드 생성
|
||||||
|
- `PUT /{keyword_id}` - 키워드 수정
|
||||||
|
- `DELETE /{keyword_id}` - 키워드 삭제
|
||||||
|
|
||||||
|
### Pipelines API (`/api/v1/pipelines`)
|
||||||
|
- `GET /` - 파이프라인 목록 및 상태
|
||||||
|
- `GET /{pipeline_id}/stats` - 파이프라인 통계
|
||||||
|
- `POST /{pipeline_id}/start` - 파이프라인 시작
|
||||||
|
- `POST /{pipeline_id}/stop` - 파이프라인 중지
|
||||||
|
|
||||||
|
### Users API (`/api/v1/users`)
|
||||||
|
- `GET /` - 사용자 목록
|
||||||
|
- `POST /` - 사용자 생성
|
||||||
|
- `GET /me` - 현재 사용자 정보
|
||||||
|
|
||||||
|
### Applications API (`/api/v1/applications`)
|
||||||
|
- `GET /` - Application 목록
|
||||||
|
- `POST /` - Application 생성 (OAuth2 클라이언트 등록)
|
||||||
|
|
||||||
|
### Monitoring API (`/api/v1/monitoring`)
|
||||||
|
- `GET /system` - 시스템 상태
|
||||||
|
- `GET /logs` - 파이프라인 로그
|
||||||
|
|
||||||
|
## 로컬 개발 환경 설정
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Python 3.11+
|
||||||
|
- MongoDB (localhost:27017)
|
||||||
|
- Redis (localhost:6379)
|
||||||
|
|
||||||
|
### Backend 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd services/news-engine-console/backend
|
||||||
|
|
||||||
|
# 가상환경 생성 (선택)
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# 의존성 설치
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 환경 변수 설정
|
||||||
|
cp .env.example .env
|
||||||
|
# .env 파일 수정
|
||||||
|
|
||||||
|
# 서버 실행
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
서버 실행 후: http://localhost:8100/docs (Swagger UI)
|
||||||
|
|
||||||
|
## 환경 변수
|
||||||
|
|
||||||
|
```env
|
||||||
|
# MongoDB
|
||||||
|
MONGODB_URL=mongodb://localhost:27017
|
||||||
|
DB_NAME=ai_writer_db
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
SECRET_KEY=your-secret-key-here
|
||||||
|
ALGORITHM=HS256
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||||
|
|
||||||
|
# Service
|
||||||
|
SERVICE_NAME=news-engine-console
|
||||||
|
API_V1_STR=/api/v1
|
||||||
|
PORT=8100
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3100
|
||||||
|
```
|
||||||
|
|
||||||
|
## 다음 단계 (TODO.md 참조)
|
||||||
|
|
||||||
|
### Phase 1: Backend 완성 (우선순위 높음)
|
||||||
|
1. MongoDB 스키마 설계
|
||||||
|
- keywords 컬렉션
|
||||||
|
- pipelines 컬렉션
|
||||||
|
- users 컬렉션
|
||||||
|
- applications 컬렉션
|
||||||
|
- logs 컬렉션
|
||||||
|
|
||||||
|
2. Pydantic 모델 및 스키마 작성
|
||||||
|
- Request/Response 모델
|
||||||
|
- 유효성 검증
|
||||||
|
|
||||||
|
3. 비즈니스 로직 구현
|
||||||
|
- KeywordService
|
||||||
|
- PipelineService
|
||||||
|
- UserService
|
||||||
|
- ApplicationService
|
||||||
|
- MonitoringService
|
||||||
|
|
||||||
|
4. Redis 통합
|
||||||
|
- 캐싱 레이어
|
||||||
|
- Pub/Sub for real-time updates
|
||||||
|
|
||||||
|
### Phase 2: Frontend 구현
|
||||||
|
1. React 프로젝트 설정
|
||||||
|
2. Material-UI 레이아웃
|
||||||
|
3. 페이지 구현
|
||||||
|
- Dashboard (통계 요약)
|
||||||
|
- Keywords Management
|
||||||
|
- Pipelines Control
|
||||||
|
- Users Management
|
||||||
|
- Applications Management
|
||||||
|
- System Monitoring
|
||||||
|
|
||||||
|
### Phase 3: 배포
|
||||||
|
1. Dockerfile 작성
|
||||||
|
2. Kubernetes 매니페스트
|
||||||
|
3. CI/CD 설정
|
||||||
|
|
||||||
|
## 데이터베이스 설계 (Draft)
|
||||||
|
|
||||||
|
### keywords 컬렉션
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_id": "ObjectId",
|
||||||
|
"keyword": "string",
|
||||||
|
"category": "string",
|
||||||
|
"status": "active|inactive",
|
||||||
|
"created_at": "datetime",
|
||||||
|
"updated_at": "datetime",
|
||||||
|
"created_by": "user_id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### pipelines 컬렉션
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_id": "ObjectId",
|
||||||
|
"name": "string",
|
||||||
|
"type": "rss|translation|image",
|
||||||
|
"status": "running|stopped|error",
|
||||||
|
"config": {},
|
||||||
|
"stats": {
|
||||||
|
"total_processed": 0,
|
||||||
|
"success_count": 0,
|
||||||
|
"error_count": 0,
|
||||||
|
"last_run": "datetime"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### users 컬렉션
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_id": "ObjectId",
|
||||||
|
"username": "string",
|
||||||
|
"email": "string",
|
||||||
|
"hashed_password": "string",
|
||||||
|
"full_name": "string",
|
||||||
|
"role": "admin|editor|viewer",
|
||||||
|
"disabled": false,
|
||||||
|
"created_at": "datetime"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 역할 기반 권한
|
||||||
|
|
||||||
|
- **Admin**: 모든 기능 접근
|
||||||
|
- **Editor**: 키워드/파이프라인 관리, 모니터링 조회
|
||||||
|
- **Viewer**: 조회만 가능
|
||||||
|
|
||||||
|
## 기여 가이드
|
||||||
|
|
||||||
|
1. 기능 구현 전 TODO.md 확인
|
||||||
|
2. API 엔드포인트 추가 시 문서 업데이트
|
||||||
|
3. 테스트 코드 작성
|
||||||
|
4. Commit 메시지 규칙 준수
|
||||||
|
|
||||||
|
## 라이선스
|
||||||
|
|
||||||
|
Part of Site11 Platform - Internal Use
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**최종 업데이트**: 2024-01-15
|
||||||
|
**버전**: 0.1.0 (Alpha)
|
||||||
|
**작성자**: Site11 Development Team
|
||||||
475
services/news-engine-console/TODO.md
Normal file
475
services/news-engine-console/TODO.md
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
# News Engine Console - 구현 계획
|
||||||
|
|
||||||
|
다음 세션을 위한 상세 구현 계획
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Phase 1: Backend 완성 (우선순위)
|
||||||
|
|
||||||
|
### 1.1 데이터 모델 구현
|
||||||
|
|
||||||
|
**models/keyword.py**
|
||||||
|
```python
|
||||||
|
class Keyword:
|
||||||
|
_id: ObjectId
|
||||||
|
keyword: str
|
||||||
|
category: str # 'people', 'topics', 'companies'
|
||||||
|
status: str # 'active', 'inactive'
|
||||||
|
pipeline_type: str # 'rss', 'translation', 'all'
|
||||||
|
priority: int # 1-10
|
||||||
|
metadata: dict
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
created_by: str
|
||||||
|
```
|
||||||
|
|
||||||
|
**models/pipeline.py**
|
||||||
|
```python
|
||||||
|
class Pipeline:
|
||||||
|
_id: ObjectId
|
||||||
|
name: str
|
||||||
|
type: str # 'rss_collector', 'translator', 'image_generator'
|
||||||
|
status: str # 'running', 'stopped', 'error'
|
||||||
|
config: dict
|
||||||
|
schedule: str # cron expression
|
||||||
|
stats: PipelineStats
|
||||||
|
last_run: datetime
|
||||||
|
next_run: datetime
|
||||||
|
```
|
||||||
|
|
||||||
|
**models/user.py**
|
||||||
|
```python
|
||||||
|
class User:
|
||||||
|
_id: ObjectId
|
||||||
|
username: str (unique)
|
||||||
|
email: str (unique)
|
||||||
|
hashed_password: str
|
||||||
|
full_name: str
|
||||||
|
role: str # 'admin', 'editor', 'viewer'
|
||||||
|
disabled: bool
|
||||||
|
created_at: datetime
|
||||||
|
last_login: datetime
|
||||||
|
```
|
||||||
|
|
||||||
|
**models/application.py**
|
||||||
|
```python
|
||||||
|
class Application:
|
||||||
|
_id: ObjectId
|
||||||
|
name: str
|
||||||
|
client_id: str (unique)
|
||||||
|
client_secret: str (hashed)
|
||||||
|
redirect_uris: List[str]
|
||||||
|
grant_types: List[str]
|
||||||
|
scopes: List[str]
|
||||||
|
owner_id: str
|
||||||
|
created_at: datetime
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Pydantic 스키마 작성
|
||||||
|
|
||||||
|
**schemas/keyword.py**
|
||||||
|
- KeywordCreate
|
||||||
|
- KeywordUpdate
|
||||||
|
- KeywordResponse
|
||||||
|
- KeywordList
|
||||||
|
|
||||||
|
**schemas/pipeline.py**
|
||||||
|
- PipelineCreate
|
||||||
|
- PipelineUpdate
|
||||||
|
- PipelineResponse
|
||||||
|
- PipelineStats
|
||||||
|
- PipelineList
|
||||||
|
|
||||||
|
**schemas/user.py**
|
||||||
|
- UserCreate
|
||||||
|
- UserUpdate
|
||||||
|
- UserResponse
|
||||||
|
- UserLogin
|
||||||
|
|
||||||
|
**schemas/application.py**
|
||||||
|
- ApplicationCreate
|
||||||
|
- ApplicationUpdate
|
||||||
|
- ApplicationResponse
|
||||||
|
|
||||||
|
### 1.3 서비스 레이어 구현
|
||||||
|
|
||||||
|
**services/keyword_service.py**
|
||||||
|
- `async def get_keywords(filters, pagination)`
|
||||||
|
- `async def create_keyword(keyword_data)`
|
||||||
|
- `async def update_keyword(keyword_id, update_data)`
|
||||||
|
- `async def delete_keyword(keyword_id)`
|
||||||
|
- `async def toggle_keyword_status(keyword_id)`
|
||||||
|
- `async def get_keyword_stats(keyword_id)`
|
||||||
|
|
||||||
|
**services/pipeline_service.py**
|
||||||
|
- `async def get_pipelines()`
|
||||||
|
- `async def get_pipeline_stats(pipeline_id)`
|
||||||
|
- `async def start_pipeline(pipeline_id)`
|
||||||
|
- `async def stop_pipeline(pipeline_id)`
|
||||||
|
- `async def restart_pipeline(pipeline_id)`
|
||||||
|
- `async def get_pipeline_logs(pipeline_id, limit)`
|
||||||
|
- `async def update_pipeline_config(pipeline_id, config)`
|
||||||
|
|
||||||
|
**services/user_service.py**
|
||||||
|
- `async def create_user(user_data)`
|
||||||
|
- `async def authenticate_user(username, password)`
|
||||||
|
- `async def get_user_by_username(username)`
|
||||||
|
- `async def update_user(user_id, update_data)`
|
||||||
|
- `async def delete_user(user_id)`
|
||||||
|
|
||||||
|
**services/application_service.py**
|
||||||
|
- `async def create_application(app_data)`
|
||||||
|
- `async def get_applications(user_id)`
|
||||||
|
- `async def regenerate_client_secret(app_id)`
|
||||||
|
- `async def delete_application(app_id)`
|
||||||
|
|
||||||
|
**services/monitoring_service.py**
|
||||||
|
- `async def get_system_health()`
|
||||||
|
- `async def get_service_status()`
|
||||||
|
- `async def get_database_stats()`
|
||||||
|
- `async def get_redis_stats()`
|
||||||
|
- `async def get_recent_logs(limit)`
|
||||||
|
|
||||||
|
### 1.4 Redis 통합
|
||||||
|
|
||||||
|
**core/redis_client.py**
|
||||||
|
```python
|
||||||
|
class RedisClient:
|
||||||
|
async def get(key)
|
||||||
|
async def set(key, value, expire)
|
||||||
|
async def delete(key)
|
||||||
|
async def publish(channel, message)
|
||||||
|
async def subscribe(channel, callback)
|
||||||
|
```
|
||||||
|
|
||||||
|
**사용 케이스**:
|
||||||
|
- 파이프라인 상태 캐싱
|
||||||
|
- 실시간 통계 업데이트 (Pub/Sub)
|
||||||
|
- 사용자 세션 관리
|
||||||
|
- Rate limiting
|
||||||
|
|
||||||
|
### 1.5 API 엔드포인트 완성
|
||||||
|
|
||||||
|
**keywords.py**
|
||||||
|
- [x] GET / - 목록 조회 (기본 구조)
|
||||||
|
- [ ] 필터링 (category, status, search)
|
||||||
|
- [ ] 페이지네이션
|
||||||
|
- [ ] 정렬 (created_at, priority)
|
||||||
|
- [ ] GET /{id}/stats - 키워드 통계
|
||||||
|
- [ ] POST /{id}/toggle - 활성화/비활성화
|
||||||
|
|
||||||
|
**pipelines.py**
|
||||||
|
- [x] GET / - 목록 조회 (기본 구조)
|
||||||
|
- [ ] GET /{id}/logs - 로그 조회
|
||||||
|
- [ ] POST /{id}/restart - 재시작
|
||||||
|
- [ ] PUT /{id}/config - 설정 업데이트
|
||||||
|
- [ ] GET /types - 파이프라인 타입 목록
|
||||||
|
|
||||||
|
**users.py**
|
||||||
|
- [x] GET / - 목록 조회 (기본 구조)
|
||||||
|
- [ ] PUT /{id} - 사용자 수정
|
||||||
|
- [ ] DELETE /{id} - 사용자 삭제
|
||||||
|
- [ ] POST /login - 로그인 (JWT 발급)
|
||||||
|
- [ ] POST /register - 회원가입
|
||||||
|
|
||||||
|
**applications.py**
|
||||||
|
- [x] GET / - 목록 조회 (기본 구조)
|
||||||
|
- [ ] GET /{id} - 상세 조회
|
||||||
|
- [ ] PUT /{id} - 수정
|
||||||
|
- [ ] DELETE /{id} - 삭제
|
||||||
|
- [ ] POST /{id}/regenerate-secret - 시크릿 재생성
|
||||||
|
|
||||||
|
**monitoring.py**
|
||||||
|
- [x] GET /system - 시스템 상태 (기본 구조)
|
||||||
|
- [ ] GET /services - 서비스별 상태
|
||||||
|
- [ ] GET /database - DB 통계
|
||||||
|
- [ ] GET /redis - Redis 상태
|
||||||
|
- [ ] GET /pipelines/activity - 파이프라인 활동 로그
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Phase 2: Frontend 구현
|
||||||
|
|
||||||
|
### 2.1 프로젝트 설정
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm create vite@latest . -- --template react-ts
|
||||||
|
npm install @mui/material @emotion/react @emotion/styled
|
||||||
|
npm install @tanstack/react-query axios react-router-dom
|
||||||
|
npm install recharts date-fns
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 레이아웃 구현
|
||||||
|
|
||||||
|
**components/Layout/AppLayout.tsx**
|
||||||
|
- Sidebar with navigation
|
||||||
|
- Top bar with user info
|
||||||
|
- Main content area
|
||||||
|
|
||||||
|
**components/Layout/Sidebar.tsx**
|
||||||
|
- Dashboard
|
||||||
|
- Keywords
|
||||||
|
- Pipelines
|
||||||
|
- Users
|
||||||
|
- Applications
|
||||||
|
- Monitoring
|
||||||
|
|
||||||
|
### 2.3 페이지 구현
|
||||||
|
|
||||||
|
**pages/Dashboard.tsx**
|
||||||
|
- 전체 통계 요약
|
||||||
|
- 파이프라인 상태 차트
|
||||||
|
- 최근 활동 로그
|
||||||
|
- 키워드 활용도 TOP 10
|
||||||
|
|
||||||
|
**pages/Keywords.tsx**
|
||||||
|
- 키워드 목록 테이블
|
||||||
|
- 검색, 필터, 정렬
|
||||||
|
- 추가/수정/삭제 모달
|
||||||
|
- 활성화/비활성화 토글
|
||||||
|
- 키워드별 통계 차트
|
||||||
|
|
||||||
|
**pages/Pipelines.tsx**
|
||||||
|
- 파이프라인 카드 그리드
|
||||||
|
- 상태별 필터 (Running, Stopped, Error)
|
||||||
|
- 시작/중지 버튼
|
||||||
|
- 실시간 로그 스트림
|
||||||
|
- 통계 차트
|
||||||
|
|
||||||
|
**pages/Users.tsx**
|
||||||
|
- 사용자 목록 테이블
|
||||||
|
- 역할 필터 (Admin, Editor, Viewer)
|
||||||
|
- 추가/수정/삭제 모달
|
||||||
|
- 마지막 로그인 시간
|
||||||
|
|
||||||
|
**pages/Applications.tsx**
|
||||||
|
- Application 카드 그리드
|
||||||
|
- Client ID/Secret 표시
|
||||||
|
- 생성/수정/삭제
|
||||||
|
- Secret 재생성 기능
|
||||||
|
|
||||||
|
**pages/Monitoring.tsx**
|
||||||
|
- 시스템 헬스체크 대시보드
|
||||||
|
- 서비스별 상태 (MongoDB, Redis, etc.)
|
||||||
|
- CPU/메모리 사용량 차트
|
||||||
|
- 실시간 로그 스트림
|
||||||
|
|
||||||
|
### 2.4 API 클라이언트
|
||||||
|
|
||||||
|
**api/client.ts**
|
||||||
|
```typescript
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const apiClient = axios.create({
|
||||||
|
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8100/api/v1',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Interceptors for auth token
|
||||||
|
apiClient.interceptors.request.use(config => {
|
||||||
|
const token = localStorage.getItem('access_token');
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default apiClient;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 TypeScript 타입 정의
|
||||||
|
|
||||||
|
**types/index.ts**
|
||||||
|
- Keyword
|
||||||
|
- Pipeline
|
||||||
|
- User
|
||||||
|
- Application
|
||||||
|
- PipelineStats
|
||||||
|
- SystemStatus
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Phase 3: Docker & Kubernetes
|
||||||
|
|
||||||
|
### 3.1 Backend Dockerfile
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 8100
|
||||||
|
|
||||||
|
CMD ["python", "main.py"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Frontend Dockerfile
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
EXPOSE 80
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Kubernetes 매니페스트
|
||||||
|
|
||||||
|
**k8s/namespace.yaml**
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: site11-console
|
||||||
|
```
|
||||||
|
|
||||||
|
**k8s/backend-deployment.yaml**
|
||||||
|
- Deployment with 2 replicas
|
||||||
|
- ConfigMap for env vars
|
||||||
|
- Secret for sensitive data
|
||||||
|
- Service (ClusterIP)
|
||||||
|
|
||||||
|
**k8s/frontend-deployment.yaml**
|
||||||
|
- Deployment with 2 replicas
|
||||||
|
- Service (LoadBalancer or Ingress)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Phase 4: 고급 기능
|
||||||
|
|
||||||
|
### 4.1 실시간 업데이트
|
||||||
|
|
||||||
|
- WebSocket 연결 (파이프라인 상태)
|
||||||
|
- Server-Sent Events (로그 스트림)
|
||||||
|
- Redis Pub/Sub 활용
|
||||||
|
|
||||||
|
### 4.2 알림 시스템
|
||||||
|
|
||||||
|
- 파이프라인 에러 시 알림
|
||||||
|
- 키워드 처리 완료 알림
|
||||||
|
- 이메일/Slack 통합
|
||||||
|
|
||||||
|
### 4.3 스케줄링
|
||||||
|
|
||||||
|
- Cron 기반 파이프라인 스케줄
|
||||||
|
- 수동 실행 vs 자동 실행
|
||||||
|
- 스케줄 히스토리
|
||||||
|
|
||||||
|
### 4.4 통계 & 분석
|
||||||
|
|
||||||
|
- 일/주/월별 처리 통계
|
||||||
|
- 키워드별 성과 분석
|
||||||
|
- 파이프라인 성능 메트릭
|
||||||
|
- CSV 다운로드
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Phase 5: 테스트 & 문서화
|
||||||
|
|
||||||
|
### 5.1 Backend 테스트
|
||||||
|
|
||||||
|
- pytest fixtures
|
||||||
|
- API endpoint tests
|
||||||
|
- Integration tests
|
||||||
|
- Coverage report
|
||||||
|
|
||||||
|
### 5.2 Frontend 테스트
|
||||||
|
|
||||||
|
- React Testing Library
|
||||||
|
- Component tests
|
||||||
|
- E2E tests (Playwright)
|
||||||
|
|
||||||
|
### 5.3 API 문서
|
||||||
|
|
||||||
|
- OpenAPI/Swagger 자동 생성
|
||||||
|
- API 예시 코드
|
||||||
|
- 에러 응답 명세
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 우선순위
|
||||||
|
|
||||||
|
### 즉시 시작 (다음 세션)
|
||||||
|
|
||||||
|
1. **MongoDB 스키마 및 인덱스 생성**
|
||||||
|
- keywords, pipelines, users 컬렉션
|
||||||
|
- 인덱스 설계
|
||||||
|
|
||||||
|
2. **Pydantic 스키마 작성**
|
||||||
|
- Request/Response 모델
|
||||||
|
- 유효성 검증
|
||||||
|
|
||||||
|
3. **키워드 관리 기능 완성**
|
||||||
|
- KeywordService 구현
|
||||||
|
- CRUD API 완성
|
||||||
|
- 단위 테스트
|
||||||
|
|
||||||
|
4. **로그인 API 구현**
|
||||||
|
- JWT 토큰 발급
|
||||||
|
- User 인증
|
||||||
|
|
||||||
|
### 중기 목표 (1-2주)
|
||||||
|
|
||||||
|
1. 파이프라인 제어 API 완성
|
||||||
|
2. Frontend 기본 구조
|
||||||
|
3. Dashboard 페이지
|
||||||
|
4. Dockerfile 작성
|
||||||
|
|
||||||
|
### 장기 목표 (1개월)
|
||||||
|
|
||||||
|
1. Frontend 전체 페이지
|
||||||
|
2. Kubernetes 배포
|
||||||
|
3. 실시간 모니터링
|
||||||
|
4. 알림 시스템
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 체크리스트
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- [x] 프로젝트 구조
|
||||||
|
- [x] 기본 설정 (config, database, auth)
|
||||||
|
- [x] API 라우터 기본 구조
|
||||||
|
- [ ] Pydantic 스키마
|
||||||
|
- [ ] MongoDB 컬렉션 및 인덱스
|
||||||
|
- [ ] 서비스 레이어 구현
|
||||||
|
- [ ] Redis 통합
|
||||||
|
- [ ] 로그인/인증 API
|
||||||
|
- [ ] 에러 핸들링
|
||||||
|
- [ ] 로깅 시스템
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- [ ] 프로젝트 설정
|
||||||
|
- [ ] 레이아웃 및 라우팅
|
||||||
|
- [ ] 로그인 페이지
|
||||||
|
- [ ] Dashboard
|
||||||
|
- [ ] Keywords 페이지
|
||||||
|
- [ ] Pipelines 페이지
|
||||||
|
- [ ] Users 페이지
|
||||||
|
- [ ] Applications 페이지
|
||||||
|
- [ ] Monitoring 페이지
|
||||||
|
|
||||||
|
### DevOps
|
||||||
|
- [ ] Backend Dockerfile
|
||||||
|
- [ ] Frontend Dockerfile
|
||||||
|
- [ ] docker-compose.yml
|
||||||
|
- [ ] Kubernetes 매니페스트
|
||||||
|
- [ ] CI/CD 설정
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**다음 세션 시작 시**: 이 TODO.md를 확인하고 체크리스트 업데이트
|
||||||
19
services/news-engine-console/backend/.env.example
Normal file
19
services/news-engine-console/backend/.env.example
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# MongoDB
|
||||||
|
MONGODB_URL=mongodb://localhost:27017
|
||||||
|
DB_NAME=ai_writer_db
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
SECRET_KEY=your-secret-key-here-change-in-production
|
||||||
|
ALGORITHM=HS256
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||||
|
|
||||||
|
# Service
|
||||||
|
SERVICE_NAME=news-engine-console
|
||||||
|
API_V1_STR=/api/v1
|
||||||
|
PORT=8100
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3100
|
||||||
1
services/news-engine-console/backend/app/__init__.py
Normal file
1
services/news-engine-console/backend/app/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# News Engine Console Backend
|
||||||
1
services/news-engine-console/backend/app/api/__init__.py
Normal file
1
services/news-engine-console/backend/app/api/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# API Routers
|
||||||
14
services/news-engine-console/backend/app/api/applications.py
Normal file
14
services/news-engine-console/backend/app/api/applications.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from app.core.auth import get_current_active_user, User
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_applications(current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get all OAuth2 applications"""
|
||||||
|
return {"applications": [], "total": 0}
|
||||||
|
|
||||||
|
@router.post("/")
|
||||||
|
async def create_application(app_data: dict, current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Create new OAuth2 application"""
|
||||||
|
return {"message": "Application created"}
|
||||||
39
services/news-engine-console/backend/app/api/keywords.py
Normal file
39
services/news-engine-console/backend/app/api/keywords.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from typing import List
|
||||||
|
from app.core.auth import get_current_active_user, User
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_keywords(current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get all keywords"""
|
||||||
|
# TODO: Implement keyword retrieval from MongoDB
|
||||||
|
return {"keywords": [], "total": 0}
|
||||||
|
|
||||||
|
@router.post("/")
|
||||||
|
async def create_keyword(
|
||||||
|
keyword_data: dict,
|
||||||
|
current_user: User = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Create new keyword"""
|
||||||
|
# TODO: Implement keyword creation
|
||||||
|
return {"message": "Keyword created", "keyword": keyword_data}
|
||||||
|
|
||||||
|
@router.put("/{keyword_id}")
|
||||||
|
async def update_keyword(
|
||||||
|
keyword_id: str,
|
||||||
|
keyword_data: dict,
|
||||||
|
current_user: User = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Update keyword"""
|
||||||
|
# TODO: Implement keyword update
|
||||||
|
return {"message": "Keyword updated", "keyword_id": keyword_id}
|
||||||
|
|
||||||
|
@router.delete("/{keyword_id}")
|
||||||
|
async def delete_keyword(
|
||||||
|
keyword_id: str,
|
||||||
|
current_user: User = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Delete keyword"""
|
||||||
|
# TODO: Implement keyword deletion
|
||||||
|
return {"message": "Keyword deleted", "keyword_id": keyword_id}
|
||||||
14
services/news-engine-console/backend/app/api/monitoring.py
Normal file
14
services/news-engine-console/backend/app/api/monitoring.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from app.core.auth import get_current_active_user, User
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/system")
|
||||||
|
async def get_system_status(current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get system status"""
|
||||||
|
return {"status": "healthy", "services": []}
|
||||||
|
|
||||||
|
@router.get("/logs")
|
||||||
|
async def get_logs(current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get pipeline logs"""
|
||||||
|
return {"logs": []}
|
||||||
24
services/news-engine-console/backend/app/api/pipelines.py
Normal file
24
services/news-engine-console/backend/app/api/pipelines.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from app.core.auth import get_current_active_user, User
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_pipelines(current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get all pipelines and their status"""
|
||||||
|
return {"pipelines": [], "total": 0}
|
||||||
|
|
||||||
|
@router.get("/{pipeline_id}/stats")
|
||||||
|
async def get_pipeline_stats(pipeline_id: str, current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get pipeline statistics"""
|
||||||
|
return {"pipeline_id": pipeline_id, "stats": {}}
|
||||||
|
|
||||||
|
@router.post("/{pipeline_id}/start")
|
||||||
|
async def start_pipeline(pipeline_id: str, current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Start pipeline"""
|
||||||
|
return {"message": "Pipeline started", "pipeline_id": pipeline_id}
|
||||||
|
|
||||||
|
@router.post("/{pipeline_id}/stop")
|
||||||
|
async def stop_pipeline(pipeline_id: str, current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Stop pipeline"""
|
||||||
|
return {"message": "Pipeline stopped", "pipeline_id": pipeline_id}
|
||||||
19
services/news-engine-console/backend/app/api/users.py
Normal file
19
services/news-engine-console/backend/app/api/users.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from app.core.auth import get_current_active_user, User
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_users(current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get all users"""
|
||||||
|
return {"users": [], "total": 0}
|
||||||
|
|
||||||
|
@router.post("/")
|
||||||
|
async def create_user(user_data: dict, current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Create new user"""
|
||||||
|
return {"message": "User created"}
|
||||||
|
|
||||||
|
@router.get("/me")
|
||||||
|
async def get_current_user_info(current_user: User = Depends(get_current_active_user)):
|
||||||
|
"""Get current user info"""
|
||||||
|
return current_user
|
||||||
75
services/news-engine-console/backend/app/core/auth.py
Normal file
75
services/news-engine-console/backend/app/core/auth.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Optional
|
||||||
|
from jose import JWTError, jwt
|
||||||
|
from passlib.context import CryptContext
|
||||||
|
from fastapi import Depends, HTTPException, status
|
||||||
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
|
# Password hashing
|
||||||
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/auth/login")
|
||||||
|
|
||||||
|
# Models
|
||||||
|
class Token(BaseModel):
|
||||||
|
access_token: str
|
||||||
|
token_type: str
|
||||||
|
|
||||||
|
class TokenData(BaseModel):
|
||||||
|
username: Optional[str] = None
|
||||||
|
|
||||||
|
class User(BaseModel):
|
||||||
|
username: str
|
||||||
|
email: Optional[str] = None
|
||||||
|
full_name: Optional[str] = None
|
||||||
|
disabled: Optional[bool] = None
|
||||||
|
role: str = "viewer" # admin, editor, viewer
|
||||||
|
|
||||||
|
class UserInDB(User):
|
||||||
|
hashed_password: str
|
||||||
|
|
||||||
|
# Password functions
|
||||||
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||||
|
return pwd_context.verify(plain_password, hashed_password)
|
||||||
|
|
||||||
|
def get_password_hash(password: str) -> str:
|
||||||
|
return pwd_context.hash(password)
|
||||||
|
|
||||||
|
# JWT functions
|
||||||
|
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
||||||
|
to_encode = data.copy()
|
||||||
|
if expires_delta:
|
||||||
|
expire = datetime.utcnow() + expires_delta
|
||||||
|
else:
|
||||||
|
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
|
||||||
|
to_encode.update({"exp": expire})
|
||||||
|
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||||
|
return encoded_jwt
|
||||||
|
|
||||||
|
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||||
|
credentials_exception = HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Could not validate credentials",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||||
|
username: str = payload.get("sub")
|
||||||
|
if username is None:
|
||||||
|
raise credentials_exception
|
||||||
|
token_data = TokenData(username=username)
|
||||||
|
except JWTError:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
|
# TODO: Get user from database
|
||||||
|
user = User(username=token_data.username, role="admin")
|
||||||
|
if user is None:
|
||||||
|
raise credentials_exception
|
||||||
|
return user
|
||||||
|
|
||||||
|
async def get_current_active_user(current_user: User = Depends(get_current_user)):
|
||||||
|
if current_user.disabled:
|
||||||
|
raise HTTPException(status_code=400, detail="Inactive user")
|
||||||
|
return current_user
|
||||||
32
services/news-engine-console/backend/app/core/config.py
Normal file
32
services/news-engine-console/backend/app/core/config.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from pydantic_settings import BaseSettings
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
# MongoDB
|
||||||
|
MONGODB_URL: str = "mongodb://localhost:27017"
|
||||||
|
DB_NAME: str = "ai_writer_db"
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL: str = "redis://localhost:6379"
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
SECRET_KEY: str = "dev-secret-key-change-in-production"
|
||||||
|
ALGORITHM: str = "HS256"
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||||
|
|
||||||
|
# Service
|
||||||
|
SERVICE_NAME: str = "news-engine-console"
|
||||||
|
API_V1_STR: str = "/api/v1"
|
||||||
|
PORT: int = 8100
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
ALLOWED_ORIGINS: List[str] = [
|
||||||
|
"http://localhost:3000",
|
||||||
|
"http://localhost:3100"
|
||||||
|
]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
env_file = ".env"
|
||||||
|
case_sensitive = True
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
24
services/news-engine-console/backend/app/core/database.py
Normal file
24
services/news-engine-console/backend/app/core/database.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from motor.motor_asyncio import AsyncIOMotorClient
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
|
class Database:
|
||||||
|
client: AsyncIOMotorClient = None
|
||||||
|
db = None
|
||||||
|
|
||||||
|
db_instance = Database()
|
||||||
|
|
||||||
|
async def connect_to_mongo():
|
||||||
|
"""Connect to MongoDB"""
|
||||||
|
db_instance.client = AsyncIOMotorClient(settings.MONGODB_URL)
|
||||||
|
db_instance.db = db_instance.client[settings.DB_NAME]
|
||||||
|
print(f"Connected to MongoDB: {settings.DB_NAME}")
|
||||||
|
|
||||||
|
async def close_mongo_connection():
|
||||||
|
"""Close MongoDB connection"""
|
||||||
|
if db_instance.client:
|
||||||
|
db_instance.client.close()
|
||||||
|
print("Closed MongoDB connection")
|
||||||
|
|
||||||
|
def get_database():
|
||||||
|
"""Get database instance"""
|
||||||
|
return db_instance.db
|
||||||
52
services/news-engine-console/backend/main.py
Normal file
52
services/news-engine-console/backend/main.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
from app.core.config import settings
|
||||||
|
from app.core.database import connect_to_mongo, close_mongo_connection
|
||||||
|
from app.api import keywords, pipelines, users, applications, monitoring
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
# Startup
|
||||||
|
await connect_to_mongo()
|
||||||
|
yield
|
||||||
|
# Shutdown
|
||||||
|
await close_mongo_connection()
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="News Engine Console API",
|
||||||
|
description="뉴스 파이프라인 관리 및 모니터링 시스템",
|
||||||
|
version="1.0.0",
|
||||||
|
lifespan=lifespan
|
||||||
|
)
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=settings.ALLOWED_ORIGINS,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
@app.get("/health")
|
||||||
|
async def health_check():
|
||||||
|
return {"status": "healthy", "service": settings.SERVICE_NAME}
|
||||||
|
|
||||||
|
# Include routers
|
||||||
|
app.include_router(keywords.router, prefix=f"{settings.API_V1_STR}/keywords", tags=["Keywords"])
|
||||||
|
app.include_router(pipelines.router, prefix=f"{settings.API_V1_STR}/pipelines", tags=["Pipelines"])
|
||||||
|
app.include_router(users.router, prefix=f"{settings.API_V1_STR}/users", tags=["Users"])
|
||||||
|
app.include_router(applications.router, prefix=f"{settings.API_V1_STR}/applications", tags=["Applications"])
|
||||||
|
app.include_router(monitoring.router, prefix=f"{settings.API_V1_STR}/monitoring", tags=["Monitoring"])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
uvicorn.run(
|
||||||
|
"main:app",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=settings.PORT,
|
||||||
|
reload=True
|
||||||
|
)
|
||||||
11
services/news-engine-console/backend/requirements.txt
Normal file
11
services/news-engine-console/backend/requirements.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fastapi==0.104.1
|
||||||
|
uvicorn[standard]==0.24.0
|
||||||
|
motor==3.3.2
|
||||||
|
pydantic==2.5.0
|
||||||
|
pydantic-settings==2.1.0
|
||||||
|
python-jose[cryptography]==3.3.0
|
||||||
|
passlib[bcrypt]==1.7.4
|
||||||
|
python-multipart==0.0.6
|
||||||
|
redis==5.0.1
|
||||||
|
httpx==0.25.2
|
||||||
|
python-dotenv==1.0.0
|
||||||
Reference in New Issue
Block a user