diff --git a/src/store/org.ts b/src/store/org.ts index 83900c3..722a4d7 100644 --- a/src/store/org.ts +++ b/src/store/org.ts @@ -2,7 +2,7 @@ import { reactive } from "vue"; import request from "~/utils/request"; import { CurrentOrgId, HtyAuthToken, HtySudoToken } from "~/utils"; import type { Organization } from "~/types"; -import { showFailToast } from "vant"; +import { showDialog, showFailToast } from "vant"; interface OrgState { currentOrgId?: string; @@ -16,25 +16,43 @@ const store = reactive({ homepageMd: "", }); -const parseCurrentOrgIdFromToken = (token: string): string | undefined => { +type JwtPayloadDebug = { + token_id?: string; + current_org_id?: string; + current_org_role_keys?: string[]; +}; + +type JwtDecodeResult = { + payload?: JwtPayloadDebug; + error?: string; +}; + +const decodeJwtPayload = (token: string): JwtDecodeResult => { try { const tokenParts = token.split("."); - if (tokenParts.length < 2) return undefined; + if (tokenParts.length < 2) { + return { error: "invalid token parts" }; + } const payloadRaw = tokenParts[1].replace(/-/g, "+").replace(/_/g, "/"); const payloadRawWithPadding = payloadRaw.padEnd( Math.ceil(payloadRaw.length / 4) * 4, "=" ); - const payloadJson = decodeURIComponent( - atob(payloadRawWithPadding) - .split("") - .map((char) => `%${(`00${char.charCodeAt(0).toString(16)}`).slice(-2)}`) - .join("") - ); + const binary = atob(payloadRawWithPadding); + const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); + const payloadJson = new TextDecoder("utf-8").decode(bytes); const payload = JSON.parse(payloadJson); - return payload.current_org_id || undefined; - } catch (_error) { - return undefined; + return { + payload: { + token_id: payload.token_id, + current_org_id: payload.current_org_id, + current_org_role_keys: payload.current_org_role_keys, + }, + }; + } catch (error) { + return { + error: error instanceof Error ? error.message : "decode jwt payload failed", + }; } }; @@ -54,6 +72,10 @@ export default function useOrg() { }; const switchOrg = async (orgId: string) => { + const beforeAuthToken = window.localStorage.getItem(HtyAuthToken) || ""; + const beforeDecodeResult = beforeAuthToken + ? decodeJwtPayload(beforeAuthToken) + : { error: "missing auth token before switch" }; const { r, d, e } = await request({ url: "/api/v1/uc/org/switch", method: "POST", @@ -67,9 +89,26 @@ export default function useOrg() { showFailToast("机构切换失败:返回 token 非法"); return false; } - const tokenOrgId = parseCurrentOrgIdFromToken(d); + const afterDecodeResult = decodeJwtPayload(d); + const tokenOrgId = afterDecodeResult.payload?.current_org_id; + const debugPayload = { + switch_target_org_id: orgId, + before: beforeDecodeResult, + after: afterDecodeResult, + response_error: e, + response_ok: r, + returned_token_length: d.length, + role_keys_count: afterDecodeResult.payload?.current_org_role_keys?.length || 0, + timestamp: new Date().toISOString(), + }; + window.localStorage.setItem("OrgSwitchDebug", JSON.stringify(debugPayload)); + console.warn("[OrgSwitchDebug]", debugPayload); + if (!tokenOrgId) { - showFailToast("机构切换失败:token 缺少机构上下文"); + showDialog({ + title: "机构切换调试信息", + message: "token 缺少机构上下文,请打开 vConsole 查看 [OrgSwitchDebug]", + }); return false; } window.localStorage.setItem(HtyAuthToken, d);