mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-08 23:33:16 +08:00
Fix WhatsApp Admin QR bind and show the scan prompt only after Bind.
Use a current WA protocol version, start the sidecar via local tsx with inherited proxy, and stop auto-opening the QR UI on unbound connecting. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
5c6833612f
commit
9cf026f605
12 changed files with 593 additions and 101 deletions
|
|
@ -5,9 +5,9 @@
|
|||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>oliver</title>
|
||||
<!-- Avoid render-blocking external fonts (can stall first paint ~30s on blocked networks). -->
|
||||
<link rel="stylesheet" href="/admin/assets/styles.css?v=20260817-wa6" />
|
||||
<link rel="stylesheet" href="/admin/assets/theme-deepseek.css?v=20260817-wa6" />
|
||||
<link rel="stylesheet" href="/admin/assets/theme-presets.css?v=20260817-wa6" />
|
||||
<link rel="stylesheet" href="/admin/assets/styles.css?v=20260817-wa12" />
|
||||
<link rel="stylesheet" href="/admin/assets/theme-deepseek.css?v=20260817-wa12" />
|
||||
<link rel="stylesheet" href="/admin/assets/theme-presets.css?v=20260817-wa12" />
|
||||
<style>
|
||||
/* First paint before app.js runs: keep admin from showing a blank dark screen. */
|
||||
.admin-boot-splash {
|
||||
|
|
@ -129,33 +129,33 @@
|
|||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"/admin/assets/app.js": "/admin/assets/app.js?v=20260817-wa6",
|
||||
"/admin/assets/js/asset-v.js": "/admin/assets/js/asset-v.js?v=20260817-wa6",
|
||||
"/admin/assets/js/core.js": "/admin/assets/js/core.js?v=20260817-wa6",
|
||||
"/admin/assets/js/i18n/index.js": "/admin/assets/js/i18n/index.js?v=20260817-wa6",
|
||||
"/admin/assets/js/i18n/messages-en.js": "/admin/assets/js/i18n/messages-en.js?v=20260817-wa6",
|
||||
"/admin/assets/js/i18n/messages-zh.js": "/admin/assets/js/i18n/messages-zh.js?v=20260817-wa6",
|
||||
"/admin/assets/js/main.js": "/admin/assets/js/main.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/attachments.js": "/admin/assets/js/pages/attachments.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/audit.js": "/admin/assets/js/pages/audit.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/authz.js": "/admin/assets/js/pages/authz.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/legacy-admin.js": "/admin/assets/js/pages/legacy-admin.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/legacy.js": "/admin/assets/js/pages/legacy.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/login.js": "/admin/assets/js/pages/login.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/memory.js": "/admin/assets/js/pages/memory.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/models.js": "/admin/assets/js/pages/models.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/plugins.js": "/admin/assets/js/pages/plugins.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/profile.js": "/admin/assets/js/pages/profile.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/scheduled-jobs.js": "/admin/assets/js/pages/scheduled-jobs.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/skills.js": "/admin/assets/js/pages/skills.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/stack.js": "/admin/assets/js/pages/stack.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/users.js": "/admin/assets/js/pages/users.js?v=20260817-wa6",
|
||||
"/admin/assets/js/pages/workspace-paths.js": "/admin/assets/js/pages/workspace-paths.js?v=20260817-wa6",
|
||||
"/admin/assets/js/router.js": "/admin/assets/js/router.js?v=20260817-wa6",
|
||||
"/admin/assets/js/state.js": "/admin/assets/js/state.js?v=20260817-wa6"
|
||||
"/admin/assets/app.js": "/admin/assets/app.js?v=20260817-wa12",
|
||||
"/admin/assets/js/asset-v.js": "/admin/assets/js/asset-v.js?v=20260817-wa12",
|
||||
"/admin/assets/js/core.js": "/admin/assets/js/core.js?v=20260817-wa12",
|
||||
"/admin/assets/js/i18n/index.js": "/admin/assets/js/i18n/index.js?v=20260817-wa12",
|
||||
"/admin/assets/js/i18n/messages-en.js": "/admin/assets/js/i18n/messages-en.js?v=20260817-wa12",
|
||||
"/admin/assets/js/i18n/messages-zh.js": "/admin/assets/js/i18n/messages-zh.js?v=20260817-wa12",
|
||||
"/admin/assets/js/main.js": "/admin/assets/js/main.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/attachments.js": "/admin/assets/js/pages/attachments.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/audit.js": "/admin/assets/js/pages/audit.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/authz.js": "/admin/assets/js/pages/authz.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/legacy-admin.js": "/admin/assets/js/pages/legacy-admin.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/legacy.js": "/admin/assets/js/pages/legacy.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/login.js": "/admin/assets/js/pages/login.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/memory.js": "/admin/assets/js/pages/memory.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/models.js": "/admin/assets/js/pages/models.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/plugins.js": "/admin/assets/js/pages/plugins.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/profile.js": "/admin/assets/js/pages/profile.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/scheduled-jobs.js": "/admin/assets/js/pages/scheduled-jobs.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/skills.js": "/admin/assets/js/pages/skills.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/stack.js": "/admin/assets/js/pages/stack.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/users.js": "/admin/assets/js/pages/users.js?v=20260817-wa12",
|
||||
"/admin/assets/js/pages/workspace-paths.js": "/admin/assets/js/pages/workspace-paths.js?v=20260817-wa12",
|
||||
"/admin/assets/js/router.js": "/admin/assets/js/router.js?v=20260817-wa12",
|
||||
"/admin/assets/js/state.js": "/admin/assets/js/state.js?v=20260817-wa12"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="/admin/assets/app.js?v=20260817-wa6" onerror="(function(){var lang='zh';try{lang=String(localStorage.getItem('ops_admin_lang')||'zh').toLowerCase();}catch(e){}if(lang!=='en')lang='zh';var msg=lang==='en'?{t:'Failed to load admin scripts',h:'Confirm /admin/assets/app.js and /admin/assets/js/ are reachable.'}:{t:'无法加载管理台脚本',h:'请确认 /admin/assets/app.js 与 /admin/assets/js/ 可访问。'};var s=document.getElementById('admin-boot-splash');if(s){var a=s.querySelector('.admin-boot-splash__title span:last-child');var b=s.querySelector('.admin-boot-splash__muted');if(a)a.textContent=msg.t;if(b)b.textContent=msg.h;}})()"></script>
|
||||
<script type="module" src="/admin/assets/app.js?v=20260817-wa12" onerror="(function(){var lang='zh';try{lang=String(localStorage.getItem('ops_admin_lang')||'zh').toLowerCase();}catch(e){}if(lang!=='en')lang='zh';var msg=lang==='en'?{t:'Failed to load admin scripts',h:'Confirm /admin/assets/app.js and /admin/assets/js/ are reachable.'}:{t:'无法加载管理台脚本',h:'请确认 /admin/assets/app.js 与 /admin/assets/js/ 可访问。'};var s=document.getElementById('admin-boot-splash');if(s){var a=s.querySelector('.admin-boot-splash__title span:last-child');var b=s.querySelector('.admin-boot-splash__muted');if(a)a.textContent=msg.t;if(b)b.textContent=msg.h;}})()"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
* - interfaces/admin/static/index.html importmap + CSS/JS ?v=
|
||||
* - interfaces/admin/static/app.js entry query (optional if importmap covers it)
|
||||
*/
|
||||
export const ADMIN_ASSET_V = "20260812-22";
|
||||
export const ADMIN_ASSET_V = "20260817-wa12";
|
||||
|
|
|
|||
|
|
@ -793,9 +793,9 @@ export default {
|
|||
"wa.sessionUnbindConfirm": "Stop sidecar and delete local WhatsApp auth (state/auth). You must scan again to rebind. Continue?",
|
||||
"wa.sessionRebindConfirm": "Clear the old session and show a new QR code. Continue?",
|
||||
"wa.sessionNotInstalled": "Sidecar not installed. Run whatsapp_install.ps1 first.",
|
||||
"wa.sessionScanHint": "On your phone: WhatsApp → Linked devices → Link a device, then scan the QR below.",
|
||||
"wa.sessionScanHint": "On your phone: WhatsApp → Linked devices → Link a device, then scan the QR below. Codes expire in about 20s; this page auto-refreshes to the latest one.",
|
||||
"wa.sessionQrWaiting": "Waiting for QR…",
|
||||
"wa.sessionQrStale": "QR may be expired. Click Scan to bind again.",
|
||||
"wa.sessionQrStale": "This QR may have expired. Waiting for a new one…",
|
||||
"wa.lifecycle.unbound": "Not bound",
|
||||
"wa.lifecycle.bound_stopped": "Bound (sidecar stopped)",
|
||||
"wa.lifecycle.bound_offline": "Bound (offline / reconnecting)",
|
||||
|
|
@ -806,6 +806,9 @@ export default {
|
|||
"wa.lifecycle.needs_rebind": "Session invalid (re-scan required)",
|
||||
"wa.sessionPolling": "Refreshing status…",
|
||||
"wa.sessionNeedsRebind": "WhatsApp rejected the stored session (e.g. disconnect=405). Click Re-scan bind, or remove the old oclaw linked device on your phone first.",
|
||||
"wa.sessionPairingFail": "WhatsApp rejected pairing before a QR (often 405). Retrying with fresh keys; if it keeps failing, check proxy/DNS then click Scan to bind.",
|
||||
"wa.sessionNetworkFail": "Cannot reach WhatsApp Web (certificate/timeout/ECONNRESET). Clash fake-ip is a common cause. Set HTTPS_PROXY or AIA_WHATSAPP_PROXY_URL to your local mixed-port proxy, then start the sidecar again.",
|
||||
"wa.sessionRunnerWaiting": "Sidecar process is up, but Baileys has not written status yet (npx hang or missing proxy). Wait a bit, or check data/logs/whatsapp_sidecar.log.",
|
||||
"wecom.autoBind": "Auto bind on first message",
|
||||
"wecom.autoBindRole": "Auto bind role",
|
||||
"wecom.autoBindTenant": "Auto bind team",
|
||||
|
|
|
|||
|
|
@ -793,9 +793,9 @@ export default {
|
|||
"wa.sessionUnbindConfirm": "将停止 sidecar 并删除本机 WhatsApp 登录态(state/auth)。之后需重新扫码绑定,确认继续?",
|
||||
"wa.sessionRebindConfirm": "将清除旧登录态并重新出二维码,确认继续?",
|
||||
"wa.sessionNotInstalled": "sidecar 未安装,请先运行 whatsapp_install.ps1",
|
||||
"wa.sessionScanHint": "请用手机 WhatsApp → 已关联的设备 → 关联设备,扫描下方二维码。",
|
||||
"wa.sessionScanHint": "请用手机 WhatsApp → 已关联的设备 → 关联设备,扫描下方二维码。码大约 20 秒过期,页面会自动换成新码,请扫最新一张。",
|
||||
"wa.sessionQrWaiting": "等待二维码…",
|
||||
"wa.sessionQrStale": "二维码可能已过期,请点「扫码绑定」刷新。",
|
||||
"wa.sessionQrStale": "当前码可能已过期,正在自动刷新新码…",
|
||||
"wa.lifecycle.unbound": "未绑定",
|
||||
"wa.lifecycle.bound_stopped": "已绑定(sidecar 已停)",
|
||||
"wa.lifecycle.bound_offline": "已绑定(离线重连中)",
|
||||
|
|
@ -806,6 +806,9 @@ export default {
|
|||
"wa.lifecycle.needs_rebind": "登录态失效(需重新扫码)",
|
||||
"wa.sessionPolling": "正在刷新状态…",
|
||||
"wa.sessionNeedsRebind": "WhatsApp 拒绝了当前登录态(disconnect=405 等)。请点「重新扫码绑定」,或在手机端删除旧的 oclaw 关联设备后再扫。",
|
||||
"wa.sessionPairingFail": "WhatsApp 在出码前拒绝了配对(常见 405)。正在换新密钥重试;若持续失败,请检查代理/DNS 后点「扫码绑定」。",
|
||||
"wa.sessionNetworkFail": "连不上 WhatsApp Web(证书/超时/ECONNRESET)。常见原因是 Clash fake-ip。请给 sidecar 设置 HTTPS_PROXY 或 AIA_WHATSAPP_PROXY_URL 指向本机混合端口后再启动。",
|
||||
"wa.sessionRunnerWaiting": "sidecar 进程已起来,但 Baileys 还没写出状态(常见于 npx 卡住或无代理)。请稍候,或看 data/logs/whatsapp_sidecar.log。",
|
||||
"wecom.autoBind": "首条消息自动绑定",
|
||||
"wecom.autoBindRole": "自动绑定角色",
|
||||
"wecom.autoBindTenant": "自动绑定团队",
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ async function renderStack() {
|
|||
const whatsappDispatchCard = createChannelDispatchCard("whatsapp", "WhatsApp dispatch", whatsappDispatchResp || {});
|
||||
let waSessionLatest = whatsappSessionResp && typeof whatsappSessionResp === "object" ? whatsappSessionResp : {};
|
||||
let waSessionBindInProgress = false;
|
||||
let waSessionBindRequested = false;
|
||||
let waSessionWatchGen = 0;
|
||||
let waLastQrKey = "";
|
||||
const waSessionHintAlert = el("div", { class: "alert alert--warning u-hidden", text: "" });
|
||||
const paintWaSession = (st) => {
|
||||
const s = st && typeof st === "object" ? st : {};
|
||||
|
|
@ -109,46 +112,74 @@ async function renderStack() {
|
|||
reconnect: Number(s.reconnect_attempt || 0),
|
||||
});
|
||||
waSessionStatus.replaceChildren(...Array.from(chips.childNodes));
|
||||
const showDisconnect = lifecycle !== "unbound" && lifecycle !== "awaiting_scan";
|
||||
const errText = String(s.last_error || "");
|
||||
const discStatus = Number(s.last_disconnect_status || 0);
|
||||
const pairingFail =
|
||||
lifecycle === "connecting" &&
|
||||
(errText.includes("pairing_rejected") || discStatus === 405 || /connection failure/i.test(errText));
|
||||
const networkFail =
|
||||
lifecycle === "connecting" &&
|
||||
/certificate|econnreset|timedOut|408|fake-ip|198\.18/i.test(`${errText} ${String(s.last_disconnect_reason || "")}`);
|
||||
const runnerWaiting =
|
||||
lifecycle === "connecting" &&
|
||||
/runner_waiting|runner_starting/i.test(errText);
|
||||
const detailBits = [
|
||||
s.display_name ? `name=${s.display_name}` : "",
|
||||
s.me ? `me=${s.me}` : "",
|
||||
s.last_error ? `err=${String(s.last_error).slice(0, 120)}` : "",
|
||||
s.last_disconnect_reason ? `disconnect=${s.last_disconnect_reason}` : "",
|
||||
showDisconnect && errText ? `err=${errText.slice(0, 120)}` : "",
|
||||
showDisconnect && s.last_disconnect_reason ? `disconnect=${s.last_disconnect_reason}` : "",
|
||||
s.log_path ? `log=${s.log_path}` : "",
|
||||
s.bridge_updated_at ? `updated=${s.bridge_updated_at}` : "",
|
||||
].filter(Boolean);
|
||||
waSessionDetail.textContent = detailBits.join(" | ") || (s.installed ? "" : t("wa.sessionNotInstalled"));
|
||||
if (lifecycle === "needs_rebind" || lifecycle === "logged_out") {
|
||||
waSessionHintAlert.textContent = t("wa.sessionNeedsRebind");
|
||||
waSessionHintAlert.classList.remove("u-hidden");
|
||||
} else if (waSessionBindRequested && runnerWaiting) {
|
||||
waSessionHintAlert.textContent = t("wa.sessionRunnerWaiting");
|
||||
waSessionHintAlert.classList.remove("u-hidden");
|
||||
} else if (waSessionBindRequested && pairingFail) {
|
||||
waSessionHintAlert.textContent = t("wa.sessionPairingFail");
|
||||
waSessionHintAlert.classList.remove("u-hidden");
|
||||
} else if (waSessionBindRequested && networkFail) {
|
||||
waSessionHintAlert.textContent = t("wa.sessionNetworkFail");
|
||||
waSessionHintAlert.classList.remove("u-hidden");
|
||||
} else {
|
||||
waSessionHintAlert.textContent = "";
|
||||
waSessionHintAlert.classList.add("u-hidden");
|
||||
}
|
||||
const qrUrl = String(s.qr_data_url || "").trim();
|
||||
const awaiting = lifecycle === "awaiting_scan" || waSessionBindInProgress;
|
||||
const awaiting = Boolean(waSessionBindRequested) && (lifecycle === "awaiting_scan" || waSessionBindInProgress);
|
||||
waSessionQrWrap.style.display = awaiting ? "" : "none";
|
||||
waSessionQrHint.textContent = awaiting
|
||||
? (s.qr_stale ? t("wa.sessionQrStale") : t("wa.sessionScanHint"))
|
||||
: "";
|
||||
const qrKey = `${String(s.qr || "").slice(0, 48)}|${s.bridge_updated_at || ""}|${qrUrl.length}`;
|
||||
if (awaiting && qrUrl) {
|
||||
waSessionQrImg.src = qrUrl;
|
||||
if (qrKey !== waLastQrKey) {
|
||||
waLastQrKey = qrKey;
|
||||
waSessionQrImg.src = qrUrl;
|
||||
}
|
||||
waSessionQrImg.style.display = "";
|
||||
waSessionQrWaiting.style.display = "none";
|
||||
} else if (awaiting) {
|
||||
waLastQrKey = "";
|
||||
waSessionQrImg.removeAttribute("src");
|
||||
waSessionQrImg.style.display = "none";
|
||||
waSessionQrWaiting.style.display = "";
|
||||
waSessionQrWaiting.textContent = t("wa.sessionQrWaiting");
|
||||
} else {
|
||||
waLastQrKey = "";
|
||||
waSessionQrImg.removeAttribute("src");
|
||||
waSessionQrImg.style.display = "none";
|
||||
waSessionQrWaiting.style.display = "none";
|
||||
}
|
||||
const bindModes = new Set(["unbound", "logged_out", "awaiting_scan", "needs_rebind"]);
|
||||
const rebindModes = new Set(["needs_rebind", "logged_out", "bound_stopped", "bound_offline", "online"]);
|
||||
if (waSessionBindBtn) {
|
||||
waSessionBindBtn.textContent = bindModes.has(lifecycle)
|
||||
? t("wa.sessionBind")
|
||||
: t("wa.sessionRebind");
|
||||
waSessionBindBtn.textContent = rebindModes.has(lifecycle)
|
||||
? t("wa.sessionRebind")
|
||||
: t("wa.sessionBind");
|
||||
waSessionBindBtn.disabled = Boolean(waSessionBindInProgress);
|
||||
}
|
||||
};
|
||||
|
|
@ -175,12 +206,14 @@ async function renderStack() {
|
|||
const stepMs = Number(opts.stepMs || 2000);
|
||||
const stopWhen = opts.stopWhen || ((st) => {
|
||||
const life = String((st && st.lifecycle) || "");
|
||||
return ["online", "awaiting_scan", "needs_rebind", "logged_out", "bound_stopped", "unbound"].includes(life);
|
||||
return ["online", "needs_rebind", "logged_out", "bound_stopped", "unbound"].includes(life);
|
||||
});
|
||||
waSessionActionStatus.textContent = t("wa.sessionPolling");
|
||||
let waited = 0;
|
||||
let latest = waSessionLatest;
|
||||
const watchGen = Number(opts.gen || 0);
|
||||
while (waited < maxMs) {
|
||||
if (watchGen && watchGen !== waSessionWatchGen) return latest;
|
||||
const st = await apiGet("/admin/api/whatsapp/session");
|
||||
paintWaSession(st);
|
||||
latest = st;
|
||||
|
|
@ -229,32 +262,59 @@ async function renderStack() {
|
|||
class: "btn",
|
||||
text: t("wa.sessionStop"),
|
||||
onclick: async () => {
|
||||
waSessionBindRequested = false;
|
||||
await withWaSessionAction(async () => apiPost("/admin/api/whatsapp/session/stop", { force: true }), { poll: false });
|
||||
},
|
||||
});
|
||||
const waSessionBindBtnClick = async () => {
|
||||
const curLife = String((waSessionLatest && waSessionLatest.lifecycle) || "").trim();
|
||||
const autoClear = ["needs_rebind", "logged_out"].includes(curLife);
|
||||
const needClear = autoClear || (curLife && !["unbound", "awaiting_scan"].includes(curLife));
|
||||
if (needClear && !autoClear && !window.confirm(t("wa.sessionRebindConfirm"))) return;
|
||||
const watchWaBindUntilOnline = async () => {
|
||||
const gen = ++waSessionWatchGen;
|
||||
waSessionBindRequested = true;
|
||||
waSessionBindInProgress = true;
|
||||
paintWaSession(waSessionLatest);
|
||||
try {
|
||||
await withWaSessionAction(async () => apiPost("/admin/api/whatsapp/session/bind", {
|
||||
clear_auth: Boolean(needClear),
|
||||
}), {
|
||||
poll: {
|
||||
maxMs: 120000,
|
||||
stopWhen: (st) => {
|
||||
const life = String((st && st.lifecycle) || "");
|
||||
if (life === "online") return true;
|
||||
return life === "awaiting_scan" && Boolean(st && (st.qr_data_url || st.qr));
|
||||
},
|
||||
await pollWaSession({
|
||||
maxMs: 10 * 60 * 1000,
|
||||
stepMs: 1500,
|
||||
gen,
|
||||
stopWhen: (st) => {
|
||||
const life = String((st && st.lifecycle) || "");
|
||||
if (life === "online") return true;
|
||||
if (life === "bound_stopped") return true;
|
||||
return false;
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
if (gen === waSessionWatchGen) {
|
||||
waSessionBindInProgress = false;
|
||||
const life = String((waSessionLatest && waSessionLatest.lifecycle) || "");
|
||||
if (life === "online" || life === "unbound" || life === "bound_stopped") {
|
||||
waSessionBindRequested = false;
|
||||
}
|
||||
paintWaSession(waSessionLatest);
|
||||
}
|
||||
}
|
||||
};
|
||||
const waSessionBindBtnClick = async () => {
|
||||
const curLife = String((waSessionLatest && waSessionLatest.lifecycle) || "").trim();
|
||||
const autoClear = ["needs_rebind", "logged_out"].includes(curLife);
|
||||
const needClear = autoClear || ["bound_stopped", "bound_offline", "online"].includes(curLife);
|
||||
if (needClear && !autoClear && !window.confirm(t("wa.sessionRebindConfirm"))) return;
|
||||
waSessionBindRequested = true;
|
||||
waSessionBindInProgress = true;
|
||||
waLastQrKey = "";
|
||||
paintWaSession(waSessionLatest);
|
||||
const watch = watchWaBindUntilOnline();
|
||||
try {
|
||||
await withWaSessionAction(async () => apiPost("/admin/api/whatsapp/session/bind", {
|
||||
clear_auth: Boolean(needClear),
|
||||
}), { poll: false });
|
||||
await watch;
|
||||
} catch (err) {
|
||||
waSessionWatchGen += 1;
|
||||
waSessionBindInProgress = false;
|
||||
waSessionBindRequested = false;
|
||||
paintWaSession(waSessionLatest);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
waSessionBindBtn.onclick = waSessionBindBtnClick;
|
||||
|
|
@ -263,6 +323,7 @@ async function renderStack() {
|
|||
text: t("wa.sessionUnbind"),
|
||||
onclick: async () => {
|
||||
if (!window.confirm(t("wa.sessionUnbindConfirm"))) return;
|
||||
waSessionBindRequested = false;
|
||||
await withWaSessionAction(async () => apiPost("/admin/api/whatsapp/session/unbind", {}), { poll: false });
|
||||
},
|
||||
});
|
||||
|
|
@ -289,7 +350,7 @@ async function renderStack() {
|
|||
paintWaSession(whatsappSessionResp || {});
|
||||
if (whatsappSessionResp && whatsappSessionResp.sidecar_running) {
|
||||
const initLife = String(whatsappSessionResp.lifecycle || "");
|
||||
if (["connecting", "bound_offline"].includes(initLife)) {
|
||||
if (initLife === "bound_offline" || initLife === "online") {
|
||||
void pollWaSession({ maxMs: 30000 });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue