Commit Graph

8 Commits

Author SHA1 Message Date
soldosluka857 7cc59f095c fix(auth/images): long-lived Emby token, unthrottle refresh, serve library posters
Three regressions reported on third-party clients and the web UI:

- Third-party clients (Emby/Jellyfin) dropped login / could not play /
  could not refresh the library, roughly hourly. The Emby
  AuthenticateByName response returned the 60-minute access token, but
  Emby clients have no refresh mechanism and reuse the AccessToken until
  logout. Issue a long-lived (30d) token for the Emby compat layer via
  AuthService.IssueEmbyToken so device sessions persist.

- Web could be thrown back to login under load: /auth/refresh was inside
  the IP rate-limited /auth group, so multiple users/tabs behind one
  reverse-proxy/NAT IP exhausted the budget and refresh failed -> logout.
  Only login/register are rate-limited now (raised to 30/min for shared
  IPs); refresh is excluded (already protected by a one-time refresh token).

- Posters/images stopped displaying on the web home and other pages
  (refresh did not help). The SSRF/path hardening (a) blocked the image
  proxy whenever a hostname *resolved* to a private IP, which happens
  under GFW DNS poisoning of public CDNs like image.tmdb.org, and (b)
  restricted local image reads to data/cache/movies/tv/anime dirs only,
  dropping sidecar posters stored under arbitrary per-library roots to a
  placeholder. isPrivateHost now only blocks literal private/loopback IPs
  (real SSRF vectors) and ImageProxy also allows reads under configured
  library roots.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-30 15:16:04 +08:00
soldosluka857 5bbc9fadfe security: fix SSRF, restrict CORS, add rate limiting on auth endpoints
- Add isPrivateHost() to block image proxy requests to loopback/private/
  link-local IPs (SSRF mitigation)
- Add isAllowedLocalPath() to restrict local file reads to configured
  data/cache/media directories only
- CORS middleware now takes debug flag; wildcard only when debug=true,
  production omits headers (same-origin enforced)
