Tests connexion
This commit is contained in:
parent
edf23bdbaa
commit
fb58a24170
8
app.vue
8
app.vue
|
|
@ -34,12 +34,12 @@ onMounted(() => {
|
|||
</svg>
|
||||
</div>
|
||||
<div class="gapx-3 flex align-stretch">
|
||||
<NuxtLink @click="hideLeftPanel" class="site-nav-bar-text" aria-label="Accueil" :href="'/'">
|
||||
<NuxtLink @click="hideLeftPanel" class="site-nav-bar-text" aria-label="Accueil" to="/">
|
||||
<ThemeIcon icon="logo" :width=40 :height=40 />
|
||||
</NuxtLink>
|
||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Systeme" :href="'/explorer'"
|
||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Systeme" to="/explorer"
|
||||
:class="{'mod-active': $route.path.startsWith('/explorer')}">Systeme</NuxtLink>
|
||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Outils" :href="'/tools'"
|
||||
<NuxtLink class="site-nav-bar-text mobile-hidden" aria-label="Outils" to="/tools"
|
||||
:class="{'mod-active': $route.path.startsWith('/tools')}">Outils</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -48,7 +48,7 @@ onMounted(() => {
|
|||
</div>
|
||||
<div class="ps-1 gapx-1 flex align-center">
|
||||
<ThemeSwitch class="mobile-hidden" />
|
||||
<NuxtLink class="site-login" href="/user/profile">
|
||||
<NuxtLink class="site-login" to="/user/profile">
|
||||
<ThemeIcon icon="user" :width=32 :height=32 />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -271,3 +271,10 @@ html.light-mode .light-block {
|
|||
box-sizing: content-box;
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
|
||||
.input-form a {
|
||||
padding: .5em;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
font-weight: var(--font-extrabold);
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ function hidePreview(e: Event) {
|
|||
}
|
||||
</script>
|
||||
|
||||
<template >
|
||||
<template>
|
||||
<template v-if="href !== ''">
|
||||
<a @mouseenter="(e) => showPreview(e, true)" @mouseleave="hidePreview" v-bind="$attrs"
|
||||
:href="stringifyParsedURL({ host: '/explorer', pathname: (content?._path ?? href) + (isTag && anchor ? '/' + anchor.substring(1) : ''), hash: !isTag ? anchor : '', search: '' })"
|
||||
|
|
@ -160,7 +160,7 @@ function hidePreview(e: Event) {
|
|||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<a v-bind="$attrs" :href="href" :target="target" v-else>
|
||||
<NuxtLink v-bind="$attrs" :to="href" v-else>
|
||||
<slot></slot>
|
||||
</a>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
interface Props
|
||||
{
|
||||
toc: Toc;
|
||||
comments?: Comment[];
|
||||
}
|
||||
const props = defineProps<Props>();
|
||||
</script>
|
||||
|
|
@ -9,14 +10,11 @@ const props = defineProps<Props>();
|
|||
<template>
|
||||
<div class="site-body-right-column">
|
||||
<div class="site-body-right-column-inner">
|
||||
<div v-if="comments !== undefined">
|
||||
{{ comments.length }} commentaire{{ comments.length === 1 ? '' : 's' }}
|
||||
</div>
|
||||
<div v-if="!!toc" class="outline-view-outer node-insert-event">
|
||||
<div class="list-item published-section-header">
|
||||
<span class="published-section-header-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list">
|
||||
<line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="3.01" y2="18"></line>
|
||||
</svg>
|
||||
</span>
|
||||
<span>Sur cette page</span>
|
||||
</div>
|
||||
<div class="outline-view">
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
export enum AuthStatus
|
||||
{
|
||||
disconnected, loading, connected
|
||||
};
|
||||
export interface Auth
|
||||
{
|
||||
id: Ref<number>;
|
||||
data: Ref<Record<string, any>>;
|
||||
sessionId: Ref<string>;
|
||||
status: Ref<AuthStatus>;
|
||||
|
||||
lastRefresh: Ref<Date>;
|
||||
|
||||
register: (username: string, email: string, password: string, data?: Record<string, any>) => Promise<any>;
|
||||
login: (usernameOrEmail: string, password: string) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
|
||||
refresh: () => Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
async function register(username: string, email: string, password: string, additionalData?: Record<string, any>): Promise<any>
|
||||
{
|
||||
const id = useState<number>("auth:id");
|
||||
const data = useState<any>("auth:data");
|
||||
const sessionId = useState<string>("auth:sessionId");
|
||||
const status = useState<AuthStatus>("auth:status");
|
||||
const lastRefresh = useState<Date>("auth:date");
|
||||
status.value = AuthStatus.loading;
|
||||
|
||||
try
|
||||
{
|
||||
const result = await $fetch("/api/auth/register", {
|
||||
method: 'POST',
|
||||
body: { username, email, password, additionalData },
|
||||
ignoreResponseError: true,
|
||||
});
|
||||
|
||||
if(result.success)
|
||||
{
|
||||
id.value = result.id!;
|
||||
data.value = { ...additionalData, username: username, email: email };
|
||||
sessionId.value = result.sessionId!;
|
||||
status.value = AuthStatus.connected;
|
||||
lastRefresh.value = new Date();
|
||||
|
||||
return;
|
||||
}
|
||||
else if(result.error)
|
||||
{
|
||||
status.value = AuthStatus.disconnected;
|
||||
|
||||
return result.error;
|
||||
}
|
||||
else
|
||||
{
|
||||
status.value = AuthStatus.disconnected;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
console.log(JSON.stringify(e));
|
||||
status.value = AuthStatus.disconnected;
|
||||
}
|
||||
}
|
||||
async function login(usernameOrEmail: string, password: string): Promise<void>
|
||||
{
|
||||
const status = useState<AuthStatus>("auth:status");
|
||||
status.value = AuthStatus.disconnected;
|
||||
}
|
||||
async function logout(): Promise<void>
|
||||
{
|
||||
const status = useState<AuthStatus>("auth:status");
|
||||
status.value = AuthStatus.disconnected;
|
||||
}
|
||||
|
||||
async function refresh(): Promise<void>
|
||||
{
|
||||
const status = useState<AuthStatus>("auth:status");
|
||||
status.value = AuthStatus.disconnected;
|
||||
}
|
||||
|
||||
export default function useAuth(): Auth {
|
||||
const id = useState<number>("auth:id", () => 0);
|
||||
const data = useState<any>("auth:data", () => { });
|
||||
const sessionId = useState<string>("auth:sessionId", () => '');
|
||||
const status = useState<AuthStatus>("auth:status", () => AuthStatus.disconnected);
|
||||
|
||||
const lastRefresh = useState<Date>("auth:date", () => new Date());
|
||||
|
||||
return {
|
||||
id, data, sessionId, status, lastRefresh,
|
||||
register, login, logout, refresh
|
||||
};
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
export default defineNuxtRouteMiddleware((to) => {
|
||||
const meta = to.meta.auth;
|
||||
|
||||
//useSession(to.)
|
||||
|
||||
return to;
|
||||
})
|
||||
|
|
@ -7,18 +7,22 @@ function toggleLeftPanel(_: Event) {
|
|||
|
||||
<script setup lang="ts">
|
||||
const { hash, path } = useRoute();
|
||||
const route = path.substring(path.indexOf('/explorer') + '/explorer'.length);
|
||||
|
||||
const { data: page } = await useAsyncData('home', queryContent(path.substring(path.indexOf('/explorer') + '/explorer'.length)).findOne);
|
||||
const { data: page } = await useAsyncData('home', queryContent(route).findOne);
|
||||
const { data: comments } = await useFetch(`/api/comments`, {
|
||||
query: {
|
||||
route: route
|
||||
}
|
||||
});
|
||||
|
||||
if(page.value)
|
||||
useContentHead(page.value!);
|
||||
|
||||
if(hash)
|
||||
{
|
||||
console.log(document.getElementById(hash.substring(1)));
|
||||
document.getElementById(hash.substring(1))?.scrollTo({ behavior: 'smooth', top: 100 });
|
||||
useContentHead(page.value!);
|
||||
}
|
||||
|
||||
const content = ref();
|
||||
|
||||
onMounted(() => {
|
||||
document.querySelectorAll('.callout.is-collapsible .callout-title').forEach(e => {
|
||||
e.addEventListener('click', (_) => {
|
||||
|
|
@ -53,7 +57,7 @@ onMounted(() => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<RightComponent v-if="page.body?.toc" :toc="page?.body?.toc" />
|
||||
<RightComponent v-if="page.body?.toc" :toc="page?.body?.toc" :comments="comments" />
|
||||
</template>
|
||||
<CanvasRenderer v-else-if="!!page && page._type == 'canvas'" :key="page._id" :canvas="page" />
|
||||
<div v-else-if="!!page">
|
||||
|
|
|
|||
|
|
@ -1,14 +1,70 @@
|
|||
<script setup lang="ts">
|
||||
import { schema, type Login } from '~/schemas/login';
|
||||
|
||||
definePageMeta({
|
||||
auth: {
|
||||
unauthenticatedOnly: true,
|
||||
navigateAuthenticatedTo: '/user/profile'
|
||||
disconnectedOnly: true,
|
||||
connectedRedirect: '/user/profile'
|
||||
}
|
||||
});
|
||||
|
||||
const state = reactive<Login>({
|
||||
username: '',
|
||||
password: ''
|
||||
});
|
||||
|
||||
const { status, login } = useAuth();
|
||||
|
||||
const usernameError = ref("");
|
||||
const passwordError = ref("");
|
||||
|
||||
async function submit()
|
||||
{
|
||||
const data = schema.safeParse(state);
|
||||
|
||||
if(data.success && state.password !== "")
|
||||
{
|
||||
let errors = await login(data.data.username, data.data.password);
|
||||
|
||||
if(status.value === AuthStatus.connected)
|
||||
{
|
||||
await navigateTo('/user/profile', { replace: true });
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = errors?.issues ?? errors;
|
||||
usernameError.value = errors?.find((e: any) => e.path.includes("username"))?.message ?? "";
|
||||
passwordError.value = errors?.find((e: any) => e.path.includes("password"))?.message ?? "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usernameError.value = data.error?.issues.find(e => e.path.includes("username"))?.message ?? "";
|
||||
passwordError.value = data.error?.issues.find(e => e.path.includes("password"))?.message ?? "";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head>
|
||||
<Title>Se connecter</Title>
|
||||
</Head>
|
||||
<div class="site-body-center-column">
|
||||
<div class="render-container flex align-center justify-center">
|
||||
<form v-if="status === AuthStatus.disconnected" @submit.prevent="submit" class="input-form input-form-wide">
|
||||
<h1>Connexion</h1>
|
||||
<Input type="text" autocomplete="username" v-model="state.username"
|
||||
placeholder="" title="Nom d'utilisateur ou adresse mail" :error="usernameError" />
|
||||
<Input type="password" autocomplete="current-password" v-model="state.password"
|
||||
placeholder="" title="Mot de passe"
|
||||
:error="passwordError" />
|
||||
<button>Se connecter</button>
|
||||
<NuxtLink to="/user/register">Pas de compte ?</NuxtLink>
|
||||
</form>
|
||||
<div v-else-if="status === AuthStatus.loading" class="input-form"><div class="loading"></div></div>
|
||||
<div v-else class="not-found-container">
|
||||
<div class="not-found-title">👀 Vous n'avez rien à faire ici. 👀</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
auth: {
|
||||
unauthenticatedOnly: false,
|
||||
navigateUnauthenticatedTo: '/user/login'
|
||||
disconnectedOnly: false,
|
||||
disconnectedRedirect: '/user/login'
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { schema, type Registration } from '~/schemas/registration';
|
|||
|
||||
definePageMeta({
|
||||
auth: {
|
||||
unauthenticatedOnly: true,
|
||||
navigateAuthenticatedTo: '/user/profile'
|
||||
disconnectedOnly: true,
|
||||
connectedRedirect: '/user/profile'
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -86,7 +86,8 @@ async function submit()
|
|||
<Input type="password" v-model="confirmPassword" placeholder="Confirmer le mot de passe"
|
||||
title="Confirmer le mot de passe"
|
||||
:error="confirmPassword === '' || confirmPassword === state.password ? '' : 'Les mots de passe saisies ne sont pas identique'" />
|
||||
<button>Valider</button>
|
||||
<button>S'inscrire</button>
|
||||
<NuxtLink to="/user/login">Se connecter</NuxtLink>
|
||||
</form>
|
||||
<div v-else-if="status === AuthStatus.loading" class="input-form"><div class="loading"></div></div>
|
||||
<div v-else class="not-found-container">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const schema = z.object({
|
||||
username: z.string({ required_error: "Nom d'utilisateur obligatoire" }),
|
||||
password: z.string({ required_error: "Mot de passe obligatoire" }),
|
||||
});
|
||||
|
||||
export type Login = z.infer<typeof schema>;
|
||||
|
|
@ -1,3 +1,96 @@
|
|||
export default defineEventHandler(async (e) => {
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { schema } from '~/schemas/login';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const { sessionPassword } = useRuntimeConfig();
|
||||
const session = await useSession(e, {
|
||||
password: sessionPassword,
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
const db = useDatabase();
|
||||
|
||||
console.log(session.id);
|
||||
|
||||
if(session.id && session.data.id)
|
||||
{
|
||||
const checkSession = db.query("SELECT user_id FROM user_sessions WHERE id = ?1");
|
||||
const sessionId = checkSession.get(session.id) as any;
|
||||
|
||||
console.log(sessionId);
|
||||
|
||||
if(sessionId && sessionId.user_id === session.data.id)
|
||||
{
|
||||
return { success: true, id: session.data.id, sessionId: session.id, data: session.data };
|
||||
}
|
||||
else
|
||||
{
|
||||
session.clear();
|
||||
|
||||
setResponseStatus(e, 406);
|
||||
return { success: false, error: { path: ['global'], message: 'Vous êtes déjà connecté' } };
|
||||
}
|
||||
|
||||
}
|
||||
const body = await readValidatedBody(e, schema.safeParse);
|
||||
|
||||
if (!body.success)
|
||||
{
|
||||
session.clear();
|
||||
|
||||
setResponseStatus(e, 406);
|
||||
return { success: false, error: body.error };
|
||||
}
|
||||
|
||||
|
||||
const hash = await Bun.password.hash(body.data.password);
|
||||
const checkID = db.query(`SELECT id FROM users WHERE (username = ?1 or email = ?1)`);
|
||||
const id = checkID.get(body.data.username) as any;
|
||||
|
||||
if(!id || !id.id)
|
||||
{
|
||||
session.clear();
|
||||
|
||||
setResponseStatus(e, 401);
|
||||
return { success: false, error: { path: ['username'], message: 'Identifiant inconnu' } };
|
||||
}
|
||||
|
||||
const checkHash = db.query(`SELECT COUNT(*) as count FROM users WHERE id = ?1 and hash = ?2`);
|
||||
const validation = checkHash.get(id.id, hash) as any;
|
||||
|
||||
if(validation && validation.count && validation.count !== 1)
|
||||
{
|
||||
session.clear();
|
||||
|
||||
setResponseStatus(e, 401);
|
||||
return { success: false, error: { path: ['password'], message: 'Mot de passe incorrect' } };
|
||||
}
|
||||
|
||||
const loggingIn = db.query(`INSERT INTO user_sessions(id, user_id, ip, agent, lastRefresh) VALUES(?1, ?2, ?3, ?4, ?5)`);
|
||||
loggingIn.get(session.id, id.id, getRequestIP(e), getRequestHeader(e, 'User-Agent'), Date.now());
|
||||
|
||||
await session.update(getData(db, id.id));
|
||||
|
||||
setResponseStatus(e, 201);
|
||||
return { success: true, id: id.id, sessionId: session.id, data: session.data };
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
session.clear();
|
||||
|
||||
console.error(e);
|
||||
return { success: false, error: e };
|
||||
}
|
||||
});
|
||||
|
||||
function getData(db: Database, id: string): any
|
||||
{
|
||||
const userQuery = db.query(`SELECT * FROM users WHERE id = ?1`);
|
||||
const user = userQuery.get(id);
|
||||
|
||||
const userDataQuery = db.query(`SELECT * FROM users_data WHERE user_id = ?1`);
|
||||
const userData = userDataQuery.get(id);
|
||||
|
||||
return { ...user, ...userData };
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
export default defineEventHandler(async (e) => {
|
||||
});
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import useDatabase from '~/composables/useDatabase';
|
||||
|
||||
export interface Comment
|
||||
{
|
||||
user: number;
|
||||
file: string;
|
||||
position: number;
|
||||
length: number;
|
||||
sequence: number;
|
||||
text: string;
|
||||
}
|
||||
export default defineEventHandler(async (e) => {
|
||||
const query = getQuery(e);
|
||||
|
||||
if(query && query.route !== undefined)
|
||||
{
|
||||
const db = useDatabase();
|
||||
|
||||
const comments = db.query("SELECT * FROM comments WHERE file = ?1").all(query.route as string) as Comment[];
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
setResponseStatus(e, 404);
|
||||
});
|
||||
Loading…
Reference in New Issue