初始化
This commit is contained in:
1 parent
34474bd5fc
commit
b638b541d0
3008 files changed
+837699
No files matched your search
@@ -0,0 +1,193 @@
|
||||
export interface LoginByUsernameResponse {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
user_id: string;
|
||||
email: string;
|
||||
system_role: string;
|
||||
needs_setup: boolean;
|
||||
created: boolean;
|
||||
/**
|
||||
* 本次登录解析出的原始登录用户名(token 登录=getTokenInfo 解析值;用户名登录=所传用户名)。
|
||||
* 用于跳转外链时拼 ``?username=``——后端落库的邮箱前缀是归一化(小写/替换)后的,有损,故以此为准。
|
||||
* 旧后端可能不返回。
|
||||
*/
|
||||
username?: string;
|
||||
}
|
||||
|
||||
import {
|
||||
authStorageGet,
|
||||
authStorageRemove,
|
||||
authStorageSet,
|
||||
} from "@/core/auth/scoped-storage";
|
||||
|
||||
const AUTH_STORAGE_KEY = "deerflow.auth";
|
||||
/** Fired in the current tab because the browser `storage` event only reaches other tabs. */
|
||||
export const AUTH_STATE_CHANGED_EVENT = "deerflow:auth-state-changed";
|
||||
|
||||
function isBrowser(): boolean {
|
||||
return typeof window !== "undefined";
|
||||
}
|
||||
|
||||
export function getStoredAuth(): LoginByUsernameResponse | null {
|
||||
if (!isBrowser()) {
|
||||
return null;
|
||||
}
|
||||
// 嵌入(iframe)态走 sessionStorage、正常态走 localStorage —— 隔离 iframe 登录用户与正常登录用户。
|
||||
const raw = authStorageGet(AUTH_STORAGE_KEY);
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(raw) as LoginByUsernameResponse;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeIdentityPart(value: unknown): string {
|
||||
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
||||
}
|
||||
|
||||
function identityFromLoginName(value: unknown): string | null {
|
||||
const normalized = normalizeIdentityPart(value);
|
||||
if (!normalized) return null;
|
||||
// Explicit email login is domain-specific. Bare username login is the
|
||||
// stable business principal even if the backend's synthetic email domain or
|
||||
// internal UUID changes.
|
||||
return normalized.includes("@")
|
||||
? `email:${normalized}`
|
||||
: `account:${normalized}`;
|
||||
}
|
||||
|
||||
function readUserInfoIdentity(): string | null {
|
||||
let info: Record<string, unknown> | null = null;
|
||||
try {
|
||||
const raw = authStorageGet("userInfo");
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||
info = parsed as Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
info = null;
|
||||
}
|
||||
if (!info) return null;
|
||||
|
||||
const candidates = [
|
||||
["yUserId", info.yUserId],
|
||||
["userName", info.userName],
|
||||
["userId", info.userId],
|
||||
] as const;
|
||||
|
||||
for (const [prefix, value] of candidates) {
|
||||
const normalized = normalizeIdentityPart(value);
|
||||
if (normalized) return `${prefix}:${normalized}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A stable cross-tab identity key for "did the account really change?" checks.
|
||||
*
|
||||
* Some external login paths can re-issue a backend ``user_id`` or use a
|
||||
* different synthetic email domain while resolving to the same business
|
||||
* account. Prefer the raw login username returned by the backend, then the
|
||||
* email local-part, and keep ``user_id`` only as the final fallback.
|
||||
*/
|
||||
export function getAccountIdentityKey(
|
||||
auth: LoginByUsernameResponse | null = getStoredAuth(),
|
||||
): string | null {
|
||||
if (!auth) return null;
|
||||
|
||||
const loginIdentity = identityFromLoginName(auth.username);
|
||||
if (loginIdentity) return loginIdentity;
|
||||
|
||||
const email = normalizeIdentityPart(auth.email);
|
||||
if (email) {
|
||||
const localPart = email.split("@", 1)[0];
|
||||
return localPart ? `account:${localPart}` : `email:${email}`;
|
||||
}
|
||||
|
||||
const userInfoIdentity = readUserInfoIdentity();
|
||||
if (userInfoIdentity) return `userInfo:${userInfoIdentity}`;
|
||||
|
||||
const userId = normalizeIdentityPart(auth.user_id);
|
||||
return userId ? `user_id:${userId}` : null;
|
||||
}
|
||||
|
||||
export function setStoredAuth(auth: LoginByUsernameResponse) {
|
||||
if (!isBrowser()) {
|
||||
return;
|
||||
}
|
||||
authStorageSet(AUTH_STORAGE_KEY, JSON.stringify(auth));
|
||||
window.dispatchEvent(new Event(AUTH_STATE_CHANGED_EVENT));
|
||||
}
|
||||
|
||||
export function clearStoredAuth() {
|
||||
if (!isBrowser()) {
|
||||
return;
|
||||
}
|
||||
authStorageRemove(AUTH_STORAGE_KEY);
|
||||
window.dispatchEvent(new Event(AUTH_STATE_CHANGED_EVENT));
|
||||
}
|
||||
|
||||
/** 与设置 → 个人信息页「用户名」一致:邮箱 @ 前缀,否则 user_id。 */
|
||||
export function getAccountDisplayName(
|
||||
auth: LoginByUsernameResponse | null = getStoredAuth(),
|
||||
): string {
|
||||
if (!auth) return "用户";
|
||||
const usernameFromEmail = (auth.email ?? "").split("@")[0] ?? "";
|
||||
return usernameFromEmail || auth.user_id || "用户";
|
||||
}
|
||||
|
||||
/**
|
||||
* 把登录用户名写入 localStorage.userInfo.userName(轻应用登录参数等场景使用)。
|
||||
* 仅在尚未设置时写入,避免覆盖外部登录(yUserId)从 consumer 拿到的用户名。
|
||||
*/
|
||||
export function setStoredUserName(name: string | null | undefined) {
|
||||
if (!isBrowser()) {
|
||||
return;
|
||||
}
|
||||
const trimmed = name?.trim();
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
let info: Record<string, unknown> = {};
|
||||
try {
|
||||
const raw = authStorageGet("userInfo");
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (parsed && typeof parsed === "object") {
|
||||
info = parsed as Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
info = {};
|
||||
}
|
||||
if (typeof info.userName === "string" && info.userName.trim()) {
|
||||
return;
|
||||
}
|
||||
info.userName = trimmed;
|
||||
authStorageSet("userInfo", JSON.stringify(info));
|
||||
}
|
||||
|
||||
export function getAccessToken(): string | undefined {
|
||||
const auth = getStoredAuth();
|
||||
const token = auth?.access_token?.trim();
|
||||
return token ? token : undefined;
|
||||
}
|
||||
|
||||
export function getAuthorizationHeaderValue(): string | undefined {
|
||||
const auth = getStoredAuth();
|
||||
const token = auth?.access_token?.trim();
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
const rawType = auth?.token_type?.trim();
|
||||
const tokenType = rawType ? rawType : "Bearer";
|
||||
return `${tokenType} ${token}`;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user