mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-01 20:42:50 +08:00
fix(guest): deny favorites + auth-status reads to guests; UI polish
Consolidated fix wave from the final whole-branch review of guest mode. - FIX 1 (critical): gate /api/favorites mount with requireNotGuest — the router keys off req.user.id (shared __guest__ principal), so guests could read/write a shared favorites bucket. Added focused guest-deny tests. - FIX 2: gate GET /api/auth/status and /api/auth/qrcode/status with requireNotGuest so config reads no longer leak to guests. - FIX 3: requireAuthInline in createSessionRouter now rejects guest sessions with 401 once guest mode is disabled (mirrors createRequireAuth), so /me stops returning guest data after an admin disables the feature. - FIX 4: Login guest button now sits BELOW the card (auth-page flex-direction column + guest-btn width 360px) instead of beside it. - FIX 5: mobile mini-player transport buttons in App.vue are now per-button gated for guests (prev/play/next/mode/volume), mirroring Player.vue. - FIX 6: refreshed stale "gated on player.control" seek comments in Player.vue and relabeled the now-stale quality-GET test. npm test: 354/354 pass. npm run build: tsc + vue-tsc + vite all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e47fc76529
commit
f142c514cd
8 files changed
+113
-12
No files matched your search
+10
-5
@@ -19,22 +19,22 @@
|
||||
<div class="m-player-artist">{{ currentSong.artist }}</div>
|
||||
</div>
|
||||
<div class="m-player-controls" @click.stop>
|
||||
<button class="m-player-btn" @click="playerStore.prev()">
|
||||
<button v-if="can('player.control')" class="m-player-btn" @click="playerStore.prev()">
|
||||
<Icon icon="mdi:skip-previous" />
|
||||
</button>
|
||||
<button class="m-player-btn" @click="playerStore.isPlaying ? playerStore.pause() : playerStore.resume()">
|
||||
<button v-if="canTransport" class="m-player-btn" @click="playerStore.isPlaying ? playerStore.pause() : playerStore.resume()">
|
||||
<Icon :icon="playerStore.isPlaying ? 'mdi:pause' : 'mdi:play'" />
|
||||
</button>
|
||||
<button class="m-player-btn" @click="playerStore.next()">
|
||||
<button v-if="canSkip" class="m-player-btn" @click="playerStore.next()">
|
||||
<Icon icon="mdi:skip-next" />
|
||||
</button>
|
||||
<button class="m-player-btn" @click="cycleMobileMode">
|
||||
<button v-if="canModeCtl" class="m-player-btn" @click="cycleMobileMode">
|
||||
<Icon :icon="mobileModeIcon" />
|
||||
</button>
|
||||
<button class="m-player-btn" @click="toggleMobileQueue">
|
||||
<Icon icon="mdi:playlist-music" />
|
||||
</button>
|
||||
<button class="m-player-btn" @click="toggleMobileVolume">
|
||||
<button v-if="canTransport" class="m-player-btn" @click="toggleMobileVolume">
|
||||
<Icon icon="mdi:volume-high" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -89,6 +89,11 @@ import Queue from './components/Queue.vue';
|
||||
|
||||
const playerStore = usePlayerStore();
|
||||
const session = useSession();
|
||||
const { can, guestCan } = session;
|
||||
// Mobile mini-player transport gating — mirrors components/Player.vue.
|
||||
const canTransport = computed(() => can('player.control') || guestCan('transport'));
|
||||
const canSkip = computed(() => can('player.control') || guestCan('skip'));
|
||||
const canModeCtl = computed(() => can('player.control') || guestCan('playMode'));
|
||||
const theme = computed(() => playerStore.theme);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Queue :open="showQueue" @close="showQueue = false" />
|
||||
|
||||
<div class="player-bar frosted-glass">
|
||||
<!-- Progress bar (read-only display; seek interaction gated on player.control) -->
|
||||
<!-- Progress bar (read-only display; seek interaction gated on transport / canTransport) -->
|
||||
<div
|
||||
class="progress-bar-container"
|
||||
:class="{ 'no-seek': !canTransport }"
|
||||
@@ -147,7 +147,7 @@ function updateProgress() {
|
||||
}
|
||||
|
||||
async function onProgressClick(e: MouseEvent) {
|
||||
if (!canTransport.value) return; // seek requires player.control
|
||||
if (!canTransport.value) return; // seek gated on transport (canTransport)
|
||||
const bar = progressBarRef.value;
|
||||
if (!bar) return;
|
||||
const rect = bar.getBoundingClientRect();
|
||||
|
||||
@@ -73,6 +73,7 @@ async function enterAsGuest() {
|
||||
.auth-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-primary);
|
||||
@@ -100,7 +101,7 @@ async function enterAsGuest() {
|
||||
.auth-card button:disabled { opacity: 0.6; cursor: progress; }
|
||||
.auth-error { color: #e26a6a; font-size: 13px; margin: 0; }
|
||||
.guest-btn {
|
||||
height: 38px; margin-top: 4px; border-radius: var(--radius-sm);
|
||||
width: 360px; height: 38px; margin-top: 4px; border-radius: var(--radius-sm);
|
||||
background: transparent; color: var(--text-secondary);
|
||||
border: 1px solid var(--border-color); cursor: pointer;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user