From 086e5ab0ea0678c117bc06c75fd69553a8ac8421 Mon Sep 17 00:00:00 2001 From: Nell Date: Thu, 30 Jul 2026 19:34:39 +0200 Subject: [PATCH] init --- frontend/src/pages/server/channel/index.vue | 4 ++- frontend/src/pages/server/index.vue | 6 +++- frontend/src/stores/user.ts | 31 +++++++++++++++++++++ frontend/src/types/user.ts | 8 ++++++ src/domain/dto/user.rs | 5 ++++ src/repositories/types.rs | 4 +++ src/repositories/user.rs | 17 +++++++++-- 7 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 frontend/src/stores/user.ts create mode 100644 frontend/src/types/user.ts diff --git a/frontend/src/pages/server/channel/index.vue b/frontend/src/pages/server/channel/index.vue index f249555..046f9f5 100644 --- a/frontend/src/pages/server/channel/index.vue +++ b/frontend/src/pages/server/channel/index.vue @@ -4,6 +4,7 @@ import {computed, nextTick, onMounted, ref, watch} from 'vue'; import {useRoute} from 'vue-router'; import {storeToRefs} from 'pinia'; import {useMessageStore} from '@/stores/message'; +import {useUserStore} from "@/stores/user.ts"; const props = defineProps<{ serverId: string @@ -14,6 +15,7 @@ const channelId = computed(() => props.channelId); const route = useRoute(); const messageStore = useMessageStore(); +const userStore = useUserStore(); // Référence vers l'élément scrollable const messageContainer = ref(null); @@ -94,7 +96,7 @@ watch(messages, () => { - {{ msg.user_id }} + {{ userStore.usersById[msg.user_id]?.username }} {{ msg.created_at }} diff --git a/frontend/src/pages/server/index.vue b/frontend/src/pages/server/index.vue index 5fbb4ab..3be9518 100644 --- a/frontend/src/pages/server/index.vue +++ b/frontend/src/pages/server/index.vue @@ -6,6 +6,7 @@ import {ref, watch} from 'vue' import {useRoute} from 'vue-router' import CreateChannelDialog from '@/components/channel/CreateChannelDialog.vue' import {type MenuItem, useContextMenu} from '@/composables/useContextMenu' +import {useUserStore} from "@/stores/user.ts"; const props = defineProps<{ serverId: string @@ -15,6 +16,7 @@ const props = defineProps<{ const route = useRoute() const channelStore = useChannelStore() const categoryStore = useCategoryStore() +const userStore = useUserStore() const {channels} = storeToRefs(channelStore) const {categories} = storeToRefs(categoryStore) const {openContextMenu} = useContextMenu() @@ -24,10 +26,12 @@ const loadServerData = async (targetServerId: string) => { if (!targetServerId) return channelStore.reset() categoryStore.reset() + userStore.reset() try { await Promise.all([ channelStore.fetchChannels(targetServerId), - categoryStore.fetchCategories(targetServerId) + categoryStore.fetchCategories(targetServerId), + userStore.fetchUsers(targetServerId) ]) } catch (error) { console.error('Failed to load server-scoped channels and categories:', error) diff --git a/frontend/src/stores/user.ts b/frontend/src/stores/user.ts new file mode 100644 index 0000000..74f811b --- /dev/null +++ b/frontend/src/stores/user.ts @@ -0,0 +1,31 @@ +import {defineStore} from 'pinia' +import {useApi} from "@/composables/useApi.ts"; +import type {User} from "@/types/user"; + +export const useUserStore = defineStore('user', { + state: () => ({ + users: [] as User[] + }), + getters: { + usersById: (state): Record => { + return state.users.reduce((acc, user) => { + acc[user.id] = user; + return acc; + }, {} as Record); + } + }, + actions: { + async fetchUsers(serverId?: string) { + let api = useApi(); + let url = "/users"; + if (serverId) { + url += `?server_id=${serverId}`; + } + const response = await api.get(url); + this.users = await response.json(); + }, + reset() { + this.users = []; + } + } +}); diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts new file mode 100644 index 0000000..a422c41 --- /dev/null +++ b/frontend/src/types/user.ts @@ -0,0 +1,8 @@ +export interface User { + id: string + username: string + pub_key: string | null + is_superuser: boolean + created_at: string + updated_at: string +} \ No newline at end of file diff --git a/src/domain/dto/user.rs b/src/domain/dto/user.rs index 44dd8f1..7cadc7c 100644 --- a/src/domain/dto/user.rs +++ b/src/domain/dto/user.rs @@ -3,6 +3,11 @@ use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use uuid::Uuid; +#[derive(Debug, Serialize, Deserialize, ToSchema, utoipa::IntoParams)] +pub struct UserQueryParams { + pub server_id: Option, +} + #[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct CreateUserRequest { pub username: String, diff --git a/src/repositories/types.rs b/src/repositories/types.rs index 99ddd56..a7019bd 100644 --- a/src/repositories/types.rs +++ b/src/repositories/types.rs @@ -28,3 +28,7 @@ pub struct MessageFilter { pub struct ChannelFilter { pub server_id: Option, } + +pub struct UserFilter { + pub server_id: Option, +} diff --git a/src/repositories/user.rs b/src/repositories/user.rs index a714f57..7fff758 100644 --- a/src/repositories/user.rs +++ b/src/repositories/user.rs @@ -1,8 +1,10 @@ use crate::auth::password; -use crate::models::user; +use crate::models::{server_user, user}; +use crate::repositories::types::UserFilter; use crate::repositories::{AnyResult, RepositoryContext}; use sea_orm::{ - ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, PaginatorTrait, QueryFilter, Set, + ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, JoinType, PaginatorTrait, + QueryFilter, QuerySelect, RelationTrait, Set, }; use std::sync::Arc; @@ -16,6 +18,17 @@ impl UserRepository { Ok(user::Entity::find().all(&self.context.db).await?) } + pub async fn filter(&self, filter: UserFilter) -> AnyResult> { + let mut query = user::Entity::find(); + if let Some(s_id) = filter.server_id { + query = query + .join(JoinType::InnerJoin, user::Relation::ServerUser.def()) + .filter(server_user::Column::ServerId.eq(s_id)) + .distinct(); + } + Ok(query.all(&self.context.db).await?) + } + pub async fn count(&self) -> AnyResult { Ok(user::Entity::find().count(&self.context.db).await?) }