Commit graph

79 commits

Author SHA1 Message Date
2ddc16d532 feat(concert): 콘서트 상세 페이지 추가 (몰입형 디자인)
기존 DefaultSection으로 빈약하게 표시되던 콘서트를 전용 상세 페이지로.

백엔드:
- getScheduleDetail 콘서트 분기: 포스터/장소/세트리스트(곡별 멤버)/굿즈/다른 회차

프론트엔드:
- ConcertSection(PC) / MobileConcertSection(모바일) 신규
- 블러 포스터 히어로 + 회차 드롭다운 + 장소 지도 다이얼로그
- 세트리스트 펼치기/접기 애니메이션, 유닛/솔로만 멤버 태그
- 굿즈 masonry 갤러리(라이트박스), 회차 전환은 history replace
- 모바일은 세로 중앙형 전용 히어로 + 바텀시트
- Lightbox/MobileLightbox를 createPortal로 변경 (오버레이 전체화면 보장)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 23:20:48 +09:00
8b8b9a7f53 feat(x-schedule): 링크 미리보기(OG) 카드 추가 - 하이브리드
트윗의 외부 링크에 대해 미리보기 카드 표시.
- Nitter가 렌더링한 카드 우선 사용 (extractCard)
- Nitter 카드가 비어있으면 본문 URL로 OG 직접 추출 (og.js)
  - YouTube/Instagram 등 복구, HTML 엔티티 디코딩 포함
  - TikTok 등 봇 차단 사이트는 Nitter 카드로 커버
- schedule_x.card_data 컬럼 + getScheduleDetail 응답에 card 포함
- 가로 레이아웃 카드 (왼쪽 이미지 + 오른쪽 텍스트)
- CardImage: 이미지 로드 실패 시 fallback 아이콘 (인스타 CDN 만료 대비)
- 자체 영상/이미지가 있으면 OG 카드 숨김 (중복 방지)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 19:34:42 +09:00
d3d3c9cf75 feat(x-schedule): 트윗 네이티브 영상 썸네일 표시
Nitter는 영상 파일을 제공하지 않으므로 썸네일만 추출해 표시하고,
재생 시 원본 트윗으로 이동.

- scraper: extractVideoThumbnails 추가 (amplify_video_thumb 등)
- schedule_x.video_thumbnails 컬럼 + saveTweet 저장
- getScheduleDetail 응답에 videoThumbnails 포함
- PC/모바일 X 상세: 썸네일 + 재생버튼 + 'X에서 재생' 배지

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 19:02:29 +09:00
4cfa4ffc00 fix(x-bot): 리트윗 이중 저장 방지
같은 리트윗이 타임라인에서 래퍼 id / 원본 id 두 형태로 번갈아 나타나
post_id 중복 체크를 통과해 두 번 저장되던 문제 수정.

리트윗 저장 시 동일 내용 + (원작자 또는 봇계정) username이 이미 있으면
중복으로 간주해 건너뜀. hydration으로 양쪽 모두 전체 내용을 갖추므로
내용 기반 매칭이 안정적으로 동작.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 18:02:22 +09:00
2cfc580283 fix(x-bot): 잘린 긴 리트윗 전체 내용 복원 (hydration)
X long tweet(280자 초과)을 Nitter 타임라인이 간헐적으로 …로 잘라서
주는 경우, 개별 상태 페이지에서 재요청해 전체 내용으로 교체.

- parseTweets: …로 끝나는 트윗에 truncated 플래그 부여
- hydrateTruncatedTweets: 잘린 트윗 status 페이지 재요청 후 교체 (best-effort)
- fetchTweets/fetchAllTweets에 적용
- fetchSingleTweet을 timeout-safe하게 변경

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 18:00:14 +09:00
d530822a68 feat(festival-bot): 대학 축제 크롤러 봇 구현 (3단계)
검색 페이지(memogipost)를 크롤링하여 프로미스나인 출연 대학 축제를
Gemini url_context로 추출, 행사 일정을 자동 생성하는 봇.

