feat: 新增鉴权
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
// app/stores/auth.ts - 用户登录状态管理
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
import { authClient } from "@/utils/auth";
|
||||
|
||||
/** 当前登录用户信息,apiToken 用于 OCS 油猴脚本配置 */
|
||||
interface AuthUser {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
/** 油猴脚本使用此 token 在 body 中传递身份 */
|
||||
apiToken?: string | null;
|
||||
}
|
||||
|
||||
export const useAuthStore = defineStore("auth", () => {
|
||||
const user = ref<AuthUser | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref("");
|
||||
/** session 是否已完成首次加载,用于区分"未登录"和"尚未检查" */
|
||||
const initialized = ref(false);
|
||||
/** 已登录且 session 已初始化完成 */
|
||||
const isOnline = computed(() => initialized.value && user.value !== null);
|
||||
|
||||
// 用于对并发调用去重,避免同一时刻发起多次 get-session 请求
|
||||
let _fetchPromise: Promise<void> | null = null;
|
||||
|
||||
/** 从服务端拉取当前 session;已在请求中时复用同一 Promise,已初始化时直接跳过 */
|
||||
const fetchSession = async () => {
|
||||
if (initialized.value) return;
|
||||
if (_fetchPromise) return _fetchPromise;
|
||||
|
||||
_fetchPromise = (async () => {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const session = await authClient.getSession();
|
||||
user.value = session.data?.user
|
||||
? {
|
||||
id: session.data.user.id,
|
||||
name: session.data.user.name,
|
||||
email: session.data.user.email,
|
||||
apiToken: (session.data.user as { apiToken?: string | null })
|
||||
.apiToken
|
||||
}
|
||||
: null;
|
||||
} catch {
|
||||
user.value = null;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
initialized.value = true;
|
||||
_fetchPromise = null;
|
||||
}
|
||||
})();
|
||||
|
||||
return _fetchPromise;
|
||||
};
|
||||
|
||||
/** 从 better-auth 返回的 user 对象中提取并写入 store */
|
||||
const _setUserFromResult = (u: {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
apiToken?: string | null;
|
||||
}) => {
|
||||
user.value = {
|
||||
id: u.id,
|
||||
name: u.name,
|
||||
email: u.email,
|
||||
apiToken: u.apiToken ?? null
|
||||
};
|
||||
initialized.value = true;
|
||||
};
|
||||
|
||||
/** 邮箱密码登录 */
|
||||
const signIn = async (email: string, password: string) => {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const result = await authClient.signIn.email({ email, password });
|
||||
if (result.error) {
|
||||
error.value = "邮箱或密码错误";
|
||||
return false;
|
||||
}
|
||||
// 直接使用登录响应中的用户数据,无需重复请求 get-session
|
||||
if (result.data?.user) {
|
||||
_setUserFromResult(
|
||||
result.data.user as Parameters<typeof _setUserFromResult>[0]
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
error.value = "登录失败,请重试";
|
||||
return false;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 注册新账号 */
|
||||
const signUp = async (name: string, email: string, password: string) => {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const result = await authClient.signUp.email({ name, email, password });
|
||||
if (result.error) {
|
||||
error.value = "注册失败,邮箱可能已被使用";
|
||||
return false;
|
||||
}
|
||||
if (result.data?.user) {
|
||||
_setUserFromResult(
|
||||
result.data.user as Parameters<typeof _setUserFromResult>[0]
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
error.value = "注册失败,请重试";
|
||||
return false;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 退出登录 */
|
||||
const signOut = async () => {
|
||||
await authClient.signOut();
|
||||
user.value = null;
|
||||
initialized.value = false;
|
||||
};
|
||||
|
||||
/** 刷新当前用户的 apiToken;成功后同步更新 store */
|
||||
const refreshApiToken = async (): Promise<boolean> => {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
try {
|
||||
const res = await $fetch<{ success: boolean; msg: string; apiToken?: string }>(
|
||||
"/api/user/refresh-token",
|
||||
{ method: "POST" }
|
||||
);
|
||||
if (res.success && res.apiToken && user.value) {
|
||||
user.value = { ...user.value, apiToken: res.apiToken };
|
||||
return true;
|
||||
}
|
||||
error.value = res.msg || "刷新失败";
|
||||
return false;
|
||||
} catch {
|
||||
error.value = "刷新失败,请重试";
|
||||
return false;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
user,
|
||||
loading,
|
||||
error,
|
||||
initialized,
|
||||
isOnline,
|
||||
fetchSession,
|
||||
signIn,
|
||||
signUp,
|
||||
signOut,
|
||||
refreshApiToken
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user