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 { 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<OrgState>({
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);