import { eq } from 'drizzle-orm' import { db } from '~/drizzle/db' import { systemSettings } from '~/drizzle/schema' import { CacheService } from '~~/server/services/cacheService' import { kuGouFetch } from '~~/server/services/kugouService' /** * 粘贴 Cookie / Token 绑定酷狗 —— 不再需要扫码。 * * 为什么可以这样做:`kuGouFetch()` 是**无状态**的 —— 它把库里存的 * `kugouAdminCookie` 放进请求头、`kugouAdminToken` 放进 query,去调 sidecar。 * 也就是说"扫码"只是**获取 cookie 的一种方式**;只要拿到一份有效 cookie, * 后续同步的行为与扫码绑定完全一致。 * * 好处:管理员在**电脑浏览器**上登录酷狗(滑块/短信验证码都能过)→ * 从 devtools 复制 Cookie 粘贴过来即可,不依赖另一台装了酷狗 App 的手机。 * * 安全:这里**不接收也不保存酷狗账号密码**,只保存浏览器会话 Cookie(与扫码同款), * 且写入前会先用它探一次 `/login/status`,无效的凭证不会落库。 */ 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 cookie = String(body?.cookie || '').trim() const token = String(body?.token || '').trim() if (!cookie && !token) { throw createError({ statusCode: 400, message: '请粘贴 Cookie(或 token)后再保存' }) } if (cookie.length > 8000) { throw createError({ statusCode: 400, message: 'Cookie 太长(超过 8000 字),可能粘贴错了内容' }) } // 先用这份凭证探一次登录状态:有效才写库,避免把废值存进去导致"看起来绑定了但同步全失败" let loginStatus: any = null try { loginStatus = await kuGouFetch( '/login/status', { timestamp: Date.now() }, { forceCookie: cookie || undefined, forceToken: token || undefined }, ) } catch (e: any) { throw createError({ statusCode: 502, message: '用这份 Cookie 连接酷狗失败:' + (e?.message || String(e)) + '(如果提示连不上 sidecar,请先到「服务设置」里确认酷狗 API 地址)', }) } const statusOk = Number(loginStatus?.status ?? 0) === 1 || !!(loginStatus?.data?.profile || loginStatus?.data?.account) if (!statusOk) { throw createError({ statusCode: 400, message: '这份 Cookie 无效或已过期(酷狗侧显示未登录):' + (loginStatus?.error || loginStatus?.message || '请在浏览器里重新登录酷狗后重新复制'), }) } const profile = loginStatus?.data?.profile || loginStatus?.data?.account || null const rows = await db.select().from(systemSettings).limit(1) const patch: Record = { kugouAdminToken: token || null, kugouAdminCookie: cookie || null, kugouAdminUser: profile || null, kugouAdminBoundAt: new Date(), updatedAt: new Date(), } if (!rows[0]) { await db.insert(systemSettings).values(patch as any) } 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, bound: true, user: profile, hint: '绑定成功,可以直接去同步歌单了', } })