diff --git a/frontend/src/composables/useApi.ts b/frontend/src/composables/useApi.ts
new file mode 100644
index 0000000..3645976
--- /dev/null
+++ b/frontend/src/composables/useApi.ts
@@ -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'}),
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/pages/join.vue b/frontend/src/pages/join.vue
index 4bd1242..7b3cea7 100644
--- a/frontend/src/pages/join.vue
+++ b/frontend/src/pages/join.vue
@@ -1,7 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/server/index.vue b/frontend/src/pages/server/index.vue
new file mode 100644
index 0000000..27324a8
--- /dev/null
+++ b/frontend/src/pages/server/index.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/plugins/api.ts b/frontend/src/plugins/api.ts
deleted file mode 100644
index b43e486..0000000
--- a/frontend/src/plugins/api.ts
+++ /dev/null
@@ -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
-}
\ No newline at end of file
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 870afa2..e5ece13 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -10,6 +10,7 @@ import Index from '@/pages/index.vue'
import {useAuthStore} from '@/stores/auth'
+
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
@@ -27,6 +28,21 @@ const router = createRouter({
path: '/',
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',
name: 'admin-dashboard',
diff --git a/frontend/src/stores/app.ts b/frontend/src/stores/app.ts
index 7429543..e1f11cb 100644
--- a/frontend/src/stores/app.ts
+++ b/frontend/src/stores/app.ts
@@ -1,8 +1,9 @@
// Utilities
-import { defineStore } from 'pinia'
+import {defineStore} from 'pinia'
export const useAppStore = defineStore('app', {
state: () => ({
- //
+ baseurl: 'http://localhost:8080',
}),
-})
+ actions: {}
+});
diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts
index b9b98b0..444c835 100644
--- a/frontend/src/stores/auth.ts
+++ b/frontend/src/stores/auth.ts
@@ -1,5 +1,5 @@
import {defineStore} from 'pinia'
-import {apiFetch} from "@/plugins/api.ts";
+import {useApi} from "@/composables/useApi";
export interface User {
id: string
@@ -30,11 +30,11 @@ export const useAuthStore = defineStore('auth', {
return
}
+ const api = useApi()
try {
- const response = await apiFetch('/auth/me', {method: 'GET'})
+ const response = await api.get('/auth/me')
if (response.ok) {
const data = await response.json()
- // On s'attend à ce que /auth/check renvoie l'objet user complet
this.user = data.user
} else {
this.logout()
diff --git a/frontend/src/stores/category.ts b/frontend/src/stores/category.ts
new file mode 100644
index 0000000..5a9c760
--- /dev/null
+++ b/frontend/src/stores/category.ts
@@ -0,0 +1,6 @@
+import {defineStore} from 'pinia'
+
+export const useCategoryStore = defineStore("category", {
+ state: () => ({}),
+ actions: {}
+});
\ No newline at end of file
diff --git a/frontend/src/stores/channel.ts b/frontend/src/stores/channel.ts
new file mode 100644
index 0000000..bfa569e
--- /dev/null
+++ b/frontend/src/stores/channel.ts
@@ -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: {}
+})
\ No newline at end of file
diff --git a/frontend/src/stores/gateway.ts b/frontend/src/stores/gateway.ts
new file mode 100644
index 0000000..7f210b6
--- /dev/null
+++ b/frontend/src/stores/gateway.ts
@@ -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)
+ },
+ }
+});
\ No newline at end of file
diff --git a/frontend/src/stores/message.ts b/frontend/src/stores/message.ts
new file mode 100644
index 0000000..eb043c4
--- /dev/null
+++ b/frontend/src/stores/message.ts
@@ -0,0 +1,6 @@
+import {defineStore} from "pinia";
+
+export const useMessageStore = defineStore("message", {
+ state: () => ({}),
+ actions: {}
+});
diff --git a/frontend/src/stores/server.ts b/frontend/src/stores/server.ts
new file mode 100644
index 0000000..b53262a
--- /dev/null
+++ b/frontend/src/stores/server.ts
@@ -0,0 +1,6 @@
+import {defineStore} from "pinia";
+
+export const useServerStore = defineStore("server", {
+ state: () => ({}),
+ actions: {}
+});
\ No newline at end of file