Files
deploy-test/pc-sdk-probe.sh
2026-04-21 12:24:04 +07:00

312 lines
13 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
cat <<'EOF'
在 Chrome DevTools Console 粘贴执行:
(() => {
const stamp = () => new Date().toISOString();
const counts = {};
if (!window.__deployTestWorkerWrapped) {
const NativeWorker = window.Worker;
window.Worker = function (url, options) {
console.log(`[probe ${stamp()}] new Worker`, { url: String(url), options });
const worker = new NativeWorker(url, options);
worker.addEventListener("error", (event) => {
console.error("[probe worker error]", {
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
});
});
worker.addEventListener("messageerror", (event) => {
console.error("[probe worker messageerror]", event);
});
worker.addEventListener("message", (event) => {
console.log(`[probe ${stamp()}] worker.message`, { url: String(url), data: event.data });
});
const originalPostMessage = worker.postMessage.bind(worker);
worker.postMessage = (...args) => {
console.log(`[probe ${stamp()}] worker.postMessage`, { url: String(url), args });
return originalPostMessage(...args);
};
return worker;
};
window.Worker.prototype = NativeWorker.prototype;
window.__deployTestWorkerWrapped = true;
console.log("[probe] Worker: wrapped; reload page after installing this probe to catch SDK worker creation and __absurd:spawn-idb-worker");
}
const wrap = (name) => {
const original = window[name];
if (typeof original !== "function") {
console.log(`[probe] ${name}:`, typeof original);
return;
}
counts[name] = 0;
const wrapped = function (...args) {
counts[name] += 1;
console.log(`[probe ${stamp()}] ${name} called`, args);
try {
const ret = original.apply(this, args);
console.log(`[probe ${stamp()}] ${name} returned`, ret);
return ret;
} catch (err) {
console.error(`[probe ${stamp()}] ${name} threw`, err);
throw err;
}
};
wrapped.__deployTestProbe = true;
wrapped.__deployTestOriginal = original;
window[name] = wrapped;
console.log(`[probe] ${name}: wrapped`);
};
["initSDK", "login", "logout", "commonEventFunc", "initDB", "setSqlWasmPath"].forEach(wrap);
window.addEventListener("error", (event) => {
console.error("[probe window error]", event.message, event.filename, event.lineno, event.error);
});
window.addEventListener("unhandledrejection", (event) => {
console.error("[probe unhandledrejection]", event.reason);
});
console.log("[probe ready]", {
electronAPI: Boolean(window.electronAPI),
href: window.location.href,
isSecureContext: window.isSecureContext,
crossOriginIsolated: window.crossOriginIsolated,
SharedArrayBuffer: typeof window.SharedArrayBuffer,
Go: typeof Go,
initSDK: typeof window.initSDK,
login: typeof window.login,
logout: typeof window.logout,
commonEventFunc: typeof window.commonEventFunc,
initDB: typeof window.initDB,
setSqlWasmPath: typeof window.setSqlWasmPath,
openIMRenderApi: typeof window.openIMRenderApi,
});
window.__deployTestProbeCounts = counts;
window.__deployTestReadLocalForage = async (key) => {
const tryRead = (dbName, storeName) => new Promise((resolve, reject) => {
const req = indexedDB.open(dbName);
req.onerror = () => reject(req.error);
req.onsuccess = () => {
const db = req.result;
if (!Array.from(db.objectStoreNames || []).includes(storeName)) {
db.close();
resolve(undefined);
return;
}
const tx = db.transaction(storeName, "readonly");
const store = tx.objectStore(storeName);
const getReq = store.get(key);
getReq.onerror = () => reject(getReq.error);
getReq.onsuccess = () => {
db.close();
resolve(getReq.result);
};
};
});
for (const dbName of ["localforage", "OpenCorp-Base"]) {
for (const storeName of ["keyvaluepairs", "local-forage-detect-blob-support"]) {
try {
const value = await tryRead(dbName, storeName);
if (value) return value;
} catch (err) {
console.warn(`[probe] read ${dbName}/${storeName}/${key} failed`, err);
}
}
}
return undefined;
};
window.__deployTestAuthState = async () => {
const state = {
href: window.location.href,
hash: window.location.hash,
isSecureContext: window.isSecureContext,
crossOriginIsolated: window.crossOriginIsolated,
SharedArrayBuffer: typeof window.SharedArrayBuffer,
electronAPI: Boolean(window.electronAPI),
Go: typeof Go,
initSDK: typeof window.initSDK,
login: typeof window.login,
logout: typeof window.logout,
initDB: typeof window.initDB,
setSqlWasmPath: typeof window.setSqlWasmPath,
userID: await window.__deployTestReadLocalForage("IM_USERID"),
hasIMToken: Boolean(await window.__deployTestReadLocalForage("IM_TOKEN")),
hasChatToken: Boolean(await window.__deployTestReadLocalForage("IM_CHAT_TOKEN")),
counts: { ...counts },
};
console.log("[probe] auth state", state);
console.log("[probe] auth state json", JSON.stringify(state, null, 2));
return state;
};
if (!window.__deployTestXHRWrapped) {
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url, ...args) {
this.__deployTestRequest = { method, url, startedAt: Date.now() };
return originalOpen.call(this, method, url, ...args);
};
XMLHttpRequest.prototype.send = function (...args) {
const req = this.__deployTestRequest;
if (req) {
this.addEventListener("loadend", () => {
if (String(req.url).includes("/account/login") || String(req.url).includes("/auth/get_user_token")) {
console.log("[probe xhr]", {
method: req.method,
url: req.url,
status: this.status,
ms: Date.now() - req.startedAt,
response: String(this.responseText || "").slice(0, 500),
});
}
});
}
return originalSend.apply(this, args);
};
window.__deployTestXHRWrapped = true;
console.log("[probe] XMLHttpRequest: wrapped");
}
window.__deployTestListBrowserDBs = async () => {
const dbs = indexedDB.databases ? await indexedDB.databases() : [];
console.log("[probe] indexedDB databases", dbs);
if (window.caches?.keys) {
console.log("[probe] cache keys", await caches.keys());
}
return dbs;
};
window.__deployTestResetBrowserStorage = async () => {
console.warn("[probe] 清理当前站点浏览器存储localStorage / CacheStorage / IndexedDB");
localStorage.clear();
if (window.caches?.keys) {
for (const key of await caches.keys()) {
console.warn("[probe] delete cache", key, await caches.delete(key));
}
}
if (indexedDB.databases) {
for (const db of await indexedDB.databases()) {
if (!db.name) continue;
await new Promise((resolve) => {
const req = indexedDB.deleteDatabase(db.name);
req.onsuccess = () => {
console.warn("[probe] deleted indexedDB", db.name);
resolve();
};
req.onerror = () => {
console.warn("[probe] delete indexedDB failed", db.name, req.error);
resolve();
};
req.onblocked = () => {
console.warn("[probe] delete indexedDB blocked, close other tabs then retry", db.name);
resolve();
};
});
}
} else {
console.warn("[probe] indexedDB.databases() 不可用,请在 DevTools Application 面板手动清理 IndexedDB");
}
console.warn("[probe] 清理完成,请关闭其它同源标签页后刷新页面并重新登录");
};
window.__deployTestManualLogin = async ({ userID, token, apiAddr, wsAddr, dataDir = "./" } = {}) => {
userID = userID || await window.__deployTestReadLocalForage("IM_USERID");
token = token || await window.__deployTestReadLocalForage("IM_TOKEN");
const operationID = `deploy-test-${Date.now()}`;
const gatewayOrigin = window.location.origin || "https://pc-jack.imharry.work";
const config = {
platformID: 5,
apiAddr: apiAddr || `${gatewayOrigin}/api/im`,
wsAddr: wsAddr || `${gatewayOrigin.replace(/^http/, "ws")}/msg_gateway`,
dataDir: String(dataDir),
logLevel: 5,
isLogStandardOutput: true,
logFilePath: "./",
isExternalExtensions: false,
};
console.log(`[probe ${stamp()}] manual initSDK`, { operationID, config });
window.initSDK(operationID, JSON.stringify(config));
console.log(`[probe ${stamp()}] manual login start`, { operationID, userID, hasToken: Boolean(token) });
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error("manual window.login timeout after 15000ms")), 15000);
});
const result = await Promise.race([window.login(operationID, String(userID || ""), String(token || "")), timeout]);
console.log(`[probe ${stamp()}] manual login result`, result);
return result;
};
window.__deployTestManualInitDB = async (userID, dataDir = "./") => {
userID = userID || await window.__deployTestReadLocalForage("IM_USERID") || "6087132211";
console.log(`[probe ${stamp()}] manual setSqlWasmPath`, "/sql-wasm.wasm");
if (typeof window.setSqlWasmPath === "function") {
try {
const setPathTimeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error("manual window.setSqlWasmPath timeout after 5000ms")), 5000);
});
await Promise.race([window.setSqlWasmPath("/sql-wasm.wasm"), setPathTimeout]);
console.log(`[probe ${stamp()}] manual setSqlWasmPath done`);
} catch (err) {
console.error(`[probe ${stamp()}] manual setSqlWasmPath failed`, err);
}
}
console.log(`[probe ${stamp()}] manual initDB start`, { userID, dataDir });
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error("manual window.initDB timeout after 15000ms")), 15000);
});
const result = await Promise.race([window.initDB(String(userID), String(dataDir)), timeout]);
console.log(`[probe ${stamp()}] manual initDB result`, result);
return result;
};
clearInterval(window.__deployTestProbeTimer);
window.__deployTestProbeTimer = setInterval(() => {
const snapshot = { ...counts };
console.log(`[probe ${stamp()}] counts`, snapshot);
if (!snapshot.initSDK && !snapshot.login) {
console.warn("[probe hint] initSDK/login 仍为 0当前页面没有重新触发 SDK 登录。若 logout > 0 且报 10008只说明业务层在未初始化 SDK 时触发了退出。请执行 await window.__deployTestAuthState();若仍有 token可执行 await window.__deployTestManualLogin() 手动验证 SDK 初始化链路。");
}
}, 5000);
})();
然后退出登录/刷新页面重新登录一次,观察是否打印:
- new Worker / worker.postMessage / worker error
- initSDK / login / logout / initDB / setSqlWasmPath called / returned
- window error / unhandledrejection
- counts 里的 initSDK/login 是否从 0 变成 1
如果点登录后 counts 仍为 0可用当前登录 token 手动测:
window.__deployTestManualLogin()
如果自动读取 token 失败,再手动传入:
window.__deployTestManualLogin({
userID: "6087132211",
token: "把 console 里 SDK login args 的 token 粘到这里"
})
若默认 dataDir="./" 仍报 initDB timeout可验证是否是浏览器 IDBFS 路径问题:
await window.__deployTestManualLogin({ dataDir: "" })
await window.__deployTestManualInitDB(undefined, "")
若报 10006 init database invoke javascript timeout先查看并清理当前站点浏览器数据库
await window.__deployTestListBrowserDBs()
await window.__deployTestResetBrowserStorage()
也可以单独测试 initDB
await window.__deployTestManualInitDB()
清理后按这个顺序重测:
1. 关闭其它 https://pc-jack.imharry.work/ 标签页
2. 打开 https://pc-jack.imharry.work/ 并刷新页面
3. 重新粘贴本脚本输出的整段 JS刷新会清掉已安装的 probe
4. 再登录
5. 观察 counts 是否从 0 变 1以及 /var/log/nginx/openim-pc-proxy-access.log 是否出现 /msg_gateway
如果 counts 仍为 0查看业务登录链路状态
await window.__deployTestAuthState()
HTTPS 域名入口下 isSecureContext / crossOriginIsolated 应为 trueSharedArrayBuffer 应为 "function";同时观察 SDK 方法是否存在、login 是否返回成功,以及 /msg_gateway 是否建立连接。
EOF