11
This commit is contained in:
144
src/app/onboarding/extension/pair-handoff.tsx
Normal file
144
src/app/onboarding/extension/pair-handoff.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
"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<void>((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 (
|
||||
<div className="rounded-lg border border-border/60 bg-muted/20 p-4 text-xs text-muted-foreground">
|
||||
请先在网页完成登录,然后保持本页面打开,系统会自动把登录态同步给扩展。
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === "pairing") {
|
||||
return (
|
||||
<div className="rounded-lg border border-border/60 bg-muted/20 p-4 text-xs text-muted-foreground">
|
||||
正在与扩展配对中…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === "ok") {
|
||||
return (
|
||||
<div className="rounded-lg border border-emerald-200 bg-emerald-50/60 p-4 text-xs text-emerald-700">
|
||||
扩展已配对成功,你可以回到扩展继续操作。
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-rose-200 bg-rose-50/60 p-4 text-xs text-rose-700">
|
||||
配对失败:{errorText || "unknown error"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user