ai-chat-ui/src/stores/auth.ts

161 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 用户认证状态管理
*/
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import type { UserInfo } from '@/types/chat';
// 开发环境默认跳过登录校验,避免频繁登录打断调试
const DEV_AUTH_BYPASS = import.meta.env.DEV;
// MARK: dev 默认 token当 URL 无 token 参数时使用)
const DEV_DEFAULT_TOKEN = '';
const DEV_BYPASS_USER: UserInfo = {
id: 'dev-user',
username: 'dev-user',
nickname: '开发环境用户',
};
// 认证接口返回格式
interface AuthResponse {
status: number;
message: string;
data: {
token: string;
userInfo: {
userId: number | string;
userName?: string;
realName?: string;
email?: string;
userIcon?: string;
[key: string]: unknown;
};
[key: string]: unknown;
} | null;
}
// 认证接口
const AUTH_BASE_URL = (import.meta.env.VITE_AUTH_BASE_URL || '').replace(/\/$/, '');
const AUTH_CHECK_URL = `${AUTH_BASE_URL}/newapi/api/login/validateToken`;
const AUTH_TOKEN_STORAGE_KEY = 'DEV_DEFAULT_TOKEN';
export const useAuthStore = defineStore('auth', () => {
// 状态
const token = ref<string | null>(null);
const user = ref<UserInfo | null>(null);
const isInitialized = ref(false);
// 计算属性
const isAuthenticated = computed(() => DEV_AUTH_BYPASS || !!token.value);
const userId = computed(() => user.value?.username || null); // username 用于 OSS 路径和数据库 user_id
/**
* 验证 token 并获取用户信息
*/
async function checkToken(tokenToCheck: string): Promise<UserInfo | null> {
try {
const response = await fetch(AUTH_CHECK_URL, {
method: 'POST',
headers: {
authorization: tokenToCheck,
},
});
if (!response.ok) {
return null;
}
const data: AuthResponse = await response.json();
if (data.status === 1000 && data.data?.userInfo) {
const { userInfo } = data.data;
return {
...userInfo,
id: String(userInfo.userId),
username: userInfo.userName,
nickname: userInfo.realName,
email: userInfo.email,
avatar: userInfo.userIcon,
};
} else {
window.$toast?.('[Auth] Token 验证失败:Token无效');
}
return null;
} catch (error) {
console.error('[Auth] Token 验证失败:', error);
return null;
}
}
/**
* 初始化 - 从 URL 参数获取 token验证后设置用户信息
*/
async function init() {
const searchParams = new URLSearchParams(window.location.search);
const urlToken = searchParams.get('token');
// 获取 tokenURL > localStorage > 默认值
const tokenValue = urlToken
|| localStorage.getItem(AUTH_TOKEN_STORAGE_KEY)
|| DEV_DEFAULT_TOKEN;
if (DEV_AUTH_BYPASS && !tokenValue) {
token.value = null;
user.value = DEV_BYPASS_USER;
isInitialized.value = true;
return;
}
if (!tokenValue) {
isInitialized.value = true;
window.$toast?.('未登录,请先登录', 'error');
return;
}
// 验证 token
const userInfo = await checkToken(tokenValue);
if (userInfo) {
token.value = tokenValue;
user.value = userInfo;
} else {
// 验证失败,清空
token.value = null;
user.value = null;
}
isInitialized.value = true;
}
/**
* 设置用户信息
*/
function setUser(userInfo: UserInfo) {
user.value = userInfo;
}
/**
* 获取认证 header
*/
function getAuthHeader(): Record<string, string> {
if (token.value) {
return { Authorization: `Bearer ${token.value}` };
}
return {};
}
return {
// 状态
token,
user,
isAuthenticated,
userId,
isInitialized,
// 方法
setUser,
getAuthHeader,
init,
};
});