# API 명세

Base URL: `/api`

## 인증

### POST /auth/login
로그인 (JWT 토큰 발급)

### GET /auth/verify
토큰 검증 및 사용자 정보 (인증 필요)

---

## 멤버

### GET /members
멤버 목록 조회

### GET /members/:id
멤버 상세 조회

---

## 앨범

### GET /albums
앨범 목록 조회

### GET /albums/:id
앨범 상세 조회

---

## 일정

### GET /schedules
일정 조회

**Query Parameters:**
- `year`, `month` - 월별 조회
- `startDate` - 시작 날짜 (YYYY-MM-DD), 다가오는 일정 조회
- `search` - 검색어 (Meilisearch 사용)
- `offset`, `limit` - 페이징

※ `search`, `startDate`, `year/month` 중 하나는 필수

**월별 조회 응답:**
```json
{
  "2026-01-18": {
    "categories": [
      { "id": 2, "name": "유튜브", "color": "#ff0033", "count": 3 }
    ],
    "schedules": [
      {
        "id": 123,
        "title": "...",
        "time": "19:00:00",
        "category": { "id": 2, "name": "유튜브", "color": "#ff0033" },
        "source": {
          "name": "fromis_9",
          "url": "https://www.youtube.com/watch?v=VIDEO_ID"
        }
      }
    ]
  }
}
```

**source 객체 (카테고리별):**
- YouTube (category_id=2): `{ name: "채널명", url: "https://www.youtube.com/..." }`
- X (category_id=3): `{ name: "", url: "https://x.com/realfromis_9/status/..." }` (name 빈 문자열)
- 기타 카테고리: source 없음

**다가오는 일정 응답 (startDate):**
```json
[
  {
    "id": 123,
    "title": "...",
    "date": "2026-01-18",
    "time": "19:00:00",
    "category_id": 2,
    "category_name": "유튜브",
    "category_color": "#ff0033",
    "members": [{ "name": "송하영" }]
  }
]
```
※ 현재 활동 멤버 전원인 경우 `[{ "name": "프로미스나인" }]` 반환 (탈퇴 멤버 제외)

**검색 응답:**
```json
{
  "schedules": [
    {
      "id": 123,
      "title": "...",
      "datetime": "2026-01-18T19:00:00",
      "category": { "id": 2, "name": "유튜브", "color": "#ff0033" },
      "source": { "name": "fromis_9", "url": "https://..." },
      "members": ["송하영"],
      "_rankingScore": 0.95
    }
  ],
  "total": 100,
  "offset": 0,
  "limit": 20,
  "hasMore": true
}
```

### GET /schedules/categories
카테고리 목록 조회

**응답:**
```json
[
  { "id": 1, "name": "기타", "color": "#gray", "sort_order": 0 },
  { "id": 2, "name": "유튜브", "color": "#ff0033", "sort_order": 1 }
]
```

### GET /schedules/:id
일정 상세 조회

### DELETE /schedules/:id
일정 삭제 (인증 필요)

### POST /schedules/sync-search
Meilisearch 전체 동기화 (인증 필요)

---

## 추천 검색어

### GET /schedules/suggestions
추천 검색어 조회

**Query Parameters:**
- `q` - 검색어 (2자 이상)
- `limit` - 결과 개수 (기본 10)

**응답:**
```json
{
  "suggestions": ["송하영", "송하영 직캠", "하영"]
}
```

### GET /schedules/suggestions/popular
인기 검색어 조회

**Query Parameters:**
- `limit` - 결과 개수 (기본 10)

**응답:**
```json
{
  "queries": ["프로미스나인", "송하영", "이서연"]
}
```

### POST /schedules/suggestions/save
검색어 저장 (검색 실행 시 호출)

**Request Body:**
```json
{
  "query": "검색어"
}
```

### GET /schedules/suggestions/dict
사용자 사전 조회 (인증 필요)

**응답:**
```json
{
  "content": "프로미스나인\t프로미스나인\tNNP\n..."
}
```

### PUT /schedules/suggestions/dict
사용자 사전 저장 (인증 필요)

**Request Body:**
```json
{
  "content": "프로미스나인\t프로미스나인\tNNP\n..."
}
```

---

## 관리자 - 봇 관리 (인증 필요)

### GET /admin/bots
봇 목록 조회

**응답:**
```json
[
  {
    "id": "youtube-fromis9",
    "name": "fromis_9",
    "type": "youtube",
    "status": "running",
    "last_check_at": "2026-01-18T10:30:00Z",
    "last_added_count": 2,
    "schedules_added": 150,
    "check_interval": 2,
    "error_message": null,
    "enabled": true
  }
]
```

### POST /admin/bots/:id/start
봇 시작

### POST /admin/bots/:id/stop
봇 정지

### POST /admin/bots/:id/sync-all
전체 동기화 (모든 영상/트윗 수집)

**응답:**
```json
{
  "success": true,
  "addedCount": 25,
  "total": 100
}
```

### GET /admin/bots/quota-warning
YouTube API 할당량 경고 조회

**응답:**
```json
{
  "active": true,
  "message": "YouTube API 할당량 초과",
  "timestamp": "2026-01-18T10:00:00Z"
}
```

### DELETE /admin/bots/quota-warning
할당량 경고 해제

---

## 관리자 - YouTube (인증 필요)

### GET /admin/youtube/video-info
YouTube 영상 정보 조회

**Query Parameters:**
- `url` - YouTube URL (watch, shorts, youtu.be 모두 지원)

**응답:**
```json
{
  "videoId": "abc123",
  "title": "영상 제목",
  "channelId": "UCxxx",
  "channelName": "채널명",
  "date": "2026-01-19",
  "time": "15:00:00",
  "videoType": "video",
  "videoUrl": "https://www.youtube.com/watch?v=abc123"
}
```

### POST /admin/youtube/schedule
YouTube 일정 저장

**Request Body:**
```json
{
  "videoId": "abc123",
  "title": "영상 제목",
  "channelId": "UCxxx",
  "channelName": "채널명",
  "date": "2026-01-19",
  "time": "15:00:00",
  "videoType": "video"
}
```

### PUT /admin/youtube/schedule/:id
YouTube 일정 수정 (멤버, 영상 유형)

**Request Body:**
```json
{
  "memberIds": [1, 2, 3],
  "videoType": "video"
}
```
※ `videoType`: "video" 또는 "shorts"

---

## 관리자 - X (인증 필요)

### GET /admin/x/post-info
X 게시글 정보 조회 (Nitter 스크래핑)

**Query Parameters:**
- `postId` - 게시글 ID (필수)
- `username` - 사용자명 (기본: realfromis_9)

**응답:**
```json
{
  "postId": "1234567890",
  "username": "realfromis_9",
  "text": "게시글 전체 내용",
  "title": "첫 문단 (자동 추출)",
  "imageUrls": ["https://pbs.twimg.com/media/..."],
  "date": "2026-01-19",
  "time": "15:00:00",
  "postUrl": "https://x.com/realfromis_9/status/1234567890",
  "profile": {
    "displayName": "프로미스나인 (fromis_9)",
    "avatarUrl": "https://..."
  }
}
```

### POST /admin/x/schedule
X 일정 저장

**Request Body:**
```json
{
  "postId": "1234567890",
  "title": "게시글 제목",
  "content": "게시글 내용",
  "imageUrls": ["https://..."],
  "date": "2026-01-19",
  "time": "15:00:00"
}
```

---

## 헬스 체크

### GET /health
서버 상태 확인

---

## API 문서

### GET /docs
Scalar API Reference UI

### GET /docs/json
OpenAPI JSON 스펙
