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:
+53
-14
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user