Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/DEMO.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ npm --prefix web run preview:demo

## 部署向导与连接诊断

真实部署首次登录改密后进入 `/setup`,已有管理员可以从导航或系统配置进入。前两步只保存 TeamSpeak 与网易云 API 配置,最后一步应用所有已保存的配置,等待 Voice 确认配置版本后检查连接。
真实部署首次登录改密后进入“设置 → 部署向导”(`/settings?group=setup`),已有管理员可以在设置内切换到部署向导。旧 `/setup` 地址自动跳转到该分类。前两步只保存 TeamSpeak 与网易云 API 配置,最后一步应用所有已保存的配置,等待 Voice 确认配置版本后检查连接。

`GET /admin/diagnostics` 仅管理员会话可访问,分别检查数据库、待应用配置、Voice gRPC、TeamSpeak 会话和可选的网易云 API。Voice 检查最多等待 3 秒;音乐 API 使用 4 秒 HTTP 超时,两者并发执行。错误响应不会包含上游异常文本、地址中的凭据或 Cookie。

Expand Down
18 changes: 2 additions & 16 deletions web/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import MusicPlayer from './components/MusicPlayer.vue'
import { appConfig } from './appConfig'
import { applyTheme, getInitialTheme } from './theme'
import { DEMO_MODE } from './demoMode'
import { PROJECT_URL, ISSUE_URL, CONTRIBUTE_URL, INSTALL_URL } from './projectLinks'
import { PROJECT_URL, INSTALL_URL } from './projectLinks'
import {
Home,
Music,
Expand All @@ -18,10 +18,7 @@ import {
Moon,
Sun,
Menu,
X,
MessageSquare,
GitPullRequest,
Compass
X
} from 'lucide-vue-next'

const sidebarOpen = ref(false)
Expand Down Expand Up @@ -113,11 +110,6 @@ function toggleTheme() {
<Search :size="20" />
<span>搜索</span>
</RouterLink>

<RouterLink to="/setup" class="nav-item" active-class="nav-item-active" @click="sidebarOpen = false">
<Compass :size="20" />
<span>部署向导</span>
</RouterLink>

<RouterLink
to="/playlists"
Expand Down Expand Up @@ -209,12 +201,6 @@ function toggleTheme() {
>
<Github :size="20" />
</a>
<a :href="ISSUE_URL" target="_blank" rel="noopener noreferrer" class="theme-toolbar-btn p-2 text-gray-500 hover:text-blue-600 rounded-lg" title="反馈问题" aria-label="反馈问题">
<MessageSquare :size="20" />
</a>
<a :href="CONTRIBUTE_URL" target="_blank" rel="noopener noreferrer" class="theme-toolbar-btn p-2 text-gray-500 hover:text-blue-600 rounded-lg" title="参与贡献" aria-label="参与贡献">
<GitPullRequest :size="20" />
</a>
<button
type="button"
:class="[
Expand Down
19 changes: 11 additions & 8 deletions web/src/views/SetupView.vue → web/src/components/SetupWizard.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { ArrowLeft, ArrowRight, Check, AlertCircle, Compass, Loader2, RefreshCw } from 'lucide-vue-next'
import { ArrowLeft, ArrowRight, Check, AlertCircle, Loader2, RefreshCw } from 'lucide-vue-next'
import { apiGet, apiPut } from '../api'
import { DEMO_MODE } from '../demoMode'
import type { DiagnosticReport, SetupSettings } from '../types/settings'

const emit = defineEmits<{ saved: [] }>()

const steps = ['连接 TeamSpeak', '选择音乐源', '应用并检查']
const step = ref(0)
const settings = ref<SetupSettings | null>(null)
Expand Down Expand Up @@ -34,6 +36,7 @@ async function saveStep() {
try {
const selected = Object.fromEntries(visibleFields.value.map(field => [field.key, values.value[field.key]]))
hydrate(await apiPut<SetupSettings>('/admin/settings', { values: selected, apply: false }))
emit('saved')
step.value += 1
} catch (e) { error.value = e instanceof Error ? e.message : '配置保存失败' }
finally { busy.value = false }
Expand All @@ -49,6 +52,7 @@ async function applyAndCheck() {
try {
const result = await apiPut<SetupSettings>('/admin/settings', { values: {}, apply: true })
hydrate(result)
emit('saved')
if (result.voice_restart_requested && result.voice_config_revision) {
message.value = '配置已应用,等待 Voice 使用新配置重启…'
const deadline = Date.now() + 60000
Expand All @@ -71,10 +75,9 @@ async function applyAndCheck() {
</script>

<template>
<div class="theme-setup-shell h-full overflow-y-auto bg-gray-50 pb-8">
<div class="mx-auto max-w-3xl px-5 py-8 md:px-8 md:py-10">
<div class="flex items-center gap-3 text-blue-600"><Compass :size="24" /><span class="text-sm font-semibold">TSBot 部署向导</span></div>
<h1 class="mt-3 text-2xl font-bold text-gray-900 md:text-3xl">让机器人加入你的频道</h1>
<section class="theme-setup-shell" aria-label="部署向导">
<div>
<h2 class="text-2xl font-bold text-gray-900 md:text-3xl">让机器人加入你的频道</h2>
<p class="mt-2 text-sm leading-6 text-gray-500">准备好 TeamSpeak 地址,按顺序配置并检查连接。</p>

<ol class="my-8 flex gap-3 border-b border-gray-200 pb-5 md:gap-6" aria-label="配置进度">
Expand All @@ -86,7 +89,7 @@ async function applyAndCheck() {
<p v-if="message" role="status" class="mb-5 text-sm leading-6 text-blue-700">{{ message }}</p>
<p v-if="!settings && busy" class="py-12 text-gray-500">正在加载配置…</p>
<template v-if="settings">
<h2 class="text-lg font-semibold text-gray-900">{{ steps[step] }}</h2>
<h3 class="text-lg font-semibold text-gray-900">{{ steps[step] }}</h3>
<p v-if="step === 0" class="mt-2 text-sm leading-6 text-gray-500">填写语音服务器端口(通常为 9987)。频道 ID 或路径选填其一;都留空则使用默认频道。</p>
<div v-if="step < 2" class="mt-6 space-y-5">
<div v-for="field in visibleFields" :key="field.key">
Expand All @@ -107,7 +110,7 @@ async function applyAndCheck() {
<button type="button" class="btn-secondary gap-2" :disabled="busy" @click="runDiagnostics"><RefreshCw :size="16" />重新检查连接</button>
</div>
<section v-if="report" class="mt-7" aria-label="连接检查结果" aria-live="polite">
<h3 class="font-semibold text-gray-900">{{ report.demo ? '模拟检查结果' : report.ready ? '必要连接已就绪' : '还有连接需要处理' }}</h3>
<h4 class="font-semibold text-gray-900">{{ report.demo ? '模拟检查结果' : report.ready ? '必要连接已就绪' : '还有连接需要处理' }}</h4>
<ul class="mt-3 divide-y divide-gray-200">
<li v-for="item in report.checks" :key="item.id" class="flex gap-3 py-4">
<Check v-if="item.status === 'success'" :size="19" class="mt-0.5 shrink-0 text-green-600" />
Expand All @@ -124,5 +127,5 @@ async function applyAndCheck() {
</div>
</template>
</div>
</div>
</section>
</template>
2 changes: 1 addition & 1 deletion web/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const router = createRouter({
{ path: '/login', name: 'login', component: LoginView, meta: { authPage: true } },
{ path: '/change-password', name: 'change-password', component: ChangePasswordView, meta: { authPage: true, requiresAuth: true } },
{ path: '/settings', component: SettingsView, meta: { requiresAuth: true } },
{ path: '/setup', component: () => import('./views/SetupView.vue'), meta: { requiresAuth: true } },
{ path: '/setup', redirect: { path: '/settings', query: { group: 'setup' } } },
{ path: '/cookie', redirect: { path: '/settings', query: { group: 'authorization' } } },
{ path: '/lyrics', component: LyricsView },
],
Expand Down
2 changes: 1 addition & 1 deletion web/src/views/ChangePasswordView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async function submit() {
try {
const firstLogin = authState.must_change_password
await changePassword(currentPassword.value, newPassword.value)
await router.replace(firstLogin ? '/setup' : '/settings')
await router.replace(firstLogin ? '/settings?group=setup' : '/settings')
} catch (e: any) {
error.value = String(e?.message || e)
} finally {
Expand Down
Loading
Loading