117 lines
5.2 KiB
JavaScript
117 lines
5.2 KiB
JavaScript
// embed(iframe)模式:父页面经 postMessage 推送 token → 进入应用;401 后重签。
|
||
// wecom 变体(?embed=1&wecom=1):token 来自 /v1/wecom/entry 回调的 URL fragment,
|
||
// 401 时 embedWecomRelogin 重定向回 entry 静默重签(企微客户端内无感)。
|
||
// app 变体(?embed=1&relogin_url=...):token 同样走 fragment,401 时跳 relogin_url
|
||
// (原生壳拦自定义 scheme 换新 token 重进,或 H5 登录页静默重签)。
|
||
// 顶层无副作用,boot 决定是否调 embedInit。导出 embedInit(boot 调)+
|
||
// embedPostToParent / embedShowWaiting / embedWecomRelogin / embedAppRelogin(auth 的 logout 用)。
|
||
import { state, LS_TOKEN, EMBED_WECOM, EMBED_PARENT_ORIGIN, EMBED_RELOGIN_URL, EMBED_INITIAL_TASK_ID, setIdentity } from "./state.js";
|
||
import { $ } from "./dom.js";
|
||
import { enterApp } from "./main.js";
|
||
import { loadTaskList } from "./chat.js";
|
||
|
||
// embed 首个 task 自动定位的一次性标志(仅 embed 段使用)
|
||
let _embedInitialTaskHandled = false;
|
||
|
||
// ───── embed mode ─────
|
||
export function embedPostToParent(msg) {
|
||
if (!EMBED_PARENT_ORIGIN || window.parent === window) return;
|
||
try { window.parent.postMessage(msg, EMBED_PARENT_ORIGIN); } catch (e) {}
|
||
}
|
||
export function embedShowWaiting(text, isErr) {
|
||
const w = $("embed-waiting");
|
||
if (!w) return;
|
||
if (isErr) {
|
||
w.querySelector(".text").textContent = "";
|
||
w.querySelector(".err").textContent = text || "";
|
||
w.querySelector(".spinner").style.display = "none";
|
||
} else {
|
||
w.querySelector(".text").textContent = text || "等待登录…";
|
||
w.querySelector(".err").textContent = "";
|
||
w.querySelector(".spinner").style.display = "";
|
||
}
|
||
}
|
||
function embedHandleMessage(e) {
|
||
if (e.origin !== EMBED_PARENT_ORIGIN) return;
|
||
const d = e.data || {};
|
||
if (d.type === "zcbot-token" && d.token && d.user_id) {
|
||
state.token = d.token;
|
||
localStorage.setItem(LS_TOKEN, state.token);
|
||
// 父页面可带 name/user_name/email;缺的由 enterApp → loadRole 拉 /v1/me 补齐
|
||
setIdentity({ user_id: d.user_id, name: d.name, user_name: d.user_name, email: d.email });
|
||
document.body.classList.remove("embed-waiting");
|
||
if ($("app").classList.contains("ready")) {
|
||
// 401 后重签:重载列表,不重复 enterApp / 不重复定位 task(尊重用户中间切过的选择)
|
||
loadTaskList();
|
||
} else {
|
||
// 首次签发:若 URL 带 task_id,直接进入该 task;enterApp 不再先加载根目录。
|
||
enterApp(takeInitialTaskId());
|
||
}
|
||
}
|
||
}
|
||
// ───── fragment 注入(wecom / app 变体共用)─────
|
||
// fragment 里的 token 优先于 localStorage 缓存(每次从登录入口进来都是新签的);
|
||
// 读完立即清 hash,token 不留在地址栏 / 浏览历史。name/email 由 enterApp → loadRole 拉 /v1/me 补齐。
|
||
function readFragmentToken() {
|
||
const h = new URLSearchParams(location.hash.replace(/^#/, ""));
|
||
const tok = h.get("token"), uid = h.get("user_id");
|
||
if (!tok || !uid) return;
|
||
state.token = tok;
|
||
localStorage.setItem(LS_TOKEN, tok);
|
||
setIdentity({ user_id: uid });
|
||
history.replaceState(null, "", location.pathname + location.search);
|
||
}
|
||
function takeInitialTaskId() {
|
||
if (EMBED_INITIAL_TASK_ID && !_embedInitialTaskHandled) {
|
||
_embedInitialTaskHandled = true;
|
||
return EMBED_INITIAL_TASK_ID;
|
||
}
|
||
return "";
|
||
}
|
||
|
||
// ───── wecom 免登变体 ─────
|
||
export function embedWecomRelogin() {
|
||
// 回 entry 重走 OAuth(snsapi_base 静默,企微客户端内用户无感);replace 不留历史,
|
||
// 避免返回键回到已失效的页面
|
||
location.replace("/v1/wecom/entry");
|
||
}
|
||
function embedWecomInit() {
|
||
document.body.classList.add("embed-mode");
|
||
readFragmentToken();
|
||
if (!state.token) { embedWecomRelogin(); return; }
|
||
enterApp(takeInitialTaskId());
|
||
}
|
||
|
||
// ───── app 套壳变体 ─────
|
||
export function embedAppRelogin() {
|
||
// 回登录入口重签:原生壳在 shouldOverrideUrlLoading / WKNavigationDelegate 拦自定义
|
||
// scheme(如 zcbotapp://relogin)→ 换新 token 重新 loadUrl;H5 登录页则静默重签后跳回
|
||
location.replace(EMBED_RELOGIN_URL);
|
||
}
|
||
function embedAppInit() {
|
||
document.body.classList.add("embed-mode");
|
||
readFragmentToken();
|
||
if (!state.token) { embedAppRelogin(); return; }
|
||
enterApp(takeInitialTaskId());
|
||
}
|
||
|
||
export function embedInit() {
|
||
if (EMBED_WECOM) { embedWecomInit(); return; }
|
||
if (EMBED_RELOGIN_URL) { embedAppInit(); return; }
|
||
if (!EMBED_PARENT_ORIGIN) {
|
||
document.body.classList.add("embed-mode", "embed-waiting");
|
||
embedShowWaiting("embed 模式缺少 parent_origin 或 relogin_url 参数 (iframe 嵌入用 ?embed=1&parent_origin=https://your-portal.com,App 套壳用 ?embed=1&relogin_url=<登录入口地址>)", true);
|
||
return;
|
||
}
|
||
document.body.classList.add("embed-mode");
|
||
window.addEventListener("message", embedHandleMessage);
|
||
// 普通 platform iframe 始终以父页面本次签发的 token 为准。若复用本地缓存,
|
||
// 过期/上一用户 token 会抢先触发初始 task 请求;其 401 返回后新 token 虽能
|
||
// 刷新任务列表,却不会重跑初始 task,页面便会一直停在“加载中”。
|
||
state.token = "";
|
||
document.body.classList.add("embed-waiting");
|
||
embedShowWaiting("等待登录…", false);
|
||
embedPostToParent({ type: "zcbot-ready" });
|
||
}
|
||
|