chore(debug): add detailed org switch token diagnostics

Record before/after jwt org context decode details for org switch into console and localStorage, and surface actionable debug dialog when returned token lacks org context.

Made-with: Cursor
This commit is contained in:
2026-04-28 00:03:00 +08:00
parent 713af27749
commit e15b68b16f
+53 -14
View File
@@ -2,7 +2,7 @@ import { reactive } from "vue";
import request from "~/utils/request"; import request from "~/utils/request";
import { CurrentOrgId, HtyAuthToken, HtySudoToken } from "~/utils"; import { CurrentOrgId, HtyAuthToken, HtySudoToken } from "~/utils";
import type { Organization } from "~/types"; import type { Organization } from "~/types";
import { showFailToast } from "vant"; import { showDialog, showFailToast } from "vant";
interface OrgState { interface OrgState {
currentOrgId?: string; currentOrgId?: string;
@@ -16,25 +16,43 @@ const store = reactive<OrgState>({
homepageMd: "", 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 { try {
const tokenParts = token.split("."); 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 payloadRaw = tokenParts[1].replace(/-/g, "+").replace(/_/g, "/");
const payloadRawWithPadding = payloadRaw.padEnd( const payloadRawWithPadding = payloadRaw.padEnd(
Math.ceil(payloadRaw.length / 4) * 4, Math.ceil(payloadRaw.length / 4) * 4,
"=" "="
); );
const payloadJson = decodeURIComponent( const binary = atob(payloadRawWithPadding);
atob(payloadRawWithPadding) const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
.split("") const payloadJson = new TextDecoder("utf-8").decode(bytes);
.map((char) => `%${(`00${char.charCodeAt(0).toString(16)}`).slice(-2)}`)
.join("")
);
const payload = JSON.parse(payloadJson); const payload = JSON.parse(payloadJson);
return payload.current_org_id || undefined; return {
} catch (_error) { payload: {
return undefined; 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 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({ const { r, d, e } = await request({
url: "/api/v1/uc/org/switch", url: "/api/v1/uc/org/switch",
method: "POST", method: "POST",
@@ -67,9 +89,26 @@ export default function useOrg() {
showFailToast("机构切换失败:返回 token 非法"); showFailToast("机构切换失败:返回 token 非法");
return false; 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) { if (!tokenOrgId) {
showFailToast("机构切换失败:token 缺少机构上下文"); showDialog({
title: "机构切换调试信息",
message: "token 缺少机构上下文,请打开 vConsole 查看 [OrgSwitchDebug]",
});
return false; return false;
} }
window.localStorage.setItem(HtyAuthToken, d); window.localStorage.setItem(HtyAuthToken, d);