Frontend created and rewritten a few times, with some backend fixes #1

Merged
leo merged 110 commits from plaintable into main 2023-11-08 20:38:40 +00:00
8 changed files with 66 additions and 64 deletions
Showing only changes of commit b759d8324c - Show all commits

View File

@ -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);
}

View File

@ -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
}

View File

@ -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)

View File

@ -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),

View File

@ -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

View File

@ -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>