Files
ctms/frontend/src/store/auth.ts
T
Cheng Zhou b491b6a146
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
发布候选:整合桌面端界面与发布稳定化里程碑
2026-07-01 10:53:24 +08:00

94 lines
2.9 KiB
TypeScript

import { defineStore } from "pinia";
import { ref } from "vue";
import { getLoginKey, login as apiLogin, devLogin, fetchMe } from "../api/auth";
import { setToken, clearToken, getToken } from "../utils/auth";
import { encryptLoginPayload } from "../utils/loginCrypto";
import type { UserInfo } from "../types/api";
import { useStudyStore } from "./study";
import { useSessionStore } from "./session";
export const useAuthStore = defineStore("auth", () => {
const LAST_LOGIN_EMAIL_KEY = "ctms_last_login_email";
const allowInsecureDevLogin =
import.meta.env.VITE_RUNTIME_ENV === "development" &&
import.meta.env.VITE_ALLOW_INSECURE_DEV_LOGIN === "true";
const token = ref<string | null>(getToken());
const user = ref<UserInfo | null>(null);
const loading = ref(false);
const forceLogin = ref(false);
const login = async (email: string, password: string) => {
loading.value = true;
try {
const { data } =
allowInsecureDevLogin && !window.isSecureContext
? await devLogin({ email, password })
: await encryptedLogin(email, password);
token.value = data.access_token;
await setToken(data.access_token);
useSessionStore().resetActivity();
localStorage.setItem(LAST_LOGIN_EMAIL_KEY, email);
const me = await fetchMeAction();
const studyStore = useStudyStore();
const userKey = me?.email || email;
await studyStore.restoreStudyForUser(userKey, { preferActive: me?.is_admin });
await studyStore.loadCurrentStudyPermissions().catch(() => {});
forceLogin.value = false;
} finally {
loading.value = false;
}
};
const encryptedLogin = async (email: string, password: string) => {
const { data: loginKey } = await getLoginKey();
const ciphertext = await encryptLoginPayload(loginKey.public_key, {
email,
password,
challenge: loginKey.challenge,
});
return apiLogin({
key_id: loginKey.key_id,
challenge: loginKey.challenge,
ciphertext,
});
};
const fetchMeAction = async () => {
const { data } = await fetchMe();
user.value = data;
if (data?.email) {
localStorage.setItem(LAST_LOGIN_EMAIL_KEY, data.email);
}
return data;
};
const logout = async () => {
const studyStore = useStudyStore();
const sessionStore = useSessionStore();
const userKey = user.value?.email || localStorage.getItem(LAST_LOGIN_EMAIL_KEY) || "";
studyStore.rememberCurrentStudyForUser(userKey);
token.value = null;
user.value = null;
forceLogin.value = false;
sessionStore.resetActivity();
await clearToken();
studyStore.clearCurrentStudy();
};
return {
token,
user,
loading,
forceLogin,
login,
fetchMe: fetchMeAction,
logout,
setToken: (newToken: string) => {
token.value = newToken;
},
requestReLogin: () => {
forceLogin.value = true;
},
};
});