mirror of
https://github.com/Floriansylvain/RenewCMS.git
synced 2026-08-19 19:53:21 +02:00
Login page implementation
This commit is contained in:
@@ -0,0 +1,76 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Frank+Ruhl+Libre:wght@500&family=Hind:wght@400;500;600;700&family=Nunito:wght@500;600;700&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--regular-weight: 400;
|
||||||
|
--medium-weight: 500;
|
||||||
|
--semibold-weight: 600;
|
||||||
|
--bold-weight: 700;
|
||||||
|
|
||||||
|
--radius: 10px;
|
||||||
|
|
||||||
|
--primary: #2CD358;
|
||||||
|
--primary-dark: #00BA2A;
|
||||||
|
--primary-light: #97E6A2;
|
||||||
|
--secondary: #D32CA6;
|
||||||
|
--secondary-dark: #BA0094;
|
||||||
|
--secondary-light: #E88DCA;
|
||||||
|
--neutral-dark: #121212;
|
||||||
|
--neutral-light: #aaaaaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: 'Frank Ruhl Libre', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, h3, h4, h5, h6 {
|
||||||
|
font-family: 'Nunito', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: 'Hind', sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
color: var(--neutral-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input label {
|
||||||
|
font-weight: var(--medium-weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input input {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 8px;
|
||||||
|
|
||||||
|
border: solid 1.25px var(--neutral-light);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
background-color: var(--primary);
|
||||||
|
|
||||||
|
font-weight: var(--medium-weight);
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary:hover {
|
||||||
|
background-color: var(--primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary:active {
|
||||||
|
background-color: var(--primary-dark);
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
|
||||||
import './assets/main.css'
|
import '@/assets/base.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import Home from '@/views/Home.vue'
|
import Home from '@/views/Home.vue'
|
||||||
|
import Debug from '@/views/Debug.vue'
|
||||||
|
import Login from '@/views/Login.vue'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@@ -9,6 +11,16 @@ const router = createRouter({
|
|||||||
name: 'home',
|
name: 'home',
|
||||||
component: Home
|
component: Home
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/debug',
|
||||||
|
name: 'debug',
|
||||||
|
component: Debug
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: Login
|
||||||
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
export interface jwtFormat {
|
||||||
|
code: number,
|
||||||
|
expire: string,
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore("AuthStore", () => {
|
||||||
|
const expire = ref("")
|
||||||
|
const token = ref("")
|
||||||
|
|
||||||
|
return { expire, token }
|
||||||
|
})
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// import EditorJS from '@editorjs/editorjs';
|
||||||
|
import { useAuthStore, type jwtFormat } from '@/stores/AuthStore';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const email = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
function getArticles() {
|
||||||
|
fetch(`http://localhost:8080/articles/`, {
|
||||||
|
headers: { "Authorization": `Bearer ${useAuthStore().token}` }
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => console.log(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
function jwtHandler(apiResponse: jwtFormat): void {
|
||||||
|
if (apiResponse.code !== 200) {
|
||||||
|
console.log(apiResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
useAuthStore().token = apiResponse.token
|
||||||
|
useAuthStore().expire = apiResponse.expire
|
||||||
|
console.log('success! logged in.')
|
||||||
|
}
|
||||||
|
|
||||||
|
function login(email: string, password: string): void {
|
||||||
|
fetch(`http://localhost:8080/login/`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
password: password
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => jwtHandler(result))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>Connexion</h2>
|
||||||
|
|
||||||
|
<form @submit.prevent="login(email, password)">
|
||||||
|
<div>
|
||||||
|
<label for="email">Email address</label>
|
||||||
|
<input id="email" name="email" placeholder="email" type="text" v-model="email">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input id="password" name="password" placeholder="password" type="password" v-model="password">
|
||||||
|
</div>
|
||||||
|
<button type="submit">Se connecter</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<button @click="getArticles()">get all articles</button>
|
||||||
|
<button @click="getArticles()">get all articles</button>
|
||||||
|
<button @click="getArticles()">get all articles</button>
|
||||||
|
<button @click="getArticles()">get all articles</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,77 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import EditorJS from '@editorjs/editorjs';
|
|
||||||
import { pingApi } from '@/utils/ping';
|
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
|
|
||||||
const inputArticleID = ref('')
|
|
||||||
const username = ref('')
|
|
||||||
const password = ref('')
|
|
||||||
|
|
||||||
onMounted(async function() {
|
|
||||||
console.log('ping...')
|
|
||||||
const pong = await pingApi()
|
|
||||||
console.log(pong)
|
|
||||||
})
|
|
||||||
|
|
||||||
function addArticle() {
|
|
||||||
fetch(`http://localhost:8080/articles/${Math.random() * 100}`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {"Authorization" : `Basic ${btoa(`${username.value}:${password.value}`)}`},
|
|
||||||
body: JSON.stringify({
|
|
||||||
content: {},
|
|
||||||
date: new Date().getTime()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteArticle(articleID: String) {
|
|
||||||
fetch(`http://localhost:8080/articles/${articleID}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {"Authorization" : `Basic ${btoa(`${username.value}:${password.value}`)}`},
|
|
||||||
body: JSON.stringify({
|
|
||||||
id_name: articleID
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
function getArticle(articleID: String) {
|
|
||||||
fetch(`http://localhost:8080/articles/${articleID}`, {
|
|
||||||
headers: {"Authorization" : `Basic ${btoa(`${username.value}:${password.value}`)}`},
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
function auth(type: string, username: string, password: string) {
|
|
||||||
fetch(`http://localhost:8080/${type}`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
email: username,
|
|
||||||
password: password
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<p>Salut à tous</p>
|
|
||||||
<button @click="addArticle">add rand article</button>
|
|
||||||
<button @click="getArticle('')">display articles in console</button> <br>
|
|
||||||
<label for="articleIDinput"></label>
|
|
||||||
<input id="articleIDinput" name="articleIDinput" type="text" placeholder="article ID" v-model="inputArticleID">
|
|
||||||
<button @click="deleteArticle(inputArticleID)">delete this article</button>
|
|
||||||
<button @click="getArticle(inputArticleID)">get this article</button> <br>
|
|
||||||
|
|
||||||
<input type="text" name="username" id="username" v-model="username" placeholder="username">
|
|
||||||
<input type="text" name="password" id="password" v-model="password" placeholder="password">
|
|
||||||
<button @click="auth('login', username, password)">login</button>
|
|
||||||
<button @click="auth('logout', username, password)">logout</button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAuthStore, type jwtFormat } from '@/stores/AuthStore';
|
||||||
|
import { ref, type Ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const isTokenOK: Ref<boolean|undefined> = ref(undefined)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
function jwtHandler(apiResponse: jwtFormat): void {
|
||||||
|
if (apiResponse.code !== 200) {
|
||||||
|
console.error(apiResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
useAuthStore().token = apiResponse.token
|
||||||
|
useAuthStore().expire = apiResponse.expire
|
||||||
|
isTokenOK.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function login(email: string, password: string): void {
|
||||||
|
fetch(`http://localhost:8080/login/`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
password: password
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => {
|
||||||
|
jwtHandler(result)
|
||||||
|
if (isTokenOK.value === true) {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const email = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="login-page">
|
||||||
|
<div class="login-form">
|
||||||
|
<h2>Connexion à GohCMS</h2>
|
||||||
|
|
||||||
|
<form @submit.prevent="login(email, password)">
|
||||||
|
<div class="inputs-group">
|
||||||
|
<div class="label-input">
|
||||||
|
<label for="email">Adresse mail</label>
|
||||||
|
<input id="email" name="email" placeholder="E-mail" type="text" v-model="email">
|
||||||
|
</div>
|
||||||
|
<div class="label-input">
|
||||||
|
<label for="password">Mot de passe</label>
|
||||||
|
<input id="password" name="password" placeholder="Mot de passe" type="password" v-model="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="button-primary" type="submit">Se connecter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
h2 {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
}
|
||||||
|
.login-page {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 350px;
|
||||||
|
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs-group,
|
||||||
|
.login-form form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form form {
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user