frontend
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import request from '@/utils/request'
|
||||
import { useRouter } from 'vue-router';
|
||||
// import { jwtDecode } from 'jwt-decode';
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const user = ref(null);
|
||||
const role = computed(() => {
|
||||
if (!user.value) return 0;
|
||||
return user.value.role;
|
||||
})
|
||||
|
||||
const token = ref(localStorage.getItem('token') || '');
|
||||
|
||||
const isAdmin = computed(() => {
|
||||
if (!user.value || user.value.role === 0) return false;
|
||||
return user.value.role > 0;
|
||||
});
|
||||
const isLoggedIn = computed(() => !!token.value);
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
const setToken = (newToken) => {
|
||||
token.value = newToken;
|
||||
localStorage.setItem('token', newToken);
|
||||
}
|
||||
|
||||
const loadTokenFromStorage=()=> {
|
||||
const storedToken = localStorage.getItem('token');
|
||||
if (storedToken) {
|
||||
token.value = storedToken;
|
||||
}
|
||||
}
|
||||
|
||||
const register = async (userInfo) => {
|
||||
error.value = null;
|
||||
try {
|
||||
const res = await request.post('/auth/register', userInfo)
|
||||
return res
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '注册失败';
|
||||
throw error // 或者您可以在这里处理错误,例如显示错误消息
|
||||
}finally{
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const login = async (userInfo) => {
|
||||
error.value = null;
|
||||
try {
|
||||
const res = await request.post('/auth/login', userInfo)
|
||||
if (res.status === 200 && !!res.data.data?.token) {
|
||||
setToken(res.data.data.token)
|
||||
}
|
||||
await getProfile() // 登录成功后获取用户信息
|
||||
return res
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '登录失败';
|
||||
throw error // 或者您可以在这里处理错误,例如显示错误消息
|
||||
}finally{
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const getProfile = async () => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
if (token.value && user.value) {
|
||||
return user.value
|
||||
}
|
||||
try {
|
||||
const res = await request.get('/profile')
|
||||
|
||||
user.value = res.data.data
|
||||
return res
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取用户信息失败';
|
||||
|
||||
throw error
|
||||
}finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const refreshProfile = async () => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const res = await request.get('/profile')
|
||||
if (res.data.code == 200) {
|
||||
user.value = res.data.data
|
||||
}
|
||||
return res
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取用户信息失败';
|
||||
throw error
|
||||
}finally{
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const updateProfile = async (userInfo) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const res = await request.post('/profile/update', userInfo)
|
||||
console.log('auth.js updateProfile', res.data);
|
||||
return res
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '更新用户信息失败';
|
||||
throw error
|
||||
}finally{
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const updatePassword = async (payload) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const res = await request.post('/profile/update/password', payload)
|
||||
console.log('auth.js updatePassword', res.data);
|
||||
return res
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '更新密码失败';
|
||||
throw error
|
||||
}finally{
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const createToken = async (newToken) => {
|
||||
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.post('/tokens', newToken)
|
||||
console.log('createToken', response.data);
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '创建token失败';
|
||||
throw error
|
||||
}finally{
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const resetToken = async (id) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.post(`/tokens/reset/${id}`)
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '重置token失败';
|
||||
throw error
|
||||
}finally{
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const updateToken = async (token) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.put(`/tokens/${token.id}`, token)
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '更新token失败';
|
||||
throw error
|
||||
}finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const deleteToken = async (id) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.delete(`/tokens/${id}`)
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '删除token失败';
|
||||
throw err
|
||||
}finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
user.value = null
|
||||
token.value = ''
|
||||
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
clear()
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
return {
|
||||
loading,error,
|
||||
user,role,token,
|
||||
isLoggedIn,
|
||||
setToken,loadTokenFromStorage,
|
||||
login,register,
|
||||
getProfile,updateProfile,updatePassword,refreshProfile,
|
||||
createToken,deleteToken,resetToken,updateToken,
|
||||
clear,
|
||||
logout
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,140 @@
|
||||
// src/stores/key.js
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export const useKeyStore = defineStore('key', () => {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const totalKeys = ref(0);
|
||||
const keys = ref([]);
|
||||
const key = ref(null);
|
||||
|
||||
const fetchKeys = async (pageSize = 20, page = 1, active) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.get('/keys', {
|
||||
params: {
|
||||
pageSize,
|
||||
page,
|
||||
active,
|
||||
},
|
||||
});
|
||||
|
||||
keys.value = response.data.data?.keys;
|
||||
totalKeys.value = response.data.data?.total;
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取ApiKeys失败';
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchKey = async (id) => {
|
||||
if (keys.value.length > 0) {
|
||||
const findkey = keys.value.find(item => item.id === id)
|
||||
if (findkey) {
|
||||
key.value = findkey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
// const findkey = keys.find(item=>item.id === id)
|
||||
// console.log('findkey',findkey)
|
||||
try {
|
||||
const response = await request.get(`/keys/${id}`);
|
||||
key.value = response.data.data;
|
||||
if (key.value.support_models.length < 3) {
|
||||
key.value.support_models = key.value.support_models_array ? JSON.stringify(key.value.support_models) : ''
|
||||
}
|
||||
if (!key.value.support_models_array) {
|
||||
key.value.support_models_array = key.value.support_models ? JSON.parse(key.value.support_models) : []
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取ApiKey失败';
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const refreshKey = async (id) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.get(`/keys/${id}`);
|
||||
key.value = response.data.data;
|
||||
if (key.value.support_models.length < 3) {
|
||||
key.value.support_models = key.value.support_models_array ? JSON.stringify(key.value.support_models) : ''
|
||||
}
|
||||
if (!key.value.support_models_array) {
|
||||
key.value.support_models_array = key.value.support_models ? JSON.parse(key.value.support_models) : []
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取ApiKey失败';
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const createKey = async (data) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.post('/keys', data);
|
||||
return response;
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '创建ApiKey失败';
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const updateKey = async (key) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.put(`/keys/${key.id}`, key);
|
||||
return response;
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '更新ApiKey失败';
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const keyOption = async (option, ids) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.post(`/keys/batch/${option}`, { ids });
|
||||
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '操作失败';
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
//todo 更新 批量操作
|
||||
|
||||
return {
|
||||
loading, error,
|
||||
key, keys, totalKeys,
|
||||
fetchKeys,
|
||||
fetchKey,
|
||||
refreshKey,
|
||||
createKey,
|
||||
updateKey,
|
||||
keyOption,
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// src/stores/user.js
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const users = ref([]);
|
||||
const totalUsers = ref(0);
|
||||
const user = ref(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
async function createUser(userData) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.post('/users', userData);
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '创建用户失败'
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function listUser(pageSize = 20, page = 1, active) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.get('/users', {
|
||||
params: {
|
||||
pageSize,
|
||||
page,
|
||||
active,
|
||||
},
|
||||
});
|
||||
users.value = response.data.data?.users;
|
||||
totalUsers.value = response.data.data?.total;
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取用户列表失败';
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getUser(id) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.get(`/users/${id}`);
|
||||
console.log('getUser response',response);
|
||||
user.value = response.data.data;
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取用户信息失败';
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshUser(id) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.get(`/users/${id}`);
|
||||
console.log('getUser response',response);
|
||||
user.value = response.data.data;
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取用户信息失败';
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function editUser(id, userData) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response= await request.put(`/users/${id}`, userData);
|
||||
console.log('editUser',response);
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '编辑用户失败';
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUser(id) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.delete(`/users/${id}`);
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '删除用户失败';
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function userOption(option, ids) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.post(`/users/batch/${option}`, { ids });
|
||||
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '操作失败';
|
||||
throw err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
users,
|
||||
totalUsers,
|
||||
user,
|
||||
loading,
|
||||
error,
|
||||
createUser,
|
||||
listUser,
|
||||
getUser,
|
||||
refreshUser,
|
||||
editUser,
|
||||
deleteUser,
|
||||
userOption,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,138 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import request from "@/utils/request";
|
||||
import { useRouter } from "vue-router";
|
||||
import { startRegistration, startAuthentication } from "@simplewebauthn/browser";
|
||||
import { useAuthStore } from "./auth";
|
||||
|
||||
export const useWebAuthStore = defineStore("webauth", () => {
|
||||
const router = useRouter();
|
||||
// const token = ref(localStorage.getItem("token") || "");
|
||||
|
||||
const passkeys = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
const addPasskey = async () => {
|
||||
error.value = "";
|
||||
loading.value = true;
|
||||
try {
|
||||
// 1. 从后端获取注册选项 (Creation Options)
|
||||
const res = await request.get("/profile/passkey");
|
||||
console.log("begin:", res.data.data.publicKey);
|
||||
const options = res.data.data.publicKey;
|
||||
|
||||
// 调用 Web Authentication API 进行注册
|
||||
// const credential = await navigator.credentials.create(options);
|
||||
// console.log("credential:", credential);
|
||||
let attestation;
|
||||
try {
|
||||
// Pass 'undefined' as the second argument if you are not using an AbortSignal
|
||||
attestation = await startRegistration({optionsJSON: options});
|
||||
// console.log("WebAuthn 注册结果 (Attestation):", JSON.stringify(attestation));
|
||||
error.value = null;
|
||||
} catch (regError) {
|
||||
// console.log("WebAuthn 注册失败或取消:", regError);
|
||||
if (regError.name === "NotAllowedError") {
|
||||
error.value = "Passkey 操作被取消或不允许。";
|
||||
} else {
|
||||
error.value = `Passkey 创建出错: ${regError.message}`;
|
||||
}
|
||||
return; // 终止流程
|
||||
}
|
||||
|
||||
// 3. 将注册结果 (Attestation) 发送到后端进行验证和保存
|
||||
const res2 = await request.post("/profile/passkey", attestation);
|
||||
console.log("end:", res2);
|
||||
return res2;
|
||||
} catch (err) {
|
||||
error.value =err.response?.data?.error || "添加 Passkey 失败,请稍后重试。";
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loginPasskey = async () => {
|
||||
error.value = null;
|
||||
loading.value = true;
|
||||
try {
|
||||
// 1. 从后端获取登录选项 (Assertion Options)
|
||||
const res = await request.get("/auth/passkey/begin");
|
||||
// console.log("login begin:", res.data);
|
||||
const options = res.data.data.publicKey;
|
||||
|
||||
// 2. 调用 Web Authentication API 进行认证
|
||||
let assertion;
|
||||
try {
|
||||
assertion = await startAuthentication({ optionsJSON: options });
|
||||
// console.log("WebAuthn 认证结果 (Assertion):", JSON.stringify(assertion));
|
||||
} catch (loginError) {
|
||||
if (loginError.name === "NotAllowedError") {
|
||||
error.value = "Passkey 登录被取消或不允许。";
|
||||
} else {
|
||||
error.value = `Passkey 登录出错: ${loginError.message}`;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// 3. 将认证结果 (Assertion) 发送到后端进行验证并获取 Token
|
||||
const challenge = options.challenge; // 从 begin 接口返回的 options 中获取 challenge
|
||||
const res2 = await request.post(`/auth/passkey/finish?challenge=${challenge}`, assertion);
|
||||
|
||||
// 4. 处理登录成功的响应,通常包含 Token
|
||||
if (res2.status === 200 && !!res2.data.data?.token) {
|
||||
const token = res2.data.data.token
|
||||
const authStore = useAuthStore()
|
||||
authStore.setToken(token)
|
||||
await authStore.getProfile()
|
||||
return res2.data
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || err.value || "Passkey 登录失败,请稍后重试。";
|
||||
throw error;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getPasskeys = async () => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.get('/profile/passkeys')
|
||||
// console.log('getPasskeys',response.data.data)
|
||||
passkeys.value = response.data.data
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || '获取token列表失败';
|
||||
throw error
|
||||
}finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const deletePasskey = async (id) => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
const response = await request.delete(`/profile/passkeys/${id}`)
|
||||
return response
|
||||
} catch (err) {
|
||||
error.value = err.response?.data?.error || `删除passkey ${id} 失败`;
|
||||
throw error
|
||||
}finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
passkeys,
|
||||
loading,
|
||||
error,
|
||||
addPasskey,
|
||||
loginPasskey,
|
||||
getPasskeys,
|
||||
deletePasskey,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user