Adds an admin/member role to WebUI auth. /api/users and /api/audit
are now gated by a requireAdmin middleware; all other authenticated
endpoints accept both roles. Schema migration defaults all existing
users to admin to preserve access. POST /api/users defaults new users
to member; first-run setup always creates an admin. Adds PATCH
/api/users/:id/role with last-admin demotion and deletion guards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Library.vue: use Song type from store so SongCard's strict platform
union accepts the data (was platform: string, broke after merge tightened
SongCard prop type).
database.test.ts: switch toEqual -> toMatchObject so getBotInstances()
returning extra profile_* schema columns no longer fails the assertion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "复制专属链接" button silently failed on public-IP HTTP deployments
because navigator.clipboard requires a secure context. Now the link is
always revealed in a modal with a read-only input (select-all on focus),
so users can copy manually even when clipboard APIs and execCommand both
fail. The dialog still tries to auto-copy when possible.
Also adds a publicUrl config option that overrides window.location.origin
for link generation (useful behind reverse proxies / with custom domains),
exposed via GET /api/config/public-url, and a trustProxy flag so Express
honors X-Forwarded-* when fronted by nginx/Caddy/Cloudflare.
https://claude.ai/code/session_019FSX3S3UUcKEYWanYmoqUv
Add BotProfileManager that updates the bot's TeamSpeak presence when
songs change: album cover as avatar, song info in nickname, away status
toggled on stop/play. Each feature is independently configurable via
REST API and persisted to the database. Permission-safe — features that
fail due to insufficient server permissions are silently disabled until
reconnect. Description falls back to nickname display on TS3 (only
supported via TS6 HTTP Query).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: on Docker restart, loadSavedBots() unconditionally connected
ALL saved bots regardless of autoStart flag, causing a rapid
connect/disconnect loop. Additionally, bot identities were regenerated
on every restart, causing TS server conflicts with stale sessions.
Changes:
- loadSavedBots() now only auto-connects bots with autoStart=true
- startBot/stopBot persist autoStart state so restart behavior matches
user intent
- Bot TS3 identity is persisted to database and reused across restarts
- Database schema migrated to include identity column
- TS3Client.connect() cleans up existing connection before reconnecting
- Stagger bot connections by 1s to avoid overwhelming the TS server
https://claude.ai/code/session_01L2kEV2M1QFWMCyPtLU5LgC
Critical:
- Persist serverProtocol/ts6ApiKey in database schema so TS6 config
survives restarts (added columns + manager save/load)
Medium:
- Clear udpErrorTimer on disconnect to prevent memory leak and stale
log messages from firing after teardown
- Guard against double connect() by disconnecting old client first
- Add settled guard in TS6HttpQuery.request() to prevent double
reject when both res error and req error fire
- Add res.on("error") handler to TS6HttpQuery response stream
Low:
- Cap probeTS3Query banner buffer at 256 bytes to prevent memory abuse
from non-TS3 services sending large data on port 10011
- Remove unnecessary EventEmitter inheritance from TS6HttpQuery
- Update database test fixtures with new serverProtocol/ts6ApiKey fields
https://claude.ai/code/session_016WhH58avUD9xy2dgADJgTh
Implement BiliBiliProvider for video-as-audio playback using direct
BiliBili API calls (search, video info, DASH audio URL extraction).
Add QR code login support and cookie persistence. Update FFmpeg to
send Referer header for BiliBili CDN URLs. Extend platform union type
to "netease" | "qq" | "bilibili" across all interfaces. Add -b flag
for chat commands and B站 badge in web UI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents EADDRINUSE crash that killed the entire app when port 3200
was still occupied from a previous run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>