Frontend created and rewritten a few times, with some backend fixes #1
|
@ -1,52 +1,38 @@
|
|||
<template>
|
||||
<button v-if="store.isUserLogged" @click="logout" class="action-button">
|
||||
Logout
|
||||
Logout {{ store.user.username }}
|
||||
</button>
|
||||
<ModalDialog v-else title="Login">
|
||||
<form @submit="login">
|
||||
<label for="username">Username:</label
|
||||
><input
|
||||
id="username"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
required
|
||||
v-model="loginForm.username"
|
||||
/>
|
||||
<label for="password">Password:</label
|
||||
><input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
v-model="loginForm.password"
|
||||
/>
|
||||
<ModalDialog v-if="store.user.isOpenLoginModal" title="Login">
|
||||
<form @submit.prevent="login">
|
||||
<div class="login-container">
|
||||
<label for="username">Username:</label>
|
||||
<input
|
||||
id="username"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
required
|
||||
v-model="loginForm.username"
|
||||
/>
|
||||
<label for="password">Password:</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
v-model="loginForm.password"
|
||||
/>
|
||||
</div>
|
||||
<h3 v-if="loginForm.error.length > 0" class="error-text">
|
||||
{{ loginForm.error }}
|
||||
</h3>
|
||||
<input type="submit" />
|
||||
<input type="submit" class="button-login" />
|
||||
</form>
|
||||
</ModalDialog>
|
||||
|
||||
<!--
|
||||
|
||||
<a-tooltip title="Login">
|
||||
<template v-if="DocumentStore.isUserLogged">
|
||||
<a-button @click="logout" type="text" class="action-button" :icon="h(UserDeleteOutlined)" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button @click="showModal" type="text" class="action-button" :icon="h(UserOutlined)" />
|
||||
</template>
|
||||
</a-tooltip>
|
||||
<a-modal v-model:open="DocumentStore.user.isOpenLoginModal" :confirm-loading="confirmLoading" okText="Login" @ok="login">
|
||||
<div class="login-container">
|
||||
</div>
|
||||
</a-modal>
|
||||
-->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { loginUser, logoutUser } from '@/repositories/User'
|
||||
import type { ISimpleError } from '@/repositories/Client'
|
||||
import { useDocumentStore } from '@/stores/documents'
|
||||
|
@ -62,7 +48,7 @@ const logout = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const loginForm = ref({
|
||||
const loginForm = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
error: ''
|
||||
|
@ -70,16 +56,15 @@ const loginForm = ref({
|
|||
|
||||
const login = async () => {
|
||||
try {
|
||||
loginForm.value.error = ''
|
||||
loginForm.error = ''
|
||||
confirmLoading.value = true
|
||||
const user = await loginUser(loginForm.value.username, loginForm.value.password)
|
||||
if (user) {
|
||||
location.reload()
|
||||
}
|
||||
const msg = await loginUser(loginForm.username, loginForm.password)
|
||||
console.log('Logged in', msg)
|
||||
store.login(msg.username, !!msg.privileged)
|
||||
} catch (error) {
|
||||
const httpError = error as ISimpleError
|
||||
if (httpError.name) {
|
||||
loginForm.value.error = httpError.message
|
||||
loginForm.error = httpError.message
|
||||
}
|
||||
} finally {
|
||||
confirmLoading.value = false
|
||||
|
@ -90,11 +75,14 @@ const login = async () => {
|
|||
<style scoped>
|
||||
.login-container {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.button-login {
|
||||
margin-left: auto;
|
||||
background-color: var(--secondary-color);
|
||||
color: var(--secondary-background);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
/* Base domain for all request */
|
||||
export const baseURL = import.meta.env.VITE_URL_DOCUMENT
|
||||
|
||||
class ClientClass {
|
||||
async post(url: string, data?: Record<string, any>): Promise<any> {
|
||||
const res = await fetch(`${baseURL}/`, {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
|
@ -11,7 +8,12 @@ class ClientClass {
|
|||
},
|
||||
body: data !== undefined ? JSON.stringify(data) : undefined
|
||||
})
|
||||
const msg = await res.json()
|
||||
let msg
|
||||
try {
|
||||
msg = await res.json()
|
||||
} catch (e) {
|
||||
throw new SimpleError(res.status, `HTTP ${res.status} ${res.statusText}`)
|
||||
}
|
||||
if ('error' in msg) throw new SimpleError(msg.error.code, msg.error.message)
|
||||
return msg
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { DocumentStore } from '@/stores/documents'
|
||||
import { useDocumentStore } from '@/stores/documents'
|
||||
import createWebSocket from './WS'
|
||||
|
||||
export type FUID = string
|
||||
|
||||
|
@ -63,6 +64,19 @@ export class DocumentHandler {
|
|||
|
||||
handleWebSocketMessage(event: MessageEvent) {
|
||||
const msg = JSON.parse(event.data)
|
||||
if ("error" in msg) {
|
||||
if (msg.error.code === 401) {
|
||||
this.store.user.isLoggedIn = false
|
||||
this.store.user.isOpenLoginModal = true
|
||||
} else {
|
||||
this.store.error = msg.error.message
|
||||
}
|
||||
// The server closes the websocket after errors, so we need to reopen it
|
||||
setTimeout(
|
||||
() => { this.store.wsWatch = createWebSocket(url_document_watch_ws, this.handleWebSocketMessage)},
|
||||
1000
|
||||
)
|
||||
}
|
||||
switch (true) {
|
||||
case !!msg.root:
|
||||
this.handleRootMessage(msg)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import ExplorerView from '../views/ExplorerView.vue'
|
||||
import ExplorerView from '@/views/ExplorerView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
|
|
|
@ -15,6 +15,8 @@ type DirectoryData = {
|
|||
[filename: string]: FileData
|
||||
}
|
||||
type User = {
|
||||
username: string
|
||||
privileged: boolean
|
||||
isOpenLoginModal: boolean
|
||||
isLoggedIn: boolean
|
||||
}
|
||||
|
@ -42,7 +44,7 @@ export const useDocumentStore = defineStore({
|
|||
wsWatch: undefined,
|
||||
wsUpload: undefined,
|
||||
error: '' as string,
|
||||
user: { isLoggedIn: false, isOpenLoginModal: false } as User
|
||||
user: { username: "", privileged: false, isLoggedIn: false, isOpenLoginModal: false } as User
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
@ -137,6 +139,12 @@ export const useDocumentStore = defineStore({
|
|||
for (const d of this.document) {
|
||||
if ('mtime' in d) d.modified = formatUnixDate(d.mtime)
|
||||
}
|
||||
},
|
||||
login(username: string, privileged: boolean) {
|
||||
this.user.username = username
|
||||
this.user.privileged = privileged
|
||||
this.user.isLoggedIn = true
|
||||
this.user.isOpenLoginModal = false
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<script type="module" crossorigin src="/assets/index-2034a7a8.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-c3cea0a2.css">
|
||||
<script type="module" crossorigin src="/assets/index-eb4428c4.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-683ba1dc.css">
|
||||
|
||||
<meta charset=UTF-8>
|
||||
<title>Cista</title>
|
||||
|
|
Loading…
Reference in New Issue
Block a user