백엔드:
- services/event.js: 이벤트 생성 로직 공유화 (upsertVenue, createEventSchedule, 카카오 검색)
- services/festival/: scraper(검색 페이지 크롤) + gemini(추출) + index(봇 플러그인)
- routes/admin/festival-bots.js: 축제 봇 CRUD API
- scheduler.js: festival 타입 지원, 시간 단위 cron(0 */H * * *) 변환
- 처리한 글 URL은 festival_crawl_log에 기록, 새 글 없으면 Gemini 미호출
- 학교명 부분일치 중복 감지, 활동 멤버 전체 자동 등록
- Gemini 503/500/429 재시도 로직

기타 수정:
- 행사 상세 페이지 관련 링크 줄바꿈 (truncate → break-all)
- 대학 축제 아이콘 변경 (GraduationCap → PartyPopper)
- docs/api.md, CLAUDE.md 환경변수 문서화

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 22:28:24 +09:00
bcc8555193 feat(youtube-bot): 제목에서 멤버 추출 옵션 추가
- bot_youtube에 extract_members_from_title 컬럼 추가 (기본값 0)
- services/youtube/index.js: 설명과 제목에서 각각 멤버 이름 검색, 합집합으로 중복 제거
- YouTubeBotDialog 고급 설정에 토글 추가 (설명 추출 토글 아래)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:50:02 +09:00
678e228bc5 feat(x-bot): 관리 YouTube 채널 영상 제외 옵션 추가
- bot_x에 exclude_managed_channels 컬럼 추가 (기본값 1, 기존 동작 유지)
- X 봇이 트윗에서 YouTube 링크를 추출할 때 이미 등록된 YouTube 봇 채널의 영상을 중복 추가할지 옵션으로 제어
- XBotDialog에 토글 추가 (extract_youtube 활성 시만 노출, 왼쪽 border로 하위 옵션 시각화)
- services/x/index.js processYoutubeLinks 시그니처에 옵션 파라미터 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:45:45 +09:00
d9836d2f5d feat(schedule): 행사 카테고리 추가 (학교 행사)
- schedule_categories에 '행사' 카테고리(id=11) 시드, CATEGORY_IDS.EVENT 상수 추가
- event_venues / schedule_event 테이블 생성 (subtype, school_name, venue_id, post_urls, poster_image_ids)
- routes/admin/events.js 신설: multipart 기반 CRUD + 다중 포스터 업로드 + 카카오맵 venue upsert
- services/image.js에 uploadEventPoster 추가 (event/{scheduleId}/poster/...)
- 공개 /schedules 서비스의 SCHEDULE_LIST_SQL / getScheduleDetail에 행사 JOIN 및 응답(subtype, schoolName, venue, posters, postUrls)
- buildSource에 EVENT 분기 추가 → source.name = 학교명
- Meilisearch 동기화: source_name에 school_name 포함, 부분 검색 대응을 위한 resolveSchoolNames 추가
- 프론트: form/index.jsx에 '행사' 분기, EventForm 컴포넌트 신설 (LocationSearchDialog 재사용, 다중 포스터/URL)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:04:42 +09:00
a01d368728 feat(variety): 썸네일을 RustFS 이미지 업로드로 변경
- schedule/{id}/thumbnail/ 경로에 original/medium_800/thumb_400 webp 업로드
- images 테이블로 이미지 관리, schedule_variety.thumbnail_id로 참조
- 프론트엔드: URL 입력 → 파일 업로드(드래그&드롭) + 미리보기로 변경
- 수정 시 기존 썸네일 교체/삭제 지원

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 19:07:08 +09:00
73f84fd7ac feat: 예능 카테고리 관리 기능 구현
- 백엔드: POST/PUT/GET /admin/variety/schedule API
- 백엔드: 일정 상세 응답에 broadcaster, replayUrl, thumbnailUrl 포함
- 프론트엔드: VarietyForm (추가), VarietyEditForm (수정) 페이지
- 방송사 프리셋 버튼 (KBS, MBC, SBS, tvN, 유튜브, 티빙 등)
- 출연 멤버 선택, 다시보기 링크, 썸네일 URL 지원
- 라우트 등록 및 일정 목록 편집 링크 연결

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 18:16:16 +09:00
ce41fc1a60 feat(concert): 콘서트 수정 페이지 추가
- 백엔드: GET /admin/concert/schedule/:seriesId (상세 조회)
- 백엔드: PUT /admin/concert/schedule/:seriesId (수정)
- 프론트엔드: ConcertEditForm 페이지 (생성 폼 컴포넌트 재사용)
- 라우트: /admin/schedule/concert/:seriesId/edit 등록
- 일정 목록: 콘서트 카테고리 편집 버튼이 수정 페이지로 연결
- schedule.js: formatSchedule에 concertSeriesId 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 18:25:01 +09:00
9498559f6b fix(x-bot): 리트윗 프로필/이미지 복구 및 원본 트윗 매칭
- getProfile: bot_x에 없는 계정도 Nitter에서 직접 조회 후 Redis 캐시
- refetch-retweets 스크립트: 원본 작성자 타임라인에서 매칭 트윗 찾아 이미지/내용 복구
- 기존 21건 리트윗 데이터 재수집 완료 (이미지 포함)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 14:07:43 +09:00
3ce8d7ec7d fix(x-bot): 리트윗 내용 잘림, Nitter 링크, 이미지 누락 수정
- extractTextFromHtml: Nitter 프록시 t.co URL을 원본 https://t.co/ URL로 변환
- parseTweets: 리트윗 원본 작성자(originalUsername) 추출, URL을 원본 작성자 기준으로 생성
- saveTweet: 리트윗인 경우 원본 작성자를 username으로 저장
- refetch-retweets 엔드포인트 및 스크립트 추가 (기존 잘못된 데이터 재수집)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 13:59:19 +09:00
1f1d6987d1 feat(backend): 관리자/봇 라우트에 logActivity 호출 추가
12개 관리자 라우트와 3개 봇 서비스 파일에 활동 로그 기록 추가.
관리자 작업(일정/앨범/멤버/봇 CRUD)과 봇 동기화(완료/에러)를
logs 테이블에 fire-and-forget으로 기록.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:04:07 +09:00
45adaaf0dc refactor(youtube): API 호출 최적화 - 새 영상만 상세 조회
기존: 매 폴링마다 activities.list + videos.list (2 units)
변경: activities.list로 videoId 확인 후 DB에 없는 새 영상만 videos.list 호출
결과: 일일 API 사용량 약 50% 감소 (1분 간격 3채널도 가능)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:52:42 +09:00
f8acb5450f fix(bot): 동시성 중복 INSERT 시 ER_DUP_ENTRY 에러 무시 처리
YouTube/X 봇의 영상 저장 트랜잭션에서 UNIQUE 제약 위반 발생 시
크래시 대신 null을 반환하여 gracefully 무시하도록 변경.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:28:27 +09:00
ba7def935c feat(x-bot): YouTube 영상 추출 옵션 추가
X 봇 설정에서 트윗 내 YouTube 링크 자동 추출 기능을 온/오프 가능하게 함:
- bot_x 테이블에 extract_youtube 컬럼 추가 (기본값: false)
- 고급 설정에 "YouTube 영상 추출" 토글 추가
- extractYoutube가 true일 때만 YouTube 일정 자동 생성

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 22:40:52 +09:00
7d140aa1f3 fix(x-bot): 동기화 결과에 total 필드 추가
- syncNewTweets(), syncAllTweets()에 total 필드 추가
- 프론트엔드 토스트에서 undefined 표시되던 문제 해결

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 09:57:04 +09:00
c86cda00ae fix(meilisearch): 전체 동기화 시 DB에 없는 문서 삭제
- syncAllSchedules()에서 Meilisearch의 모든 문서 ID 조회
- DB에 없는 문서는 Meilisearch에서 삭제
- 삭제된 일정이 검색에 계속 나타나는 문제 해결

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 09:50:54 +09:00
294018c93b refactor(x-bot): x_profiles 테이블 제거, bot_x로 통합
- x_profiles 테이블 삭제 (bot_x에 프로필 정보 포함)
- saveProfile(), getProfile() 함수가 bot_x 테이블 사용하도록 수정
- Redis 캐시는 그대로 유지 (성능)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 09:42:26 +09:00
5d44434e36 feat(x-bot): 리트윗 옵션 및 고정 트윗 제외 기능 추가
- include_retweets 옵션으로 리트윗 포함 여부 설정 가능
- 고정된 트윗(pinned)은 기본적으로 파싱에서 제외
- XBotDialog에서 Twitter 아이콘을 X 아이콘으로 변경
- schedule_x의 username을 source_name으로 활용

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 09:37:57 +09:00
9ceef6c656 feat(x-bot): 키워드 필터링 및 전체 동기화 기능 추가
Backend:
- bot_x 테이블에 text_filters 컬럼 추가
- syncNewTweets/syncAllTweets에 텍스트 필터링 로직 적용
- 봇 추가 시 전체 트윗 동기화 수행 (백그라운드)
- X 봇 API에 text_filters 필드 처리