- Add per-IP sliding-window rate limiter (10 req/min) on login/register
  and Emby AuthenticateByName endpoints

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-30 10:03:43 +08:00
ShukeBta b5e11b6938 fix: secure adult visibility and telegram bot access 2026-05-30 01:39:11 +08:00
ShukeBta 68a5a1e3c0 Fix Emby playback routes and stream compatibility 2026-05-28 16:00:09 +08:00
ShukeBta e93eb40f4e Add commit message generation 2026-05-28 14:55:01 +08:00
ShukeBta 32ccb33fed Fix local adult metadata and watch history removal 2026-05-28 14:52:20 +08:00
ShukeBta 3a2db6bdd9 feat: Emby 兼容层完整实现 + 多模块功能增强
## Emby/Jellyfin 兼容层 (emby_compat.go / emby.go)
- 新增 SystemInfoPublic、FindUser、Items、Item、LatestItems、ResumeItems
- 新增 SetFavorite、MarkPlayed、RecordProgress 用户播放状态同步
- 新增 itemPayload、mediaSource、mediaStreams 媒体信息组装
- 双前缀路由 /emby/* 和 / 根路径,兼容 Infuse/Yamby/Senplayer/Kodi
- 新增 Ping、SystemEndpoint、AuthByName 端点
- emby.go 扩展对应 handler 函数

## 站点适配器 (site_adapter.go / site.go)
- SiteConfig 扩展 UserAgent/Timeout/Extra/FlareSolverrURL 字段
- doRequest() GET 请求支持 FlareSolverr 代理绕过 Cloudflare/WAF
- MTeam api_key 认证改为 Authorization: Bearer 格式
- Search() 重构为 sync.WaitGroup 并发执行,提升多站搜索性能
- siteModelToConfig() 改为 SiteService 方法,按 BrowserEmulation 填充 FlareSolverrURL

## 图片代理 (image_proxy.go)
- 重构图片代理服务,支持更多来源和缓存策略

## 下载管理 (downloads.go / download_clients.go / qbittorrent.go)
- 下载任务增强:状态管理、进度追踪优化
- qBittorrent 客户端连接稳定性改进

## 刮削与数据库 (scraper.go / tmdb.go / repository.go)
- 刮削器增强 TMDB 集成,补全元数据字段
- repository 扩展查询方法

## 前端 (web/src/)
- HomePage: 首页布局重构,按媒体库分组展示,系列聚合优化
- DiscoverPage: 发现页增强,错误处理改进(API key 缺失/网络错误分离)
- PosterWallPage: 海报墙优化,系列聚合展示
- MediaCard: 媒体卡片优化
- PlayerPage: 播放器改进
- 新增 utils/groupSeries.ts: 系列聚合工具函数
- .gitignore: 添加 .tmp_* 临时文件排除规则
2026-05-26 16:09:13 +08:00
Kiro 4b747c74ca feat: port missing MediaStation features (DLNA, STRM, files, dup, sched, api-configs, emby, storage)
Audit-driven port from the original Python MediaStation. Eight major
subsystems that were absent from the Go rewrite are now in place,
each with its own service, handler, frontend page and smoke-test
assertions.

Backend services
  - service/crypto.go: AES-256-GCM encrypt/decrypt for at-rest secrets
    keyed off the JWT secret. Legacy plaintext rows pass through
    unchanged for smooth upgrades. Unit-tested.
  - service/api_config.go: third-party provider config (TMDb, Bangumi,
    TheTVDB, Fanart, Douban, OpenAI). Seeds defaults on first run.
    Encrypts api_key on write, returns masked 'abc1****wxyz' projection.
  - service/duplicate.go: sparse-sample MD5 (head + middle + tail, 1MiB
    each, plus file-size suffix) duplicate finder. Picks 'best' primary
    (matched > size > id) and marks others is_duplicate=true.
  - service/filemanager.go: server-side allow-listed file browser used
    by the library-path picker. Strict path-traversal protection.
  - service/dlna.go: real SSDP M-SEARCH discovery + AVTransport
    SetAVTransportURI/Play SOAP cast. 30 s discovery cache.
  - service/scheduler.go: 3 recurring background jobs (library_scan
    60min, transcode_cleanup 24h, recycle_purge 24h with 30-day
    cutoff). Status + run-now endpoints.
  - service/cache_cleanup.go: walkAndPrune helper used by scheduler.
  - service/storage.go: DB-only disk-usage breakdown by library and by
    container format.
  - service/emby_compat.go: read-only Emby/Jellyfin shim
    (System/Info, Users, Users/x/Views, Items, PlaybackInfo) so Infuse
    / VidHub / Kodi can browse MediaStationGo libraries.

Model updates
  - Media: new strm_url (302 redirect target), file_hash, is_duplicate,
    duplicate_of fields.
  - APIConfig: new table for encrypted provider secrets.
  - AutoMigrate registers APIConfig.

Stream layer
  - StreamService.ServeFile now redirects 302 to strm_url when set so
    WebDAV / Alist / S3 / HTTP direct links work transparently.

Handlers + routes
  - Authed: GET /files, GET /storage, GET /dlna/devices, POST /dlna/cast,
    PUT/DELETE /media/:id/strm, POST /strm/import,
    POST /duplicates/{scan,unmark}.
  - Admin: GET/PUT/DELETE /admin/api-configs/:provider,
    GET /admin/scheduler, POST /admin/scheduler/:name/run.
  - New /emby/* group: System/Info, Users, Users/:userId/Views,
    Users/:userId/Items, Items/:id/PlaybackInfo (auth-required).

Frontend pages (lazy-loaded, 7 new chunks)
  - DlnaPage: device list + media picker + cast button.
  - FileManagerPage: root selector + breadcrumb + sortable listing.
  - APIConfigsPage: per-provider card with masked-key editor.
  - StoragePage: usage tiles + per-library bars + per-container grid.
  - DuplicatesPage: scan form + grouped report with primary highlight.
  - SchedulerPage: live job table with run-now button (5s refresh).
  - Sidebar reorganised: 自动化 group adds DLNA, 管理 group adds
    存储 / 文件浏览 / 重复文件 / 定时任务 / API 配置.

Smoke test additions (all admin-only)
  - api-configs seeded with 6 providers
  - api-config encrypted in db (sqlite3 enc:v1: prefix check)
  - storage breakdown
  - file browser lists library root + rejects /etc (path traversal)
  - dlna devices endpoint
  - scheduler exposes 3 jobs + run library_scan
  - emby /System/Info + /Users/{x}/Views
  - strm set + stream 302 + strm clear
  - duplicate scan

Verified: go build, go vet, go test (incl. new TestCrypto* suite + the
existing TestParseEpisode/TestCleanQuery/TestSrtToVTT/TestStripASSTags/
TestBuildFFmpegArgs); tsc -b && vite build emits 28 route chunks plus
the deferred hls chunk; main bundle 253 KB / 85 KB gzipped; smoke test
PASS=42 / FAIL=0.
2026-05-15 10:58:32 +00:00