fix(admin+server): 酷狗 baseUrl 默认值移除 127.0.0.1 + 新增后台配置入口与推荐快捷地址
问题:容器内 SystemSettings.kugouApiBaseUrl 默认 http://127.0.0.1:3000 指向自己,sidecar 不可达。 修复:1) schema + migration 移除 kugouApiBaseUrl 默认值;kugouService + status API 兜底改空串(不再假设有 localhost),优先级 env(KUGOU_API_BASE) > DB。 2) 新增 POST /api/admin/kugou/settings:ADMIN+/SUPER 即时写 kugouApiBaseUrl 入库+刷新 CacheService。 3) KuGouImporter.client.vue 左栏新增 baseUrl 输入框(回车即存)+ 保存并测试按钮 + 两个推荐快捷按钮(容器宿主 http://172.19.0.1:3000、公网中继 https://bywmusic.bzw-hheb.top/kugou)+ 当前生效地址显式提示。 4) UI/Icon.vue 新增 server / globe / save 三个 SVG 图标与上述入口配套。
This commit is contained in:
@@ -70,7 +70,55 @@
|
|||||||
<LoadingState :height="72" />
|
<LoadingState :height="72" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-5 space-y-3">
|
<div class="mt-5 space-y-4">
|
||||||
|
<!-- baseUrl 配置 -->
|
||||||
|
<div class="rounded-xl border border-zinc-200 bg-zinc-50 p-4 space-y-3">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<label class="text-[11px] font-black uppercase tracking-wider text-zinc-600">
|
||||||
|
KuGouMusicApi 地址
|
||||||
|
</label>
|
||||||
|
<span
|
||||||
|
v-if="baseUrlSaving"
|
||||||
|
class="text-[11px] text-zinc-500"
|
||||||
|
>保存中...</span>
|
||||||
|
<span
|
||||||
|
v-else-if="baseUrlSavedTick"
|
||||||
|
class="text-[11px] text-emerald-600 font-bold"
|
||||||
|
>已保存</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-model="baseUrlInput"
|
||||||
|
type="url"
|
||||||
|
placeholder="例:http://172.19.0.1:3000 或 https://bywmusic.bzw-hheb.top/kugou,留空则走环境变量 KUGOU_API_BASE"
|
||||||
|
class="w-full h-[42px] px-3 rounded-xl border border-zinc-200 bg-white text-[12px] text-zinc-900 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-indigo-500/40 focus:border-indigo-400"
|
||||||
|
@keydown.enter="saveBaseUrl"
|
||||||
|
/>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<button
|
||||||
|
class="h-[34px] px-3 rounded-lg text-[11px] font-bold bg-white hover:bg-zinc-100 border border-zinc-200 text-zinc-700 transition-colors inline-flex items-center gap-1"
|
||||||
|
@click="baseUrlInput = 'http://172.19.0.1:3000'"
|
||||||
|
>
|
||||||
|
<Icon name="server" :size="13" /> 容器直连宿主 http://172.19.0.1:3000
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="h-[34px] px-3 rounded-lg text-[11px] font-bold bg-white hover:bg-zinc-100 border border-zinc-200 text-zinc-700 transition-colors inline-flex items-center gap-1"
|
||||||
|
@click="baseUrlInput = 'https://bywmusic.bzw-hheb.top/kugou'"
|
||||||
|
>
|
||||||
|
<Icon name="globe" :size="13" /> 中继 https://bywmusic.bzw-hheb.top/kugou
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="h-[34px] px-3 rounded-lg text-[11px] font-bold bg-zinc-900 hover:bg-zinc-800 text-white transition-colors inline-flex items-center gap-1"
|
||||||
|
@click="saveBaseUrl"
|
||||||
|
:disabled="baseUrlSaving"
|
||||||
|
>
|
||||||
|
<Icon name="save" :size="13" /> 保存并测试
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p v-if="status?.service?.baseUrl" class="text-[11px] text-zinc-500 leading-relaxed">
|
||||||
|
当前生效地址:<code class="px-1.5 py-0.5 rounded-md bg-white border border-zinc-200 text-zinc-700 font-mono">{{ status.service.baseUrl }}</code>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
class="flex-1 h-[44px] px-3 rounded-xl text-[12px] font-bold bg-zinc-100 hover:bg-zinc-200 text-zinc-700 transition-colors flex items-center justify-center gap-1.5"
|
class="flex-1 h-[44px] px-3 rounded-xl text-[12px] font-bold bg-zinc-100 hover:bg-zinc-200 text-zinc-700 transition-colors flex items-center justify-center gap-1.5"
|
||||||
@@ -379,6 +427,9 @@ const qrContext = ref<any>(null)
|
|||||||
const qrHint = ref('')
|
const qrHint = ref('')
|
||||||
const qrState = ref<number>(0)
|
const qrState = ref<number>(0)
|
||||||
const qrPollTimer = ref<any>(null)
|
const qrPollTimer = ref<any>(null)
|
||||||
|
const baseUrlInput = ref('')
|
||||||
|
const baseUrlSaving = ref(false)
|
||||||
|
const baseUrlSavedTick = ref(0)
|
||||||
|
|
||||||
const totalLoadedSongs = computed(
|
const totalLoadedSongs = computed(
|
||||||
() => Object.values(schedulesByDate.value).reduce((n, arr) => n + arr.length, 0),
|
() => Object.values(schedulesByDate.value).reduce((n, arr) => n + arr.length, 0),
|
||||||
@@ -436,11 +487,38 @@ async function reloadStatus() {
|
|||||||
try {
|
try {
|
||||||
const r = await $fetch('/api/admin/kugou/status', { ...auth.getAuthConfig() })
|
const r = await $fetch('/api/admin/kugou/status', { ...auth.getAuthConfig() })
|
||||||
status.value = r
|
status.value = r
|
||||||
|
if (r?.service?.baseUrl && !baseUrlInput.value) {
|
||||||
|
baseUrlInput.value = r.service.baseUrl
|
||||||
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
toast.show?.(e?.message || '状态刷新失败', 'error')
|
toast.show?.(e?.message || '状态刷新失败', 'error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function saveBaseUrl() {
|
||||||
|
const value = (baseUrlInput.value || '').trim()
|
||||||
|
if (value && !/^https?:\/\//i.test(value)) {
|
||||||
|
toast.show?.('请填写合法的 http(s) URL,或留空走环境变量 KUGOU_API_BASE', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
baseUrlSaving.value = true
|
||||||
|
baseUrlSavedTick.value = 0
|
||||||
|
try {
|
||||||
|
await $fetch('/api/admin/kugou/settings', {
|
||||||
|
method: 'POST',
|
||||||
|
...auth.getAuthConfig(),
|
||||||
|
body: { kugouApiBaseUrl: value || null },
|
||||||
|
})
|
||||||
|
toast.show?.('地址已保存', 'success')
|
||||||
|
baseUrlSavedTick.value = Date.now()
|
||||||
|
await reloadStatus()
|
||||||
|
} catch (e: any) {
|
||||||
|
toast.show?.(e?.message || '保存失败', 'error')
|
||||||
|
} finally {
|
||||||
|
baseUrlSaving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function startQrLogin() {
|
async function startQrLogin() {
|
||||||
qrLoading.value = true
|
qrLoading.value = true
|
||||||
qrOpen.value = true
|
qrOpen.value = true
|
||||||
|
|||||||
@@ -377,6 +377,28 @@
|
|||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
|
<!-- 服务器图标 -->
|
||||||
|
<g v-else-if="name === 'server'">
|
||||||
|
<rect x="2" y="2" width="20" height="8" rx="2" />
|
||||||
|
<rect x="2" y="14" width="20" height="8" rx="2" />
|
||||||
|
<line x1="6" x2="6.01" y1="6" y2="6" />
|
||||||
|
<line x1="6" x2="6.01" y1="18" y2="18" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- 地球图标 (globe) -->
|
||||||
|
<g v-else-if="name === 'globe'">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M2 12h20" />
|
||||||
|
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- 保存图标 -->
|
||||||
|
<g v-else-if="name === 'save'">
|
||||||
|
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
||||||
|
<polyline points="17 21 17 13 7 13 7 21" />
|
||||||
|
<polyline points="7 3 7 8 15 8" />
|
||||||
|
</g>
|
||||||
|
|
||||||
<!-- 搜索图标 -->
|
<!-- 搜索图标 -->
|
||||||
<g v-else-if="name === 'search'">
|
<g v-else-if="name === 'search'">
|
||||||
<circle cx="11" cy="11" r="8" />
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- 酷狗导入:管理员账号凭证 + 按日歌单同步记录表
|
-- 酷狗导入:管理员账号凭证 + 按日歌单同步记录表
|
||||||
ALTER TABLE "SystemSettings"
|
ALTER TABLE "SystemSettings"
|
||||||
ADD COLUMN IF NOT EXISTS "kugouApiBaseUrl" text DEFAULT 'http://127.0.0.1:3000',
|
ADD COLUMN IF NOT EXISTS "kugouApiBaseUrl" text,
|
||||||
ADD COLUMN IF NOT EXISTS "kugouAdminCookie" text,
|
ADD COLUMN IF NOT EXISTS "kugouAdminCookie" text,
|
||||||
ADD COLUMN IF NOT EXISTS "kugouAdminToken" text,
|
ADD COLUMN IF NOT EXISTS "kugouAdminToken" text,
|
||||||
ADD COLUMN IF NOT EXISTS "kugouAdminUser" jsonb,
|
ADD COLUMN IF NOT EXISTS "kugouAdminUser" jsonb,
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ export const systemSettings = pgTable('SystemSettings', {
|
|||||||
enabledThemes: text('enabledThemes').default('[\"System\",\"ClassicDark\",\"ClassicLight\",\"ModernLight\"]').notNull(),
|
enabledThemes: text('enabledThemes').default('[\"System\",\"ClassicDark\",\"ClassicLight\",\"ModernLight\"]').notNull(),
|
||||||
enabledPlatforms: text('enabledPlatforms'),
|
enabledPlatforms: text('enabledPlatforms'),
|
||||||
platformOrder: text('platformOrder'),
|
platformOrder: text('platformOrder'),
|
||||||
kugouApiBaseUrl: text('kugouApiBaseUrl').default('http://127.0.0.1:3000'),
|
kugouApiBaseUrl: text('kugouApiBaseUrl'),
|
||||||
kugouAdminCookie: text('kugouAdminCookie'),
|
kugouAdminCookie: text('kugouAdminCookie'),
|
||||||
kugouAdminToken: text('kugouAdminToken'),
|
kugouAdminToken: text('kugouAdminToken'),
|
||||||
kugouAdminUser: jsonb('kugouAdminUser').$type<Record<string, any>>(),
|
kugouAdminUser: jsonb('kugouAdminUser').$type<Record<string, any>>(),
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { eq } from 'drizzle-orm'
|
||||||
|
import { db } from '~/drizzle/db'
|
||||||
|
import { systemSettings } from '~/drizzle/schema'
|
||||||
|
import { CacheService } from '~~/server/services/cacheService'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const user = event.context.user
|
||||||
|
if (!user) throw createError({ statusCode: 401, message: '未登录' })
|
||||||
|
if (!['ADMIN', 'SUPER_ADMIN'].includes(user.role))
|
||||||
|
throw createError({ statusCode: 403, message: '仅管理员可修改酷狗配置' })
|
||||||
|
|
||||||
|
const body = await readBody(event)
|
||||||
|
const baseUrl = String(body?.kugouApiBaseUrl || '').trim()
|
||||||
|
|
||||||
|
if (baseUrl && !/^https?:\/\//i.test(baseUrl)) {
|
||||||
|
throw createError({ statusCode: 400, message: 'kugouApiBaseUrl 需要是合法的 http(s) URL,留空表示禁用 sidecar' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = await db.select().from(systemSettings).limit(1)
|
||||||
|
const patch: Record<string, any> = {
|
||||||
|
kugouApiBaseUrl: baseUrl || null,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rows[0]) {
|
||||||
|
await db.insert(systemSettings).values(patch)
|
||||||
|
} else {
|
||||||
|
await db.update(systemSettings).set(patch).where(eq(systemSettings.id, rows[0].id))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cs = CacheService.getInstance()
|
||||||
|
await cs.setSystemSettings?.({ ...(rows[0] || {}), ...patch })
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return { ok: true, kugouApiBaseUrl: baseUrl || null }
|
||||||
|
})
|
||||||
@@ -17,7 +17,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
const rows = await db.select().from(systemSettings).limit(1)
|
const rows = await db.select().from(systemSettings).limit(1)
|
||||||
const settings = rows[0] as any
|
const settings = rows[0] as any
|
||||||
const usedBase = base || settings?.kugouApiBaseUrl || process.env.KUGOU_API_BASE || 'http://127.0.0.1:3000'
|
const usedBase = base || settings?.kugouApiBaseUrl || process.env.KUGOU_API_BASE || ''
|
||||||
|
|
||||||
let serviceOk: boolean
|
let serviceOk: boolean
|
||||||
let serviceMessage: string
|
let serviceMessage: string
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ export default defineEventHandler(async (event) => {
|
|||||||
try {
|
try {
|
||||||
const cs = CacheService.getInstance()
|
const cs = CacheService.getInstance()
|
||||||
if (rows[0]) await cs.setSystemSettings?.({ ...rows[0], ...patch })
|
if (rows[0]) await cs.setSystemSettings?.({ ...rows[0], ...patch })
|
||||||
} catch (_) {}
|
} catch {}
|
||||||
return { ok: true }
|
return { ok: true }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export async function kuGouFetch<T = any>(
|
|||||||
options?: KuGouCallOptions,
|
options?: KuGouCallOptions,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const settings = (await readAdminSettings()) as any
|
const settings = (await readAdminSettings()) as any
|
||||||
const base = (options?.forceToken ? null : settings?.kugouApiBaseUrl) || process.env.KUGOU_API_BASE || 'http://127.0.0.1:3000'
|
const base = (options?.forceToken ? null : settings?.kugouApiBaseUrl) || process.env.KUGOU_API_BASE || ''
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128 Safari/537.36',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128 Safari/537.36',
|
||||||
|
|||||||
Reference in New Issue
Block a user