Init
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
import {useAppStore} from "@/stores/app";
|
||||||
|
import {useAuthStore} from "@/stores/auth";
|
||||||
|
|
||||||
|
export function useApi() {
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const baseUrl = `${appStore.baseurl}/api`
|
||||||
|
|
||||||
|
const request = async (endpoint: string, options: RequestInit = {}) => {
|
||||||
|
const headers = new Headers(options.headers)
|
||||||
|
|
||||||
|
if (!headers.has('Content-Type')) {
|
||||||
|
headers.set('Content-Type', 'application/json')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authStore.token) {
|
||||||
|
headers.set('Authorization', `Bearer ${authStore.token}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...options,
|
||||||
|
headers,
|
||||||
|
body: options.body && typeof options.body === 'object'
|
||||||
|
? JSON.stringify(options.body)
|
||||||
|
: options.body
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ici on ne fait QUE la requête.
|
||||||
|
// On laisse l'appelant décider de ce qu'il fait du status (401, 404, etc.)
|
||||||
|
return fetch(`${baseUrl}${endpoint}`, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
request,
|
||||||
|
get: (url: string, options?: RequestInit) =>
|
||||||
|
request(url, {...options, method: 'GET'}),
|
||||||
|
|
||||||
|
post: (url: string, data?: any, options?: RequestInit) =>
|
||||||
|
request(url, {...options, method: 'POST', body: data}),
|
||||||
|
|
||||||
|
put: (url: string, data?: any, options?: RequestInit) =>
|
||||||
|
request(url, {...options, method: 'PUT', body: data}),
|
||||||
|
|
||||||
|
delete: (url: string, options?: RequestInit) =>
|
||||||
|
request(url, {...options, method: 'DELETE'}),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {ref, watch} from 'vue'
|
import {ref, watch} from 'vue'
|
||||||
import {useRouter} from 'vue-router'
|
import {useRouter} from 'vue-router'
|
||||||
import {apiFetch} from '@/plugins/api'
|
import {useApi} from "@/composables/useApi.ts";
|
||||||
|
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -47,17 +50,14 @@ async function handleRegister() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch('/join', {
|
const response = await api.post('/join', body)
|
||||||
method: 'POST',
|
|
||||||
body
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errData = await response.json().catch(() => ({message: 'Erreur lors de l\'inscription'}))
|
const errData = await response.json().catch(() => ({message: 'Erreur lors de l\'inscription'}))
|
||||||
throw new Error(errData.message || 'Échec de l\'inscription')
|
throw new Error(errData.message || 'Échec de l\'inscription')
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push('/login')
|
await router.push('/login')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err instanceof Error ? err.message : 'Une erreur est survenue'
|
error.value = err instanceof Error ? err.message : 'Une erreur est survenue'
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
import {ref} from 'vue'
|
import {ref} from 'vue'
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
import {useRouter} from 'vue-router'
|
import {useRouter} from 'vue-router'
|
||||||
import {apiFetch} from "@/plugins/api.ts";
|
import {useApi} from "@/composables/useApi.ts";
|
||||||
|
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -21,24 +23,26 @@ async function handleLogin() {
|
|||||||
error.value = ''
|
error.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Appel direct via votre helper
|
const response = await api.post('/auth/login', {
|
||||||
const response = await apiFetch('/auth/login', {
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
username: username.value,
|
username: username.value,
|
||||||
password: password.value
|
password: password.value
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (response.status === 401) {
|
||||||
|
error.value = "Identifiants incorrects"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Identifiants invalides')
|
error.value = "Une erreur est survenue lors de la connexion"
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
await authStore.setToken(data.token)
|
await authStore.setToken(data.token)
|
||||||
await router.push('/')
|
await router.push('/')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = 'Erreur de connexion'
|
error.value = 'Impossible de contacter le serveur'
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// frontend/src/utils/api.ts
|
|
||||||
import {useAuthStore} from '@/stores/auth'
|
|
||||||
|
|
||||||
export async function apiFetch(endpoint: string, options: RequestInit = {}) {
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
|
|
||||||
// Remplacer par l'URL réelle de votre backend Rust
|
|
||||||
const baseUrl = 'http://localhost:8080/api'
|
|
||||||
|
|
||||||
const headers = new Headers(options.headers)
|
|
||||||
headers.set('Content-Type', 'application/json')
|
|
||||||
|
|
||||||
if (authStore.token) {
|
|
||||||
headers.set('Authorization', `Bearer ${authStore.token}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si un body est fourni et est un objet, on le stringify automatiquement
|
|
||||||
const config = {
|
|
||||||
...options,
|
|
||||||
headers,
|
|
||||||
body: options.body && typeof options.body === 'object'
|
|
||||||
? JSON.stringify(options.body)
|
|
||||||
: options.body
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(`${baseUrl}${endpoint}`, config)
|
|
||||||
|
|
||||||
if (response.status === 401) {
|
|
||||||
authStore.logout()
|
|
||||||
window.location.href = '/login'
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ import Index from '@/pages/index.vue'
|
|||||||
|
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
@@ -27,6 +28,21 @@ const router = createRouter({
|
|||||||
path: '/',
|
path: '/',
|
||||||
component: Index,
|
component: Index,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Cette regex capture soit un UUID, soit le mot "default"
|
||||||
|
path: '/server/:id(default|[0-9a-fA-F-]{36})',
|
||||||
|
name: 'server-dashboard',
|
||||||
|
component: () => import('@/pages/server/index.vue'),
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'channel/:channelId',
|
||||||
|
name: 'server-channel',
|
||||||
|
component: () => import('@/pages/server/channel/index.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/admin',
|
path: '/admin',
|
||||||
name: 'admin-dashboard',
|
name: 'admin-dashboard',
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// Utilities
|
// Utilities
|
||||||
import { defineStore } from 'pinia'
|
import {defineStore} from 'pinia'
|
||||||
|
|
||||||
export const useAppStore = defineStore('app', {
|
export const useAppStore = defineStore('app', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
//
|
baseurl: 'http://localhost:8080',
|
||||||
}),
|
}),
|
||||||
})
|
actions: {}
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {defineStore} from 'pinia'
|
import {defineStore} from 'pinia'
|
||||||
import {apiFetch} from "@/plugins/api.ts";
|
import {useApi} from "@/composables/useApi";
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string
|
id: string
|
||||||
@@ -30,11 +30,11 @@ export const useAuthStore = defineStore('auth', {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const api = useApi()
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch('/auth/me', {method: 'GET'})
|
const response = await api.get('/auth/me')
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
// On s'attend à ce que /auth/check renvoie l'objet user complet
|
|
||||||
this.user = data.user
|
this.user = data.user
|
||||||
} else {
|
} else {
|
||||||
this.logout()
|
this.logout()
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import {defineStore} from 'pinia'
|
||||||
|
|
||||||
|
export const useCategoryStore = defineStore("category", {
|
||||||
|
state: () => ({}),
|
||||||
|
actions: {}
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import {defineStore} from 'pinia'
|
||||||
|
/*
|
||||||
|
* This file contains the store for channels.
|
||||||
|
* Centralisation des channels de l'ensemble des serveurs pour faciliter l'intéraction depuis les différents éléments du site
|
||||||
|
*/
|
||||||
|
export const useChannelStore = defineStore('channel', {
|
||||||
|
state: () => ({
|
||||||
|
channels: []
|
||||||
|
}),
|
||||||
|
actions: {}
|
||||||
|
})
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import {defineStore} from 'pinia';
|
||||||
|
|
||||||
|
type GatewayStatus = 'disconnected' | 'connecting' | 'connected' | 'error'
|
||||||
|
|
||||||
|
export const useGatewayStore = defineStore('gateway', {
|
||||||
|
state: () => ({
|
||||||
|
socket: null as WebSocket | null,
|
||||||
|
status: 'disconnected' as GatewayStatus,
|
||||||
|
reconnectAttempts: 0,
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async connect() {
|
||||||
|
if (this.socket && this.status === 'connected') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = 'connecting'
|
||||||
|
|
||||||
|
const wsUrl = `ws://localhost:3000/ws`
|
||||||
|
const socket = new WebSocket(wsUrl)
|
||||||
|
|
||||||
|
socket.onopen = () => {
|
||||||
|
this.status = 'connected'
|
||||||
|
this.reconnectAttempts = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.onclose = () => {
|
||||||
|
this.status = 'disconnected'
|
||||||
|
this.socket = null
|
||||||
|
this.scheduleReconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.onerror = () => {
|
||||||
|
this.status = 'error'
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.onmessage = event => {
|
||||||
|
this.handleMessage(event.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket = socket
|
||||||
|
},
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
this.socket?.close()
|
||||||
|
this.socket = null
|
||||||
|
this.status = 'disconnected'
|
||||||
|
},
|
||||||
|
|
||||||
|
async send(payload: object) {
|
||||||
|
if (!this.socket || this.status !== 'connected') {
|
||||||
|
console.warn('WebSocket is not connected')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.send(JSON.stringify(payload))
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleMessage(rawData: string) {
|
||||||
|
},
|
||||||
|
|
||||||
|
async scheduleReconnect() {
|
||||||
|
const delay = Math.min(1000 * 2 ** this.reconnectAttempts, 30000)
|
||||||
|
this.reconnectAttempts += 1
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
this.connect()
|
||||||
|
}, delay)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import {defineStore} from "pinia";
|
||||||
|
|
||||||
|
export const useMessageStore = defineStore("message", {
|
||||||
|
state: () => ({}),
|
||||||
|
actions: {}
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import {defineStore} from "pinia";
|
||||||
|
|
||||||
|
export const useServerStore = defineStore("server", {
|
||||||
|
state: () => ({}),
|
||||||
|
actions: {}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user