Created Profile page in frontend
This commit is contained in:
@@ -13,7 +13,7 @@ async function start_redirect() {
|
||||
if (!token) return;
|
||||
const root = await FS.get_root(token);
|
||||
if (isErrorResponse(root)) return jwt.logout();
|
||||
await router.push({
|
||||
await router.replace({
|
||||
name: 'fs',
|
||||
params: { node_id: root.rootId }
|
||||
});
|
||||
|
||||
@@ -48,14 +48,14 @@ async function login() {
|
||||
<template v-if="!requestOtp">
|
||||
<input type="email" placeholder="Email" v-model="username" />
|
||||
<input type="password" placeholder="Password" v-model="password" />
|
||||
<a href="/api/auth/gitlab">Login with gitlab</a>
|
||||
<router-link to="signup">Signup instead?</router-link>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div>Please input your 2 factor authentication code</div>
|
||||
<input type="text" placeholder="Code" v-model="otp" />
|
||||
</template>
|
||||
<button @click="login()">Login</button>
|
||||
<a href="/api/auth/gitlab">Login with gitlab</a>
|
||||
<router-link to="signup">Signup instead?</router-link>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
124
frontend/src/views/ProfileView.vue
Normal file
124
frontend/src/views/ProfileView.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, onBeforeMount } from 'vue';
|
||||
import {
|
||||
Auth,
|
||||
User,
|
||||
check_token,
|
||||
isErrorResponse,
|
||||
TokenInjectType,
|
||||
Responses
|
||||
} from '@/api';
|
||||
import { onBeforeRouteUpdate } from 'vue-router';
|
||||
|
||||
const error = ref('');
|
||||
const oldPw = ref('');
|
||||
const newPw = ref('');
|
||||
const newPw2 = ref('');
|
||||
const user = ref<Responses.User.UserInfoResponse | null>(null);
|
||||
|
||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
|
||||
|
||||
onBeforeRouteUpdate(async () => {
|
||||
await updateProfile();
|
||||
});
|
||||
onBeforeMount(async () => {
|
||||
await updateProfile();
|
||||
});
|
||||
async function updateProfile() {
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
|
||||
const res = await User.get_user_info(token);
|
||||
if (isErrorResponse(res)) return jwt.logout();
|
||||
user.value = res;
|
||||
}
|
||||
|
||||
async function deleteUser() {
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
await User.delete_user(token);
|
||||
jwt.logout();
|
||||
}
|
||||
|
||||
async function logoutAll() {
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
await Auth.logout_all(token);
|
||||
jwt.logout();
|
||||
}
|
||||
|
||||
async function changePw() {
|
||||
if (oldPw.value === '' || newPw.value === '' || newPw2.value === '') {
|
||||
error.value = 'Password missing';
|
||||
return;
|
||||
}
|
||||
if (newPw.value !== newPw2.value) {
|
||||
error.value = "Passwords don't match";
|
||||
return;
|
||||
}
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
const res = await Auth.change_password(oldPw.value, newPw.value, token);
|
||||
if (isErrorResponse(res))
|
||||
error.value = 'Password change failed: ' + res.message;
|
||||
else jwt.logout();
|
||||
}
|
||||
|
||||
async function tfaDisable() {
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
await Auth.tfa_disable(token);
|
||||
jwt.logout();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="user">
|
||||
<div v-if="error !== ''" v-text="error"></div>
|
||||
<div>User: {{ user.name }}</div>
|
||||
<div>Signed in with {{ user.gitlab ? 'gitlab' : 'password' }}</div>
|
||||
<template v-if="!user.gitlab">
|
||||
<div>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Old password"
|
||||
v-model="oldPw"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="New password"
|
||||
v-model="newPw"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Repeat new password"
|
||||
v-model="newPw2"
|
||||
/>
|
||||
<button @click="changePw">Change</button>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
2 Factor authentication:
|
||||
{{ user.tfaEnabled ? 'Enabled' : 'Disabled' }}
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" v-if="user.tfaEnabled" @click="tfaDisable">
|
||||
Disable
|
||||
</a>
|
||||
<router-link to="/profile/2fa-enable" v-else>
|
||||
Enable
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<a href="#" @click="logoutAll">Logout everywhere</a>
|
||||
<a href="#" @click="deleteUser">Delete Account</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div>Loading...</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -2,9 +2,9 @@
|
||||
import { ref } from 'vue';
|
||||
import { Auth, isErrorResponse } from '@/api';
|
||||
|
||||
let username = ref('');
|
||||
let password = ref('');
|
||||
let password2 = ref('');
|
||||
const username = ref('');
|
||||
const password = ref('');
|
||||
const password2 = ref('');
|
||||
const error = ref('');
|
||||
|
||||
async function signup() {
|
||||
|
||||
89
frontend/src/views/TFAView.vue
Normal file
89
frontend/src/views/TFAView.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, inject } from 'vue';
|
||||
import { Auth, check_token, isErrorResponse, TokenInjectType } from '@/api';
|
||||
|
||||
enum state {
|
||||
SELECT,
|
||||
MAIL,
|
||||
TOTP
|
||||
}
|
||||
|
||||
const currentState = ref<state>(state.SELECT);
|
||||
|
||||
const error = ref('');
|
||||
const qrImage = ref('');
|
||||
const secret = ref('');
|
||||
const code = ref('');
|
||||
|
||||
const jwt = inject<TokenInjectType>('jwt') as TokenInjectType;
|
||||
|
||||
async function selectMail() {
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
error.value = 'Working...';
|
||||
const res = await Auth.tfa_setup(true, token);
|
||||
if (isErrorResponse(res))
|
||||
error.value = 'Failed to select 2fa type: ' + res.message;
|
||||
else {
|
||||
error.value = '';
|
||||
currentState.value = state.MAIL;
|
||||
}
|
||||
}
|
||||
|
||||
async function selectTotp() {
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
error.value = 'Working...';
|
||||
const res = await Auth.tfa_setup(false, token);
|
||||
if (isErrorResponse(res))
|
||||
error.value = 'Failed to select 2fa type: ' + res.message;
|
||||
else {
|
||||
qrImage.value = res.qrCode;
|
||||
secret.value = res.secret;
|
||||
error.value = '';
|
||||
currentState.value = state.TOTP;
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
const token = await check_token(jwt);
|
||||
if (!token) return;
|
||||
error.value = 'Working...';
|
||||
const res = await Auth.tfa_complete(
|
||||
currentState.value === state.MAIL,
|
||||
code.value,
|
||||
token
|
||||
);
|
||||
if (isErrorResponse(res))
|
||||
error.value = 'Failed to submit code: ' + res.message;
|
||||
else jwt.logout();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="error !== ''" v-text="error"></div>
|
||||
<template v-if="currentState === state.SELECT">
|
||||
<div>Select 2 Factor authentication type:</div>
|
||||
<div>
|
||||
<button @click="selectMail">Mail</button>
|
||||
<button @click="selectTotp">Google Authenticator</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="currentState === state.MAIL">
|
||||
<div>Please enter the code you got by mail</div>
|
||||
<input type="text" placeholder="Code" v-model="code" />
|
||||
<button @click="submit()">Submit</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<img :src="qrImage" alt="QrCode" />
|
||||
<details>
|
||||
<summary>Show manual input code</summary>
|
||||
{{ secret }}
|
||||
</details>
|
||||
<div>Please enter the current code</div>
|
||||
<input type="text" placeholder="Code" v-model="code" />
|
||||
<button @click="submit()">Submit</button>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user