Commit Graph

5 Commits

Author SHA1 Message Date
joungmin
319fd18258 feat(stats): #337 봇 UA 필터 + IP 레이트리밋
- BotDetector 유틸 (Pattern.CASE_INSENSITIVE: bot|crawler|spider|slurp|scrap|fetch|monitor|preview|lighthouse)
- RateLimitService: Redis SET NX EX(60s) 패턴으로 같은 IP 윈도우 차단
  - Bucket4j 대신 spring-data-redis 기존 의존성 재사용 (간결)
  - Redis 다운 시 fail-open (사용자 경험 우선)
- StatsController.recordVisit: HttpServletRequest 받아 UA + X-Forwarded-For 우선 IP
  - 봇/리밋 초과 → 200 + counted:false (사용자 페이지 로드 지장 X)
  - 통과 → 200 + counted:true → statsService.recordVisit()
- application.yml: app.rate-limit.visit-window-seconds (env VISIT_WINDOW_SECONDS) 기본 60
- dev 검증: 봇 UA → counted=false, Mozilla → true, 즉시 재호출 → false

설계서: docs/design/337-stats-bot-ratelimit/README.md

Refs: #337 (Developer 단계)
2026-06-15 15:26:27 +09:00
joungmin
e5dc0534c4 feat(daemon): #335 분산 락 (ShedLock + Redis)
build.gradle:
- shedlock-spring 5.16.0
- shedlock-provider-redis-spring 5.16.0

TastebyApplication: @EnableSchedulerLock(defaultLockAtMostFor=PT15M)

ShedLockConfig 신규: RedisLockProvider Bean (in-cluster Redis 재사용)

DaemonScheduler.run:
- @SchedulerLock(name="daemon-runner", lockAtMostFor=PT15M, lockAtLeastFor=PT30S)
- 멀티 파드 환경(RollingUpdate 등)에서 한 인스턴스만 실행
- Redis 키: lock:daemon-runner

설계서: docs/design/335-daemon-distributed-lock/README.md (commit c88cb6a)

Refs: #335 (Developer 단계)
2026-06-15 15:18:14 +09:00
joungmin
69e1882c2b Add video management endpoints and fix LLM extraction pipeline
- Add missing endpoints: fetch-transcript, extract, bulk-extract/pending,
  bulk-transcript/pending, manual restaurant add, restaurant update
- Add OCI HTTP client dependency (jersey3) for GenAI SDK compatibility
- Fix Oracle null parameter ORA-17004 with jdbcType=CLOB in MyBatis
- Fix evaluation IS JSON constraint by storing as valid JSON
- Add @JsonProperty("transcript") for frontend compatibility
- Add Korean-only rule to LLM extraction prompt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:08:40 +09:00
joungmin
c16add08c3 Migrate to MyBatis with proper Controller→Service→Mapper layering
- Add MyBatis Spring Boot Starter with XML mappers and domain classes
- Create 9 mapper interfaces + XML: Restaurant, Video, Channel, Review,
  User, Stats, DaemonConfig, Search, Vector
- Create 10 domain classes with Lombok: Restaurant, VideoSummary,
  VideoDetail, VideoRestaurantLink, Channel, Review, UserInfo,
  DaemonConfig, SiteVisitStats, VectorSearchResult
- Create 7 new service classes: RestaurantService, VideoService,
  ChannelService, ReviewService, UserService, StatsService,
  DaemonConfigService
- Refactor all controllers to be thin (HTTP + auth only), delegating
  business logic to services
- Refactor SearchService, PipelineService, DaemonScheduler, AuthService,
  YouTubeService to use mappers/services instead of JDBC/repositories
- Add Jackson SNAKE_CASE property naming for consistent API responses
- Add ClobTypeHandler for Oracle CLOB→String in MyBatis
- Add IdGenerator utility for centralized UUID generation
- Delete old repository/ package (6 files), JdbcConfig, LowerCaseKeyAdvice
- VectorService retains JDBC for Oracle VECTOR type support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:13:44 +09:00
joungmin
6d05be2331 Migrate backend from Python to Java Spring Boot
- Full Java 21 + Spring Boot 3.3 backend with Virtual Threads
- HikariCP connection pool for Oracle ADB
- JWT auth, Redis caching, OCI GenAI integration
- YouTube transcript extraction via API + Playwright browser fallback
- SSE streaming for bulk operations
- Scheduled daemon for channel scanning/video processing
- Mobile UI: collapse restaurant list to single row on selection
- Switch PM2 ecosystem config to Java backend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 20:26:32 +09:00