feat: add user login support

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/3445/head
Ryan Wang 2022-07-02 14:01:40 +08:00
parent 9dc7016f06
commit 9765dd5529
6 changed files with 119 additions and 3 deletions

View File

@ -34,6 +34,7 @@
"floating-vue": "2.0.0-beta.16",
"lodash.clonedeep": "^4.5.0",
"pinia": "^2.0.14",
"qs": "^6.11.0",
"uuid": "^8.3.2",
"vue": "^3.2.37",
"vue-filepond": "^7.0.3",
@ -46,6 +47,8 @@
"@types/jsdom": "^16.2.14",
"@types/lodash.clonedeep": "4.5.7",
"@types/node": "^17.0.45",
"@types/qs": "^6.9.7",
"@types/uuid": "^8.3.4",
"@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vitest/ui": "^0.15.2",

View File

@ -5,4 +5,17 @@ const axiosInstance = axios.create({
withCredentials: true,
});
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
console.log("error", error);
if (error.response.status === 401) {
window.location.href = "/#/login";
}
return Promise.reject(error);
}
);
export { axiosInstance };

View File

@ -1,13 +1,12 @@
const { themeable } = require("tailwindcss-themeable");
module.exports = {
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
content: ["./src/**/*.{vue,js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [
require("tailwindcss-safe-area"),
require("@tailwindcss/aspect-ratio"),
themeable({
defaultTheme: "default",
themes: [

View File

@ -12,6 +12,8 @@ importers:
'@types/jsdom': ^16.2.14
'@types/lodash.clonedeep': 4.5.7
'@types/node': ^17.0.45
'@types/qs': ^6.9.7
'@types/uuid': ^8.3.4
'@vitejs/plugin-vue': ^2.3.3
'@vitejs/plugin-vue-jsx': ^1.3.10
'@vitest/ui': ^0.15.2
@ -36,6 +38,7 @@ importers:
postcss: ^8.4.14
prettier: ^2.7.1
prettier-plugin-tailwindcss: ^0.1.11
qs: ^6.11.0
sass: ^1.53.0
start-server-and-test: ^1.14.0
tailwindcss: ^3.1.4
@ -65,6 +68,7 @@ importers:
floating-vue: 2.0.0-beta.16_vue@3.2.37
lodash.clonedeep: 4.5.0
pinia: 2.0.14_j6bzmzd4ujpabbp5objtwxyjp4
qs: 6.11.0
uuid: 8.3.2
vue: 3.2.37
vue-filepond: 7.0.3_filepond@4.30.4+vue@3.2.37
@ -76,6 +80,8 @@ importers:
'@types/jsdom': 16.2.14
'@types/lodash.clonedeep': 4.5.7
'@types/node': 17.0.45
'@types/qs': 6.9.7
'@types/uuid': 8.3.4
'@vitejs/plugin-vue': 2.3.3_vite@2.9.12+vue@3.2.37
'@vitejs/plugin-vue-jsx': 1.3.10
'@vitest/ui': 0.15.2
@ -1472,7 +1478,7 @@ packages:
axios: 0.24.0
form-data: 4.0.0
js-base64: 3.7.2
qs: 6.10.3
qs: 6.11.0
tslib: 2.4.0
transitivePeerDependencies:
- debug
@ -2156,6 +2162,10 @@ packages:
resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==}
dev: true
/@types/uuid/8.3.4:
resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==}
dev: true
/@types/web-bluetooth/0.0.14:
resolution: {integrity: sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A==}
@ -5763,6 +5773,14 @@ packages:
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.4
dev: true
/qs/6.11.0:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.4
dev: false
/qs/6.5.3:
resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}

View File

@ -0,0 +1,77 @@
<script lang="ts" setup>
import { VButton, VInput, VSpace } from "@halo-dev/components";
import { v4 as uuid } from "uuid";
import { axiosInstance } from "@halo-dev/admin-shared";
import qs from "qs";
import logo from "../../../assets/logo.svg";
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
interface LoginForm {
_csrf: string;
username: string;
password: string;
}
interface LoginFormState {
logging: boolean;
state: LoginForm;
}
const router = useRouter();
const loginForm = ref<LoginFormState>({
logging: false,
state: {
_csrf: "",
username: "admin",
password: "wsYdVrLTYnLbzN6e",
},
});
const handleGenerateToken = async () => {
const token = uuid();
loginForm.value.state._csrf = token;
document.cookie = `XSRF-TOKEN=${token}; Path=/;`;
};
const handleLogin = async () => {
try {
loginForm.value.logging = true;
await axiosInstance.post(
`http://localhost:8090/login`,
qs.stringify(loginForm.value.state),
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
} catch (e) {
console.error(e);
} finally {
await router.replace({ name: "Dashboard" });
loginForm.value.logging = false;
}
};
onMounted(() => {
handleGenerateToken();
});
</script>
<template>
<div class="flex h-screen flex-col items-center justify-center">
<img :src="logo" alt="Logo" class="mb-8 w-20" />
<div class="login-form w-72">
<VSpace class="w-full" direction="column" spacing="lg">
<VInput
v-model="loginForm.state.username"
placeholder="用户名"
></VInput>
<VInput v-model="loginForm.state.password" placeholder="密码"></VInput>
<VButton block type="secondary" @click="handleLogin"></VButton>
</VSpace>
</div>
</div>
</template>

View File

@ -9,12 +9,18 @@ import UserDetail from "./UserDetail.vue";
import ProfileModification from "./ProfileModification.vue";
import PasswordChange from "./PasswordChange.vue";
import PersonalAccessTokens from "./PersonalAccessTokens.vue";
import Login from "./Login.vue";
import { IconUserSettings } from "@halo-dev/components";
export default definePlugin({
name: "userModule",
components: [],
routes: [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/users",
component: BlankLayout,