55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import dd from "dingtalk-jsapi";
|
|
|
|
import { getDingTalkConfig, loginWithDingTalk } from "@/api/auth";
|
|
import type { AuthTokenData } from "@/types/auth";
|
|
|
|
declare global {
|
|
interface Window {
|
|
dingtalk?: { platform?: { invokeAPI?: unknown } };
|
|
DingTalkJSBridge?: unknown;
|
|
}
|
|
}
|
|
|
|
export function isDingTalkContainer() {
|
|
if (typeof window === "undefined" || typeof navigator === "undefined")
|
|
return false;
|
|
const userAgent =
|
|
navigator.userAgent ||
|
|
(navigator as Navigator & { swuserAgent?: string }).swuserAgent ||
|
|
"";
|
|
return Boolean(
|
|
/DingTalk|dingtalk-win|dd-web/i.test(userAgent) ||
|
|
window.DingTalkJSBridge ||
|
|
window.dingtalk?.platform?.invokeAPI ||
|
|
// DingTalk desktop can expose its container identity through window.name
|
|
// even when the embedded browser omits DingTalk from userAgent.
|
|
/dingtalk/i.test(window.name || ""),
|
|
);
|
|
}
|
|
|
|
export async function waitForDingTalkContainer(timeoutMs = 5000) {
|
|
if (isDingTalkContainer()) return true;
|
|
const startedAt = Date.now();
|
|
while (Date.now() - startedAt < timeoutMs) {
|
|
await new Promise((resolve) => window.setTimeout(resolve, 100));
|
|
if (isDingTalkContainer()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export async function authenticateWithDingTalk(): Promise<AuthTokenData> {
|
|
if (!(await waitForDingTalkContainer()))
|
|
throw new Error("请从钉钉工作台打开此应用");
|
|
const configResponse = await getDingTalkConfig();
|
|
const config = configResponse.data.data;
|
|
if (!config) throw new Error("钉钉免登配置不完整");
|
|
const result = await dd.runtime.permission.requestAuthCode({
|
|
corpId: config.corpId,
|
|
});
|
|
const authCode = result?.code;
|
|
if (!authCode) throw new Error("钉钉未返回免登授权码");
|
|
const response = await loginWithDingTalk(authCode);
|
|
if (!response.data.data) throw new Error("钉钉登录响应为空");
|
|
return response.data.data;
|
|
}
|