Frontend:
- XBotDialog에 고급 설정 (키워드 필터) UI 추가
- BotTableRow에서 X 봇 수정/삭제 버튼 활성화

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 09:23:45 +09:00
535fbb6768 feat(x): Nitter 프로필 조회 함수 추가
- fetchProfile() 함수 추가: username으로 프로필 정보 조회
- displayName, avatarUrl 반환

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 20:08:59 +09:00
4f11e14b12 refactor(db): 봇 테이블 이름 통일 및 X 봇 스키마 추가
- youtube_bots → bot_youtube, x_bots → bot_x로 테이블 이름 변경
- bot_x 테이블 생성 및 시드 데이터 추가
- 관련 백엔드 코드에서 테이블 참조 업데이트
- X 봇 동적 관리 구현 계획 문서 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 19:52:41 +09:00
2e7fe697fc feat(admin): YouTube 봇 추가/수정/삭제 기능 완성
- 채널 핸들로 채널 정보 조회 API 추가 (POST /youtube-bots/lookup)
- getChannelByHandle 함수 추가 (YouTube API forHandle 사용)
- 봇 추가 시 채널 조회 후 배너 이미지 표시
- 봇 수정 API 스키마에 null 허용 추가
- 삭제 확인 다이얼로그 및 삭제 기능 구현
- 디버깅 로그 제거

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-07 10:51:45 +09:00
a8c12aa76d feat: YouTube 봇 DB 기반 관리로 마이그레이션
- YouTube 봇 설정을 bots.js에서 youtube_bots 테이블로 이동
- 봇 ID를 AUTO_INCREMENT로 변경 (youtube-{id} 형식)
- 고정 멤버 다중 선택 지원 (default_member_ids JSON)
- 제목 필터 다중 키워드 지원 (title_filters JSON)
- Redis 캐싱 제거 (Activities API 사용으로 불필요)
- 채널 배너 URL DB 저장 (youtube_bots.banner_url)
- YouTubeBotDialog UI 개선:
  - Portal 기반 드롭다운 (overflow 문제 해결)
  - AnimatePresence 애니메이션 적용
  - 다중 선택 컴포넌트 추가
  - 태그 입력 형태의 제목 필터
  - 뒷배경 클릭 방지

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-07 10:15:07 +09:00
730da864a4 fix: YouTube 봇 fetchRecentVideos를 Activities API로 변경
- playlistItems API 대신 Activities API 사용
- playlistItems는 새 영상 반영이 지연되는 문제가 있었음
- Activities API는 새 업로드를 즉시 반영함
- upload 외 다른 활동(좋아요, 플레이리스트 등)도 포함되므로 2배로 조회 후 필터링
- API 할당량 비용은 동일 (1 단위)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:58:38 +09:00
f01b2b8054 fix: 유튜브 채널 배너 이미지 고해상도로 변경
- bannerUrl에 =w2560 파라미터 추가하여 2560px 너비로 요청

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 18:22:45 +09:00
cb184e4fa5 feat: 유튜브 예정 일정에 채널 배너 이미지 표시
- YouTube API에서 채널 정보(배너 이미지) 조회 함수 추가
- 채널 정보 Redis 캐싱 (24시간)
- 일정 상세 API에 bannerUrl 필드 추가
- 예정 일정 placeholder에 배너 이미지 배경 표시

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 18:20:49 +09:00
b16aa963cd feat: 유튜브 예정 일정 UI 추가
- PC YoutubeSection에 예정 일정 placeholder UI 추가
- 예정 일정일 경우 "예정" 배지 표시
- 영상 준비 중 placeholder 컴포넌트 추가
- 예정 일정에도 channelName 반환하도록 API 수정

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 14:57:58 +09:00
e759d14ed6 feat: 스프 채널 다음 주 예정 일정 자동 생성 기능
- 새 영상(쇼츠 제외) 추가 시 다음 주 같은 요일 예정 일정 자동 생성
- 실제 영상 업로드 시 예정 일정을 실제 정보로 덮어씌움
- 금요일 00시까지 영상 없으면 예정 일정 삭제 + 다음 주 예정 일정 생성
- autoScheduleNext 설정: dayOfWeek, time, title, deadlineDayOfWeek, excludeShorts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 14:51:12 +09:00
48f41c6db0 feat: 장소 검색 API 추가 (카카오/구글)
- 국내: 카카오맵 API (/api/admin/kakao/places)
- 해외: 구글 Places API (/api/admin/google/places)
- YOUTUBE_API_KEY를 GOOGLE_API_KEY로 통합

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 14:06:33 +09:00
65b1d931f3 feat: 콘서트 일정 저장 API 구현
콘서트 폼 데이터를 저장하는 백엔드 API 추가.
multipart/form-data로 포스터, 굿즈 이미지, 회차, 세트리스트를 처리하고
트랜잭션으로 관련 테이블에 일괄 저장 후 Meilisearch 동기화.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 11:48:50 +09:00
f97c925fba refactor: 생일 페이지 라우트를 /schedule/:id 형식으로 변경
- /birthday/:memberName/:year → /schedule/birthday-{year}-{nameEn}
- ScheduleDetail에서 특수 ID(birthday, debut, anniversary) 감지
- Birthday 컴포넌트가 props로 year, nameEn 받도록 변경
- 멤버 API가 영문명으로도 조회 가능하도록 수정

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 13:15:04 +09:00
e1ee0b47a0 fix: 특수 일정 ID에 연도 포함
- 생일: birthday-{year}-{name_en} (예: birthday-2025-saerom)
- 데뷔: debut-{year} (예: debut-2018)
- 주년: anniversary-{year} (예: anniversary-2026)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 13:07:39 +09:00
2fec6c552d feat: 봇 일정 추가 시 Meilisearch 실시간 동기화
- syncScheduleById 함수 추가: 개별 일정 동기화
- YouTube 봇: 영상 추가 시 Meilisearch 동기화
- X 봇: 트윗/유튜브 링크 추가 시 Meilisearch 동기화
- description 컬럼 제거 (schedules 테이블에 없음)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:26:32 +09:00
ea9922de00 refactor: 일정 정렬 로직을 백엔드로 이동
- 백엔드에서 특수 일정(기념일, 생일) 우선 정렬
- 프론트엔드의 중복 정렬 로직 제거
- 정렬 순서: 날짜 > 특수 일정 > 시간

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:30:51 +09:00
5b9d93b37f feat: 데뷔/주년 기념일 카드 및 축하 다이얼로그 추가
- DebutCard 컴포넌트 추가 (PC/모바일)
- DebutCelebrationDialog 축하 다이얼로그 추가
- Fromis9Logo SVG 컴포넌트 추가
- 기념일 카테고리 추가 (ID: 9)
- 데뷔일(2018.01.24) 및 주년 일정 자동 생성
- 폭죽 효과 추가 (fireDebutConfetti)
- 카테고리 정보 DB에서 동적 조회하도록 개선

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 15:04:29 +09:00
5415893f9d 트랙 영상 타입 구분 기능 추가
- DB: music_video_url을 video_url로 변경, video_type 컬럼 추가
- 백엔드: insertTracks에서 video_url, video_type 처리
- 관리자: 영상 타입 선택 드롭다운 추가 (뮤직비디오/스페셜 영상)
- CustomSelect: {value, label} 객체 옵션 및 size prop 지원
- 트랙 상세: video_type에 따른 라벨 동적 표시

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 10:56:12 +09:00
87a69c0cbd refactor: API 응답에서 datetime을 date와 time으로 분리
- datetime 필드를 date와 time 필드로 분리하여 00:00 시간도 정상 표시되도록 수정
- 백엔드: formatSchedule, Meilisearch 검색 결과, 스키마 업데이트
- 프론트엔드: datetime 파싱 로직 제거, date/time 직접 사용
- 문서: API 응답 예시 업데이트

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 10:11:02 +09:00
85f03cb2d8 타임스탬프 KST 통일 및 Meilisearch 동기화 소요 시간 추가
- date.js: nowKST() 함수 추가
- 모든 타임스탬프를 UTC에서 KST(+09:00)로 변경
  - scheduler.js, bots.js, x/index.js, logger.js, app.js
