mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-01 20:42:50 +08:00
fix: QR login now persists cookies, shows server-generated QR image, passes timestamp
- Auth router accepts CookieStore and persists cookies on login - NeteaseProvider passes timestamp to prevent cached QR responses - QR image from server used directly (qrimg field) - Cookie saved to disk on confirmed QR scan Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f77a6f0fd7
commit
9755ba2909
6 files changed
+42
-6
No files matched your search
@@ -58,6 +58,7 @@ async function main() {
|
||||
database: db,
|
||||
config,
|
||||
logger,
|
||||
cookieStore,
|
||||
staticDir: STATIC_DIR,
|
||||
});
|
||||
await webServer.start();
|
||||
|
||||
@@ -188,13 +188,16 @@ export class NeteaseProvider implements MusicProvider {
|
||||
}
|
||||
|
||||
async getQrCode(): Promise<QrCodeResult> {
|
||||
const keyRes = await this.api.get("/login/qr/key");
|
||||
const keyRes = await this.api.get("/login/qr/key", {
|
||||
params: { timestamp: Date.now() },
|
||||
});
|
||||
const key = keyRes.data?.data?.unikey ?? "";
|
||||
const createRes = await this.api.get("/login/qr/create", {
|
||||
params: { key, qrimg: true },
|
||||
});
|
||||
return {
|
||||
qrUrl: createRes.data?.data?.qrurl ?? "",
|
||||
qrImg: createRes.data?.data?.qrimg ?? "",
|
||||
key,
|
||||
};
|
||||
}
|
||||
@@ -203,7 +206,7 @@ export class NeteaseProvider implements MusicProvider {
|
||||
key: string
|
||||
): Promise<"waiting" | "scanned" | "confirmed" | "expired"> {
|
||||
const res = await this.api.get("/login/qr/check", {
|
||||
params: { key },
|
||||
params: { key, timestamp: Date.now() },
|
||||
});
|
||||
const code = res.data?.code;
|
||||
switch (code) {
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface SearchResult {
|
||||
|
||||
export interface QrCodeResult {
|
||||
qrUrl: string;
|
||||
qrImg?: string; // base64 data URL of QR image
|
||||
key: string;
|
||||
}
|
||||
|
||||
|
||||
+26
-1
@@ -1,11 +1,13 @@
|
||||
import { Router } from "express";
|
||||
import type { MusicProvider } from "../../music/provider.js";
|
||||
import type { CookieStore } from "../../music/auth.js";
|
||||
import type { Logger } from "../../logger.js";
|
||||
|
||||
export function createAuthRouter(
|
||||
neteaseProvider: MusicProvider,
|
||||
qqProvider: MusicProvider,
|
||||
logger: Logger
|
||||
logger: Logger,
|
||||
cookieStore?: CookieStore
|
||||
): Router {
|
||||
const router = Router();
|
||||
|
||||
@@ -18,8 +20,10 @@ export function createAuthRouter(
|
||||
const platform = req.query.platform as string;
|
||||
const provider = getProvider(platform);
|
||||
const status = await provider.getAuthStatus();
|
||||
logger.debug({ platform, status }, "Auth status check");
|
||||
res.json({ platform: provider.platform, ...status });
|
||||
} catch (err) {
|
||||
logger.error({ err }, "Auth status check failed");
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
}
|
||||
});
|
||||
@@ -29,6 +33,7 @@ export function createAuthRouter(
|
||||
const { platform } = req.body;
|
||||
const provider = getProvider(platform);
|
||||
const qr = await provider.getQrCode();
|
||||
logger.info({ platform, key: qr.key }, "QR code generated");
|
||||
res.json(qr);
|
||||
} catch (err) {
|
||||
logger.error({ err }, "QR code generation failed");
|
||||
@@ -45,8 +50,21 @@ export function createAuthRouter(
|
||||
}
|
||||
const provider = getProvider(platform as string);
|
||||
const status = await provider.checkQrCodeStatus(key as string);
|
||||
logger.info({ platform, status, key }, "QR status check");
|
||||
|
||||
// When confirmed, persist cookie
|
||||
if (status === "confirmed") {
|
||||
const cookie = provider.getCookie();
|
||||
const plat = (platform as string) === "qq" ? "qq" as const : "netease" as const;
|
||||
if (cookie && cookieStore) {
|
||||
cookieStore.save(plat, cookie);
|
||||
logger.info({ platform: plat }, "Cookie persisted to disk");
|
||||
}
|
||||
}
|
||||
|
||||
res.json({ status });
|
||||
} catch (err) {
|
||||
logger.error({ err }, "QR status check failed");
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
}
|
||||
});
|
||||
@@ -83,6 +101,9 @@ export function createAuthRouter(
|
||||
return;
|
||||
}
|
||||
const success = await neteaseProvider.loginWithSms(phone, code);
|
||||
if (success && cookieStore) {
|
||||
cookieStore.save("netease", neteaseProvider.getCookie());
|
||||
}
|
||||
res.json({ success });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: (err as Error).message });
|
||||
@@ -97,6 +118,10 @@ export function createAuthRouter(
|
||||
}
|
||||
const provider = getProvider(platform);
|
||||
provider.setCookie(cookie);
|
||||
const plat = platform === "qq" ? "qq" as const : "netease" as const;
|
||||
if (cookieStore) {
|
||||
cookieStore.save(plat, cookie);
|
||||
}
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ import type { MusicProvider } from "../music/provider.js";
|
||||
import type { BotDatabase } from "../data/database.js";
|
||||
import type { BotConfig } from "../data/config.js";
|
||||
import type { Logger } from "../logger.js";
|
||||
import type { CookieStore } from "../music/auth.js";
|
||||
import { createBotRouter } from "./api/bot.js";
|
||||
import { createMusicRouter } from "./api/music.js";
|
||||
import { createPlayerRouter } from "./api/player.js";
|
||||
@@ -21,6 +22,7 @@ export interface WebServerOptions {
|
||||
database: BotDatabase;
|
||||
config: BotConfig;
|
||||
logger: Logger;
|
||||
cookieStore?: CookieStore;
|
||||
staticDir?: string;
|
||||
}
|
||||
|
||||
@@ -47,7 +49,7 @@ export function createWebServer(options: WebServerOptions): WebServer {
|
||||
app.use("/api/player", createPlayerRouter(options.botManager, logger));
|
||||
app.use(
|
||||
"/api/auth",
|
||||
createAuthRouter(options.neteaseProvider, options.qqProvider, logger)
|
||||
createAuthRouter(options.neteaseProvider, options.qqProvider, logger, options.cookieStore)
|
||||
);
|
||||
|
||||
app.get("/api/health", (_req, res) => {
|
||||
|
||||
@@ -277,10 +277,13 @@ async function startQrLogin(platform: string) {
|
||||
|
||||
try {
|
||||
const res = await axios.post('/api/auth/qrcode', { platform });
|
||||
const { qrUrl, key } = res.data;
|
||||
const { qrUrl, qrImg, key } = res.data;
|
||||
qr.key = key;
|
||||
|
||||
// Generate QR code image
|
||||
// Use server-generated QR image if available, otherwise generate client-side
|
||||
if (qrImg) {
|
||||
qr.dataUrl = qrImg;
|
||||
} else {
|
||||
qr.dataUrl = await QRCode.toDataURL(qrUrl, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
@@ -289,6 +292,7 @@ async function startQrLogin(platform: string) {
|
||||
light: store.theme === 'dark' ? '#2a2a2a' : '#ffffff',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
qr.loading = false;
|
||||
|
||||
|
||||
Reference in new issue
Block a user