feat: 新增鉴权

This commit is contained in:
2026-05-22 22:38:01 +08:00
parent b02f15f735
commit 3857f77f8b
38 changed files with 1697 additions and 195 deletions
+165
View File
@@ -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
};
});