- Meilisearch 봇에 동기화 소요 시간(ms) 추적 추가
- BotCard.jsx: 중복된 마지막 동기화 대신 소요 시간 표시

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 22:00:58 +09:00
8091f4ac67 feat: Meilisearch 버전 체크 기반 자동 동기화
- 4시~4시 5분간 1분 간격으로 Meilisearch 버전 체크
- watchtower 업데이트로 버전 변경 감지 시 즉시 동기화
- 동기화 오류 시 인덱스 삭제 후 재생성하여 재시도
- 기존 고정 시간(4:05) cron 방식에서 버전 감지 방식으로 변경

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 21:41:05 +09:00
52a655bf76 feat: 외부 서비스 안정성 개선 (Phase 3)
- Nitter 요청에 10초 타임아웃 및 HTTP 상태 코드 검증 추가
- Meilisearch syncAllSchedules에서 불필요한 deleteAllDocuments 제거
  - addDocuments는 같은 ID면 자동 업데이트(upsert)
- 일정 삭제 시 Meilisearch 동기화 코드 정리 (동적 import 제거)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 21:01:35 +09:00
4edef16310 fix: 일정 저장/삭제에 트랜잭션 적용
- youtube/index.js: saveVideo에 withTransaction 적용
- x/index.js: saveTweet, saveYoutubeFromTweet에 withTransaction 적용
- schedules/index.js: DELETE 핸들러에 withTransaction 적용
- 중간 실패 시 자동 롤백으로 데이터 무결성 보장
- docs/improvements.md 문서 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 20:21:56 +09:00
b8137935c2 refactor: 보안 강화 및 앨범 삭제 로직 개선
- JWT_SECRET 환경변수 필수화 (기본값 제거)
- 앨범 삭제 시 S3 파일(사진, 티저, 비디오) 함께 삭제
- 앨범 삭제 시 관련 DB 테이블 정리 (album_photo_members, album_photos, album_teasers)
- Meilisearch latest 태그로 변경 (v1.6 → latest)
- 코드 리뷰 문서 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:00:17 +09:00
2a50a07a29 fix: Meilisearch 검색 결과에서도 전체 멤버 '프로미스나인' 처리
- searchSchedules: 검색 시 현재 활동 멤버 수 캐시
- formatScheduleResponse: 전체 멤버인 경우 '프로미스나인'으로 대체
- syncAllSchedules: 동기화 시 탈퇴 멤버 제외, 멤버 수 캐시 갱신

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:56:38 +09:00
9515db712d feat: 전체 멤버인 경우 '프로미스나인'으로 표시
- 백엔드: buildMemberMap, getScheduleDetail 함수에서 현재 활동 멤버 전원인 경우
  "프로미스나인"으로 대체하여 반환
