"use client"; import {useEffect, useState} from "react"; import {useSearchParams} from "next/navigation"; import useUserStore from "@/store/user"; import {pairExtensionTokenApi} from "@/api/scan"; import {getSettingApi} from "@/api/set"; import {EXTENSION_ID} from "@/utils/extension/detect_extension"; declare const chrome: any; /** * 网页侧“配对回传”: * - 由扩展 popup 的“登录”按钮打开本页(带 from=extension)。 * - 若网页已登录:调用 `/api/auth/extension-pair` 拿到 extension token,然后回传给扩展保存。 * 中文备注:这里不搞复杂类型,按需求全用 any。 */ export default function PairHandoff() { const params = useSearchParams(); const token = useUserStore((s) => s.token); const [status, setStatus] = useState<"idle" | "pairing" | "ok" | "need_login" | "error">("idle"); const [errorText, setErrorText] = useState(""); useEffect(() => { const from = params.get("from"); if (from !== "extension") return; // 中文备注:必须在浏览器环境 + 可调用 chrome.runtime 的情况下才能回传。 if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) { return; } if (!token) { setStatus("need_login"); return; } void (async () => { setStatus("pairing"); setErrorText(""); try { const extId = params.get("extId") || EXTENSION_ID; // 1) 先问扩展是否已经配对(避免重复 pair) const authCheck: any = await new Promise((resolve, reject) => { chrome.runtime.sendMessage( extId, {type: "DIANSHAN_AUTH_CHECK"}, (resp: any) => { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } resolve(resp); }, ); }); if (authCheck?.data?.authed) { setStatus("ok"); return; } // 2) 换取扩展 token(Web token -> extension token) const pair = await pairExtensionTokenApi(); // 3) 读取品牌/店铺/定时配置(给扩展做 ingest 与 scheduled scan 使用) const settings: any = await getSettingApi(); const brand = settings?.brand || null; const storeId = brand?.stores?.[0]?.id || brand?.storeId || brand?.store_id || null; const authState: any = { token: pair?.token, apiBaseUrl: pair?.apiBaseUrl, userEmail: pair?.userEmail ?? null, brandId: brand?.id ?? null, storeId, timezone: brand?.timezone ?? null, morningBriefHour: brand?.morning_brief_hour ?? null, eveningRecapHour: brand?.evening_recap_hour ?? null, }; // 4) 回传给扩展保存 await new Promise((resolve, reject) => { chrome.runtime.sendMessage( extId, {type: "DIANSHAN_SSO_HANDOFF", payload: {authState}}, (resp: any) => { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } if (!resp?.ok) { reject(new Error(resp?.error || "handoff_failed")); return; } resolve(); }, ); }); setStatus("ok"); } catch (e: any) { setStatus("error"); setErrorText(e?.message || "Pairing failed."); } })(); }, [params, token]); if (status === "idle") return null; if (status === "need_login") { return (
请先在网页完成登录,然后保持本页面打开,系统会自动把登录态同步给扩展。
); } if (status === "pairing") { return (
正在与扩展配对中…
); } if (status === "ok") { return (
扩展已配对成功,你可以回到扩展继续操作。
); } return (
配对失败:{errorText || "unknown error"}
); }