Capability flags (player.control/player.queue/bot.manage/platform.auth/quality) + per-member bot allow-list, layered under the existing member role; admin is super-user. Backend-enforced via requirePermission/requireBotAccess; existing members backfilled to full on upgrade, new members get a basic tier.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bilibili's CDN (i*.hdslb.com) returns 403 with `x-error-info:
RefererWhite` for image requests whose Referer is not on their
whitelist. `CoverArt.vue` already sets `referrerpolicy="no-referrer"`
on its `<img>` tag, BUT the `.cover-shadow` div renders the same URL
as a CSS `background-image`, which ignores the img attribute and uses
the document default policy (`strict-origin-when-cross-origin` in
modern Firefox/Chrome) — that sends `Referer: http://localhost:3000/`
and triggers the block.
Setting `<meta name="referrer" content="no-referrer">` in index.html
applies no-referrer site-wide: covers <img> tags, CSS background-image
fetches, and anywhere else CDNs check referer. Doesn't affect our
/api/* CSRF middleware because that uses Origin (still sent by the
browser), not Referer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous commit misidentified the second bug. Reverting the
Settings.vue `resize: vertical` → `resize: none` change — that
wasn't the issue.
Real fix: `.daily-card` (used by B站热门 and 每日推荐 sections in
Home.vue) is a CSS Grid cell with default `min-width: auto`, which
refuses to shrink below its content. A long Bilibili video title
inside `.daily-name` expanded the cell past its 1fr column, breaking
the 6-column grid and creating empty/black space on the right. The
existing `text-overflow: ellipsis` on `.daily-name` couldn't engage.
Adding `min-width: 0` to `.daily-card` lets the cell shrink to the
1fr grid track size, and the ellipsis truncation now works.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Player.vue: wrap artist text in a span with ellipsis. The previous
text node sat directly inside the flex `.song-artist` container with
no overflow handling, so a long author name expanded the container
past its 240px parent and broke the bottom Player bar layout. Also
add `min-width: 0 + overflow: hidden` to `.song-info` and
`.song-artist`, and a `:title` attribute for the full text on hover.
- Settings.vue: change `resize: vertical` on the cookie textareas
to `resize: none`. The browser's resize grip rendered as a stray
black triangle at the bottom-right corner in dark theme, and
dragging it caused visual artifacts on the right edge. The
textareas keep their `rows="3"` default height.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every response now carries:
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
Prevents the WebUI from being embedded in a third-party iframe.
Combined with the existing CSRF Origin-host check, this closes the
last meaningful UI-redress surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the missing case so role-change entries display in Chinese
instead of falling through to the generic key→target format.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
apiFetch called window.fetch which installApiClient had reassigned to
call apiFetch — every request blew the stack. Capture the native fetch
at module load (before any wrap) and use it inside apiFetch.
Symptom: first-run / login redirect never fires because the router
guard hangs on session.refresh().
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec for adding username+password auth to the WebUI to close the
unauthenticated-API exposure (all /api/* and /ws currently open).
Design: SQLite users + sessions tables, bcryptjs, 7-day rolling
HTTP-only cookie sessions, first-run setup wizard, Origin/Referer
CSRF check, WebSocket upgrade gated on the same session cookie.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes#64. Bilibili moved the unsigned /x/web-interface/search/type endpoint
behind their anti-bot wall; it now returns an HTML error page (出错啦!) even
with buvid3+buvid4 cookies, causing `play -b` to report "No results found".
Switch search() to /x/web-interface/wbi/search/type with proper wbi signing:
fetch img_key/sub_key from /nav, derive the mixin key via the standard
permutation, and sign each request with wts + w_rid (md5). Keys are cached
for 6h since they rotate ~daily. Other endpoints (view, playurl, popular,
top/rcmd) still work unsigned and are left unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>