- 프론트엔드: getDisplayMembers 함수에서 멤버 수 계산 로직 제거 (백엔드에서 처리)
- 탈퇴 멤버(is_former=1) 제외하고 현재 활동 멤버만 계산

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 20:42:05 +09:00
e136f3c74b fix(backend): 검색 결과에서 _rankingScore 제거 및 CORS 설정 추가
변경 사항:
- 검색 API 응답에서 _rankingScore 필드 제거
- @fastify/cors 패키지 추가
- docs.caadiq.co.kr에서 API 테스트 요청 허용

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 23:21:24 +09:00
51063a120a refactor(backend): 일정 API 공통 포맷팅 함수로 리팩토링
공통 함수 추가:
- normalizeDate(): 날짜 문자열 정규화
- buildDatetime(): datetime 문자열 생성
- buildSource(): source 객체 생성
- formatSchedule(): 단일 일정 포맷팅
- formatSchedules(): 일정 목록 포맷팅
- buildMemberMap(): 멤버 맵 조회

변경:
- getMonthlySchedules: 공통 함수 사용
- getUpcomingSchedules: 공통 함수 사용
- meilisearch/formatScheduleResponse: buildDatetime 공통 함수 사용

SQL 쿼리도 SCHEDULE_LIST_SQL 상수로 통합

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 23:18:48 +09:00