fix: show scannable login QR on mobile

This commit is contained in:
Rixuan Shao
2026-07-11 21:42:02 +08:00
parent a4657d3d65
commit 782cfb66d0
8 changed files with 163 additions and 11 deletions
+22
View File
@@ -997,6 +997,28 @@ def create_app():
if not accepted:
await websocket.close(code=1011)
@app.get("/login-desktop/qr")
async def login_desktop_qr(request: Request):
maybe_redirect = require_user(request)
if maybe_redirect:
return maybe_redirect
url = f"{login_desktop_api_url()}/qr"
try:
upstream_request = urllib.request.Request(url, method="GET")
content = await asyncio.to_thread(
lambda: urllib.request.urlopen(upstream_request, timeout=20).read()
)
return Response(
content=content,
media_type="image/png",
headers={"Cache-Control": "no-store, max-age=0"},
)
except urllib.error.HTTPError as exc:
status = 404 if exc.code == 404 else 502
return PlainTextResponse("login QR code is not ready", status_code=status)
except (urllib.error.URLError, TimeoutError):
return PlainTextResponse("login QR service is unavailable", status_code=502)
@app.get("/login-desktop/status")
async def login_desktop_status(request: Request):
maybe_redirect = require_user(request)
+25
View File
@@ -1071,6 +1071,31 @@ textarea {
gap: 12px;
}
.login-qr-panel {
display: grid;
justify-items: center;
gap: 10px;
padding: 14px;
border: 1px solid var(--border);
border-radius: 7px;
background: var(--surface-alt);
text-align: center;
}
.login-qr-image {
width: min(320px, 100%);
aspect-ratio: 1;
object-fit: contain;
padding: 10px;
border-radius: 8px;
background: #fff;
image-rendering: pixelated;
}
.login-qr-panel .button-row {
justify-content: center;
}
.desktop-frame-wrap {
min-width: 0;
overflow: hidden;
+34 -3
View File
@@ -344,7 +344,10 @@
);
const statusText = document.getElementById("login-desktop-status-text");
const frame = document.querySelector("[data-login-frame]");
const qrImage = document.querySelector("[data-login-qr]");
const qrStatus = document.querySelector("[data-login-qr-status]");
let timer = null;
let qrRefreshTimer = null;
const setStatus = (text, tone = "") => {
if (statusText) statusText.textContent = text;
@@ -380,6 +383,29 @@
}
};
const refreshLoginQr = async (delay = 0) => {
if (!qrImage) return;
window.clearTimeout(qrRefreshTimer);
qrRefreshTimer = window.setTimeout(async () => {
if (qrStatus) qrStatus.textContent = "正在读取登录二维码...";
const url = `/login-desktop/qr?t=${Date.now()}`;
try {
const response = await fetch(url, { credentials: "same-origin", cache: "no-store" });
if (!response.ok) throw new Error(String(response.status));
const blob = await response.blob();
const previous = qrImage.dataset.objectUrl || "";
const objectUrl = URL.createObjectURL(blob);
qrImage.src = objectUrl;
qrImage.dataset.objectUrl = objectUrl;
qrImage.hidden = false;
if (previous) URL.revokeObjectURL(previous);
if (qrStatus) qrStatus.textContent = "二维码已加载。如果过期,点击刷新。";
} catch {
if (qrStatus) qrStatus.textContent = "二维码还未准备好,请稍后刷新。";
}
}, delay);
};
const pollStatus = async () => {
if (document.visibilityState !== "visible" || (section && !section.open)) {
return;
@@ -417,18 +443,23 @@
try {
await postForm("/login-desktop/open");
loadFrame();
refreshLoginQr(1800);
if (!popup && frame) {
frame.scrollIntoView({ behavior: "smooth", block: "start" });
setStatus("???????????????????????");
setStatus("弹窗被浏览器拦截,已在当前页面加载登录工作区。");
} else {
setStatus("????????????????????????");
setStatus("请在登录工作区完成登录,然后返回此页保存登录态。");
}
} catch (error) {
setStatus(`??????????${error.message}`, "danger");
setStatus(`打开登录工作区失败:${error.message}`, "danger");
}
});
});
document.querySelectorAll("[data-refresh-login-qr]").forEach((button) => {
button.addEventListener("click", () => refreshLoginQr());
});
document.querySelectorAll(".login-desktop-save").forEach((button) => {
button.addEventListener("click", async () => {
try {
+14 -1
View File
@@ -210,7 +210,20 @@
<div class="login-actions">
<div class="login-status-panel">
<strong>登录状态</strong>
<p class="muted compact" id="login-desktop-status-text">???????????????????????????????????? 8788 ???</p>
<p class="muted compact" id="login-desktop-status-text">展开后检查登录桌面状态。手机端可直接查看下方的大图二维码。</p>
</div>
<div class="login-qr-panel">
<strong>手机扫码登录</strong>
<img class="login-qr-image" data-login-qr alt="抖音登录二维码" hidden>
<p class="muted compact" data-login-qr-status>点击“打开登录工作区”后会在这里显示大图二维码。</p>
<div class="button-row">
<button class="button button-soft" type="button" data-refresh-login-qr>
<i data-lucide="refresh-cw"></i><span>刷新二维码</span>
</button>
<a class="button button-quiet" href="/login-desktop/qr" target="_blank" rel="noopener">
<i data-lucide="maximize-2"></i><span>单独打开二维码</span>
</a>
</div>
</div>
<div class="button-row">
<button class="button button-primary login-desktop-open" type="button">