init
This commit is contained in:
@@ -65,7 +65,7 @@ async function load() {
|
|||||||
if (!props.channel) return
|
if (!props.channel) return
|
||||||
loading.value = true; error.value = null
|
loading.value = true; error.value = null
|
||||||
try {
|
try {
|
||||||
const [roleResponse] = await Promise.all([api.get('/groups'), users.fetchUsers(props.serverId)])
|
const [roleResponse] = await Promise.all([api.get(`/roles?server_id=${props.serverId}`), users.fetchUsers(props.serverId)])
|
||||||
if (!roleResponse.ok) throw new Error('Impossible de charger les rôles')
|
if (!roleResponse.ok) throw new Error('Impossible de charger les rôles')
|
||||||
roles.value = (await roleResponse.json() as Role[]).filter(role => role.server_id === props.serverId)
|
roles.value = (await roleResponse.json() as Role[]).filter(role => role.server_id === props.serverId)
|
||||||
configured.value = await permissionsApi.getChannelPermissions(props.channel.id)
|
configured.value = await permissionsApi.getChannelPermissions(props.channel.id)
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {computed, ref, watch} from 'vue'
|
||||||
|
import {storeToRefs} from 'pinia'
|
||||||
|
import {useServerStore} from '@/stores/server'
|
||||||
|
import {useRoleStore} from '@/stores/role'
|
||||||
|
import {useUserStore} from '@/stores/user'
|
||||||
|
import {usePermissions} from '@/composables/usePermissions'
|
||||||
|
import {toServerPermissionMask, type ServerPermissionMask} from '@/types/permissions'
|
||||||
|
import ServerPermissionEditor from '@/components/permissions/ServerPermissionEditor.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{modelValue: boolean; serverId: string; serverName: string}>()
|
||||||
|
const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>()
|
||||||
|
const serverStore = useServerStore()
|
||||||
|
const roleStore = useRoleStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const permissionsApi = usePermissions()
|
||||||
|
const {roles} = storeToRefs(roleStore)
|
||||||
|
const activeTab = ref('general')
|
||||||
|
const selectedRoleId = ref<string | null>(null)
|
||||||
|
const name = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const saving = ref(false)
|
||||||
|
const error = ref<string | null>(null)
|
||||||
|
const roleForm = ref('')
|
||||||
|
const rolePermissions = ref<ServerPermissionMask>(toServerPermissionMask(0))
|
||||||
|
|
||||||
|
const selectedRole = computed(() => roles.value.find(role => role.id === selectedRoleId.value) || null)
|
||||||
|
const selectedMembers = computed(() => selectedRoleId.value ? roleStore.members[selectedRoleId.value] || [] : [])
|
||||||
|
const availableUsers = computed(() => userStore.users.filter(user => !selectedMembers.value.some(member => member.id === user.id)))
|
||||||
|
const memberToAdd = ref<string | null>(null)
|
||||||
|
|
||||||
|
watch(() => props.modelValue, async open => {
|
||||||
|
if (open) await load()
|
||||||
|
})
|
||||||
|
watch(selectedRoleId, async roleId => {
|
||||||
|
if (!roleId) return
|
||||||
|
roleForm.value = selectedRole.value?.name || ''
|
||||||
|
rolePermissions.value = toServerPermissionMask(0)
|
||||||
|
try {
|
||||||
|
await roleStore.fetchMembers(roleId)
|
||||||
|
const permission = await permissionsApi.getServerRolePermission(props.serverId, roleId)
|
||||||
|
rolePermissions.value = permission.permissions
|
||||||
|
} catch (e) {
|
||||||
|
if ((e as Error & {status?: number}).status !== 404) error.value = e instanceof Error ? e.message : 'Erreur de chargement'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
name.value = props.serverName
|
||||||
|
try {
|
||||||
|
await Promise.all([roleStore.fetchRoles(props.serverId), userStore.fetchUsers(props.serverId)])
|
||||||
|
} catch (e) { error.value = e instanceof Error ? e.message : 'Erreur de chargement' }
|
||||||
|
finally { loading.value = false }
|
||||||
|
}
|
||||||
|
async function saveServer() {
|
||||||
|
if (!name.value.trim()) return
|
||||||
|
saving.value = true
|
||||||
|
try { await serverStore.updateServer(props.serverId, {name: name.value.trim()}) }
|
||||||
|
catch (e) { error.value = e instanceof Error ? e.message : 'Erreur de sauvegarde' }
|
||||||
|
finally { saving.value = false }
|
||||||
|
}
|
||||||
|
async function createRole() {
|
||||||
|
if (!roleForm.value.trim()) return
|
||||||
|
try { const role = await roleStore.createRole({server_id: props.serverId, name: roleForm.value.trim()}); selectedRoleId.value = role.id; roleForm.value = '' }
|
||||||
|
catch (e) { error.value = e instanceof Error ? e.message : 'Erreur de création' }
|
||||||
|
}
|
||||||
|
async function saveRole() {
|
||||||
|
if (!selectedRoleId.value || !roleForm.value.trim()) return
|
||||||
|
try {
|
||||||
|
await roleStore.updateRole(selectedRoleId.value, {name: roleForm.value.trim()})
|
||||||
|
await permissionsApi.setServerRolePermission(props.serverId, selectedRoleId.value, rolePermissions.value)
|
||||||
|
} catch (e) { error.value = e instanceof Error ? e.message : 'Erreur de sauvegarde' }
|
||||||
|
}
|
||||||
|
async function deleteRole() {
|
||||||
|
if (!selectedRoleId.value || selectedRole.value?.is_default) return
|
||||||
|
try { await roleStore.deleteRole(selectedRoleId.value); selectedRoleId.value = null }
|
||||||
|
catch (e) { error.value = e instanceof Error ? e.message : 'Erreur de suppression' }
|
||||||
|
}
|
||||||
|
async function addMember() {
|
||||||
|
if (!selectedRoleId.value || !memberToAdd.value) return
|
||||||
|
try { await roleStore.addMember(selectedRoleId.value, memberToAdd.value); memberToAdd.value = null }
|
||||||
|
catch (e) { error.value = e instanceof Error ? e.message : 'Erreur d’ajout' }
|
||||||
|
}
|
||||||
|
async function savePermissions(value: ServerPermissionMask) {
|
||||||
|
if (!selectedRoleId.value) return
|
||||||
|
try { await permissionsApi.setServerRolePermission(props.serverId, selectedRoleId.value, value); rolePermissions.value = value }
|
||||||
|
catch (e) { error.value = e instanceof Error ? e.message : 'Erreur de permissions' }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-dialog :model-value="modelValue" max-width="980" @update:model-value="emit('update:modelValue', $event)">
|
||||||
|
<v-card min-height="620">
|
||||||
|
<v-card-title>Paramètres du serveur</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-alert v-if="error" type="error" density="compact" class="mb-4">{{ error }}</v-alert>
|
||||||
|
<v-row class="settings-layout" no-gutters>
|
||||||
|
<v-col cols="12" md="3" class="settings-sidebar">
|
||||||
|
<v-list density="compact" nav>
|
||||||
|
<v-list-item title="Général" prepend-icon="mdi-cog" :active="activeTab === 'general'" @click="activeTab = 'general'" />
|
||||||
|
<v-list-item title="Rôles" prepend-icon="mdi-shield-account" :active="activeTab === 'roles'" @click="activeTab = 'roles'" />
|
||||||
|
</v-list>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="9" class="pa-5">
|
||||||
|
<v-progress-linear v-if="loading" indeterminate class="mb-4" />
|
||||||
|
<template v-if="activeTab === 'general'">
|
||||||
|
<div class="text-h6 mb-4">Général</div>
|
||||||
|
<v-text-field v-model="name" label="Nom du serveur" :disabled="loading" />
|
||||||
|
<v-btn color="primary" :loading="saving" :disabled="!name.trim()" @click="saveServer">Enregistrer</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="text-h6 mb-4">Rôles</div>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<v-list border density="compact" class="role-list">
|
||||||
|
<v-list-item v-for="role in roles" :key="role.id" :title="role.name" :active="role.id === selectedRoleId" @click="selectedRoleId = role.id">
|
||||||
|
<template #append><v-icon v-if="role.is_default" icon="mdi-star" size="small" /></template>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
<v-text-field v-model="roleForm" class="mt-3" label="Nouveau rôle" hide-details @keyup.enter="createRole" />
|
||||||
|
<v-btn class="mt-2" block color="primary" variant="tonal" @click="createRole">Ajouter un rôle</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="8">
|
||||||
|
<template v-if="selectedRole">
|
||||||
|
<v-text-field v-model="roleForm" label="Nom du rôle" />
|
||||||
|
<div class="d-flex ga-2 mb-4"><v-btn color="primary" @click="saveRole">Enregistrer</v-btn><v-btn v-if="!selectedRole.is_default" color="error" variant="text" @click="deleteRole">Supprimer</v-btn></div>
|
||||||
|
<v-select v-model="memberToAdd" :items="availableUsers" item-title="username" item-value="id" label="Ajouter un membre" clearable @update:model-value="addMember" />
|
||||||
|
<v-list density="compact" border class="mb-4">
|
||||||
|
<v-list-item v-for="member in selectedMembers" :key="member.id" :title="member.username">
|
||||||
|
<template #append><v-btn icon="mdi-close" size="small" variant="text" @click="roleStore.removeMember(selectedRoleId!, member.id)" /></template>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
<ServerPermissionEditor v-model="rolePermissions" title="Permissions du rôle" @save="savePermissions" />
|
||||||
|
</template>
|
||||||
|
<v-sheet v-else class="empty-selection d-flex align-center justify-center text-medium-emphasis" border rounded>Sélectionnez un rôle.</v-sheet>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions><v-spacer /><v-btn variant="text" @click="emit('update:modelValue', false)">Fermer</v-btn></v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.settings-layout { min-height: 500px; }
|
||||||
|
.settings-sidebar { border-right: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); }
|
||||||
|
.role-list { max-height: 250px; overflow-y: auto; }
|
||||||
|
.empty-selection { min-height: 400px; }
|
||||||
|
@media (max-width: 959px) { .settings-sidebar { border-right: 0; border-bottom: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); } }
|
||||||
|
</style>
|
||||||
@@ -1,18 +1,24 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {storeToRefs} from 'pinia'
|
import {storeToRefs} from 'pinia'
|
||||||
import {useServerStore} from '@/stores/server'
|
import {useServerStore, type Server} from '@/stores/server'
|
||||||
import {computed, ref, watch} from 'vue'
|
import {computed, ref, watch} from 'vue'
|
||||||
import {useRoute, useRouter} from 'vue-router'
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
import ContextMenu from "@/components/ContextMenu.vue";
|
import ContextMenu from "@/components/ContextMenu.vue";
|
||||||
import UserListDrawer from '@/components/UserListDrawer.vue'
|
import UserListDrawer from '@/components/UserListDrawer.vue'
|
||||||
|
import ServerSettingsDialog from '@/components/server/ServerSettingsDialog.vue'
|
||||||
|
import {useContextMenu} from '@/composables/useContextMenu'
|
||||||
|
|
||||||
const serverStore = useServerStore()
|
const serverStore = useServerStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const {openContextMenu} = useContextMenu()
|
||||||
|
|
||||||
const {servers} = storeToRefs(serverStore)
|
const {servers} = storeToRefs(serverStore)
|
||||||
|
|
||||||
const showUsersDrawer = ref(false)
|
const showUsersDrawer = ref(false)
|
||||||
|
const showServerSettings = ref(false)
|
||||||
|
const selectedServerId = ref<string | null>(null)
|
||||||
|
const selectedServerName = ref('')
|
||||||
const isServerContext = computed(() => Boolean(route.params.serverId))
|
const isServerContext = computed(() => Boolean(route.params.serverId))
|
||||||
|
|
||||||
watch(isServerContext, (isActive) => {
|
watch(isServerContext, (isActive) => {
|
||||||
@@ -82,6 +88,18 @@ const getServerColor = (str: string): string => {
|
|||||||
return `hsl(${hue}, 60%, 45%)`
|
return `hsl(${hue}, 60%, 45%)`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onServerContextMenu(event: MouseEvent, server: Server) {
|
||||||
|
openContextMenu(event, [{
|
||||||
|
label: 'Gérer le serveur',
|
||||||
|
icon: 'mdi-cog',
|
||||||
|
action: () => {
|
||||||
|
selectedServerId.value = server.id
|
||||||
|
selectedServerName.value = server.name
|
||||||
|
showServerSettings.value = true
|
||||||
|
},
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -125,6 +143,7 @@ const getServerColor = (str: string): string => {
|
|||||||
v-for="server in servers"
|
v-for="server in servers"
|
||||||
:key="server.id"
|
:key="server.id"
|
||||||
:to="`/server/${server.id}`"
|
:to="`/server/${server.id}`"
|
||||||
|
@contextmenu="onServerContextMenu($event, server)"
|
||||||
>
|
>
|
||||||
<v-avatar
|
<v-avatar
|
||||||
:style="{ backgroundColor: getServerColor(server.name) }"
|
:style="{ backgroundColor: getServerColor(server.name) }"
|
||||||
@@ -150,6 +169,13 @@ const getServerColor = (str: string): string => {
|
|||||||
v-model="showUsersDrawer"
|
v-model="showUsersDrawer"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ServerSettingsDialog
|
||||||
|
v-if="selectedServerId"
|
||||||
|
v-model="showServerSettings"
|
||||||
|
:server-id="selectedServerId"
|
||||||
|
:server-name="selectedServerName"
|
||||||
|
/>
|
||||||
|
|
||||||
<router-view/>
|
<router-view/>
|
||||||
<!-- Menu contextuel global -->
|
<!-- Menu contextuel global -->
|
||||||
<ContextMenu/>
|
<ContextMenu/>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import {storeToRefs} from 'pinia'
|
import {storeToRefs} from 'pinia'
|
||||||
import {useChannelStore} from '@/stores/channel'
|
import {useChannelStore} from '@/stores/channel'
|
||||||
import {useCategoryStore} from '@/stores/category'
|
import {useCategoryStore} from '@/stores/category'
|
||||||
import {ref, watch} from 'vue'
|
import {computed, ref, watch} from 'vue'
|
||||||
import {useRoute} from 'vue-router'
|
import {useRoute} from 'vue-router'
|
||||||
import CreateChannelDialog from '@/components/channel/CreateChannelDialog.vue'
|
import CreateChannelDialog from '@/components/channel/CreateChannelDialog.vue'
|
||||||
import CreateCategoryDialog from '@/components/category/CreateCategoryDialog.vue'
|
import CreateCategoryDialog from '@/components/category/CreateCategoryDialog.vue'
|
||||||
@@ -11,6 +11,7 @@ import {useUserStore} from "@/stores/user.ts";
|
|||||||
import {useServerStore} from "@/stores/server.ts";
|
import {useServerStore} from "@/stores/server.ts";
|
||||||
import ChannelPermissionsDialog from '@/components/permissions/ChannelPermissionsDialog.vue'
|
import ChannelPermissionsDialog from '@/components/permissions/ChannelPermissionsDialog.vue'
|
||||||
import {useAuthStore} from '@/stores/auth'
|
import {useAuthStore} from '@/stores/auth'
|
||||||
|
import ServerSettingsDialog from '@/components/server/ServerSettingsDialog.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
serverId: string
|
serverId: string
|
||||||
@@ -27,6 +28,8 @@ const {openContextMenu} = useContextMenu()
|
|||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const showPermissionsDialog = ref(false)
|
const showPermissionsDialog = ref(false)
|
||||||
const selectedChannel = ref<any | null>(null)
|
const selectedChannel = ref<any | null>(null)
|
||||||
|
const showServerSettings = ref(false)
|
||||||
|
const serverName = computed(() => serverStore.servers.find(server => server.id === props.serverId)?.name || 'Serveur')
|
||||||
|
|
||||||
|
|
||||||
const loadServerData = async (targetServerId: string) => {
|
const loadServerData = async (targetServerId: string) => {
|
||||||
@@ -149,11 +152,9 @@ function onChannelContextMenu(event: MouseEvent, channel: any) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-navigation-drawer width="244" @contextmenu="onSidebarContextMenu">
|
<v-navigation-drawer width="244" @contextmenu="onSidebarContextMenu">
|
||||||
<v-sheet
|
<v-sheet color="grey-lighten-5" height="128" width="100%" class="pa-3">
|
||||||
color="grey-lighten-5"
|
<v-btn block variant="text" prepend-icon="mdi-cog" @click="showServerSettings = true">Gérer le serveur</v-btn>
|
||||||
height="128"
|
</v-sheet>
|
||||||
width="100%"
|
|
||||||
></v-sheet>
|
|
||||||
|
|
||||||
<v-list
|
<v-list
|
||||||
v-model:opened="openedCategories"
|
v-model:opened="openedCategories"
|
||||||
@@ -213,6 +214,12 @@ function onChannelContextMenu(event: MouseEvent, channel: any) {
|
|||||||
:server-id="serverId"
|
:server-id="serverId"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ServerSettingsDialog
|
||||||
|
v-model="showServerSettings"
|
||||||
|
:server-id="serverId"
|
||||||
|
:server-name="serverName"
|
||||||
|
/>
|
||||||
|
|
||||||
<v-main>
|
<v-main>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</v-main>
|
</v-main>
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import {defineStore} from 'pinia'
|
||||||
|
import {useApi} from '@/composables/useApi'
|
||||||
|
import type {Role} from '@/types/role'
|
||||||
|
import type {User} from '@/types/user'
|
||||||
|
|
||||||
|
export const useRoleStore = defineStore('role', {
|
||||||
|
state: () => ({
|
||||||
|
roles: [] as Role[],
|
||||||
|
members: {} as Record<string, User[]>,
|
||||||
|
loading: false,
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async fetchRoles(serverId: string) {
|
||||||
|
const response = await useApi().get(`/roles?server_id=${serverId}`)
|
||||||
|
if (!response.ok) throw new Error('Impossible de charger les rôles')
|
||||||
|
this.roles = await response.json()
|
||||||
|
return this.roles
|
||||||
|
},
|
||||||
|
async createRole(payload: {server_id: string; name: string; is_default?: boolean}) {
|
||||||
|
const response = await useApi().post('/roles', payload)
|
||||||
|
if (!response.ok) throw new Error('Impossible de créer le rôle')
|
||||||
|
const role = await response.json()
|
||||||
|
this.roles.push(role)
|
||||||
|
return role
|
||||||
|
},
|
||||||
|
async updateRole(id: string, payload: {name: string; is_default?: boolean}) {
|
||||||
|
const response = await useApi().put(`/roles/${id}`, payload)
|
||||||
|
if (!response.ok) throw new Error('Impossible de modifier le rôle')
|
||||||
|
const role = await response.json()
|
||||||
|
const index = this.roles.findIndex(item => item.id === id)
|
||||||
|
if (index >= 0) this.roles[index] = role
|
||||||
|
return role
|
||||||
|
},
|
||||||
|
async deleteRole(id: string) {
|
||||||
|
const response = await useApi().delete(`/roles/${id}`)
|
||||||
|
if (!response.ok) throw new Error('Impossible de supprimer le rôle')
|
||||||
|
this.roles = this.roles.filter(role => role.id !== id)
|
||||||
|
delete this.members[id]
|
||||||
|
},
|
||||||
|
async fetchMembers(roleId: string) {
|
||||||
|
const response = await useApi().get(`/roles/${roleId}/members`)
|
||||||
|
if (!response.ok) throw new Error('Impossible de charger les membres')
|
||||||
|
this.members[roleId] = await response.json()
|
||||||
|
return this.members[roleId]
|
||||||
|
},
|
||||||
|
async addMember(roleId: string, userId: string) {
|
||||||
|
const response = await useApi().put(`/roles/${roleId}/members/${userId}`)
|
||||||
|
if (!response.ok) throw new Error('Impossible d’ajouter le membre')
|
||||||
|
await this.fetchMembers(roleId)
|
||||||
|
},
|
||||||
|
async removeMember(roleId: string, userId: string) {
|
||||||
|
const response = await useApi().delete(`/roles/${roleId}/members/${userId}`)
|
||||||
|
if (!response.ok) throw new Error('Impossible de retirer le membre')
|
||||||
|
await this.fetchMembers(roleId)
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.roles = []
|
||||||
|
this.members = {}
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -3,7 +3,7 @@ import {useApi} from "@/composables/useApi.ts";
|
|||||||
import {useChannelStore} from "@/stores/channel.ts";
|
import {useChannelStore} from "@/stores/channel.ts";
|
||||||
import {useCategoryStore} from "@/stores/category.ts";
|
import {useCategoryStore} from "@/stores/category.ts";
|
||||||
|
|
||||||
interface Server {
|
export interface Server {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
is_default: boolean
|
is_default: boolean
|
||||||
@@ -44,6 +44,22 @@ export const useServerStore = defineStore("server", {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async updateServer(serverId: string, payload: { name: string; is_default?: boolean }) {
|
||||||
|
const api = useApi();
|
||||||
|
const response = await api.put(`/servers/${serverId}`, {
|
||||||
|
name: payload.name,
|
||||||
|
password: null,
|
||||||
|
is_default: payload.is_default ?? false,
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json().catch(() => null);
|
||||||
|
throw new Error(error?.message || 'Failed to update server');
|
||||||
|
}
|
||||||
|
const updated = await response.json();
|
||||||
|
const index = this.servers.findIndex(server => server.id === serverId);
|
||||||
|
if (index >= 0) this.servers[index] = updated;
|
||||||
|
return updated;
|
||||||
|
},
|
||||||
async fetchServerTree(serverId: string) {
|
async fetchServerTree(serverId: string) {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const channelStore = useChannelStore();
|
const channelStore = useChannelStore();
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export interface Role {
|
||||||
|
id: string
|
||||||
|
server_id: string
|
||||||
|
name: string
|
||||||
|
is_default: boolean
|
||||||
|
created_at: string
|
||||||
|
}
|
||||||
@@ -3,6 +3,11 @@ use serde::{Deserialize, Serialize};
|
|||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
|
||||||
|
pub struct RoleQueryParams {
|
||||||
|
pub server_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||||
pub struct CreateRoleRequest {
|
pub struct CreateRoleRequest {
|
||||||
pub server_id: Uuid,
|
pub server_id: Uuid,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::models::{role, role_user};
|
use crate::models::{role, role_user, user};
|
||||||
use crate::repositories::{AnyResult, RepositoryContext};
|
use crate::repositories::{AnyResult, RepositoryContext};
|
||||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -63,4 +63,49 @@ impl RoleRepository {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(res.rows_affected > 0)
|
Ok(res.rows_affected > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_members(&self, role_id: Uuid) -> AnyResult<Vec<user::Model>> {
|
||||||
|
let memberships = role_user::Entity::find()
|
||||||
|
.filter(role_user::Column::RoleId.eq(role_id))
|
||||||
|
.all(&self.context.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut members = Vec::with_capacity(memberships.len());
|
||||||
|
for membership in memberships {
|
||||||
|
if let Some(user) = user::Entity::find_by_id(membership.user_id)
|
||||||
|
.one(&self.context.db)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
members.push(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(members)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_member(&self, role_id: Uuid, user_id: Uuid) -> AnyResult<bool> {
|
||||||
|
if role_user::Entity::find_by_id((role_id, user_id))
|
||||||
|
.one(&self.context.db)
|
||||||
|
.await?
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
role_user::ActiveModel {
|
||||||
|
role_id: Set(role_id),
|
||||||
|
user_id: Set(user_id),
|
||||||
|
}
|
||||||
|
.insert(&self.context.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_member(&self, role_id: Uuid, user_id: Uuid) -> AnyResult<bool> {
|
||||||
|
let result = role_user::Entity::delete_by_id((role_id, user_id))
|
||||||
|
.exec(&self.context.db)
|
||||||
|
.await?;
|
||||||
|
Ok(result.rows_affected > 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ impl ServerRepository {
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_user(&self, server_id: Uuid, user_id: Uuid) -> AnyResult<Option<server_user::Model>> {
|
||||||
|
Ok(server_user::Entity::find()
|
||||||
|
.filter(server_user::Column::ServerId.eq(server_id))
|
||||||
|
.filter(server_user::Column::UserId.eq(user_id))
|
||||||
|
.one(&self.context.db)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn delete(&self, id: Uuid) -> AnyResult<bool> {
|
pub async fn delete(&self, id: Uuid) -> AnyResult<bool> {
|
||||||
let res = server::Entity::delete_by_id(id)
|
let res = server::Entity::delete_by_id(id)
|
||||||
.exec(&self.context.db)
|
.exec(&self.context.db)
|
||||||
|
|||||||
+96
-127
@@ -1,162 +1,131 @@
|
|||||||
use crate::core::state::AppState;
|
use crate::core::state::AppState;
|
||||||
use crate::http::context::Superuser;
|
use crate::domain::dto::role::{CreateRoleRequest, RoleQueryParams, RoleResponse, UpdateRoleRequest};
|
||||||
|
use crate::domain::dto::user::UserResponse;
|
||||||
|
use crate::http::context::CurrentUser;
|
||||||
use crate::http::error::HTTPError;
|
use crate::http::error::HTTPError;
|
||||||
use crate::domain::dto::role::{CreateRoleRequest, RoleResponse, UpdateRoleRequest};
|
use crate::permissions::ServerPermission;
|
||||||
use crate::routes::role::mapper;
|
use crate::routes::role::mapper;
|
||||||
use axum::{
|
use crate::routes::user::mapper as user_mapper;
|
||||||
Json,
|
use axum::{Json, extract::{Path, Query, State}, http::StatusCode};
|
||||||
extract::{Path, State},
|
|
||||||
http::StatusCode,
|
|
||||||
};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// Liste tous les groupes
|
async fn require_permission(
|
||||||
#[utoipa::path(
|
state: &AppState,
|
||||||
get,
|
user: &CurrentUser,
|
||||||
path = "/groups",
|
server_id: Uuid,
|
||||||
responses(
|
permission: ServerPermission,
|
||||||
(status = 200, description = "Liste des groupes récupérée avec succès", body = [RoleResponse]),
|
) -> Result<(), HTTPError> {
|
||||||
(status = 500, description = "Erreur interne du serveur")
|
if user.is_superuser {
|
||||||
),
|
return Ok(());
|
||||||
tag = "Roles"
|
}
|
||||||
)]
|
|
||||||
pub async fn get_all(State(state): State<AppState>) -> Result<Json<Vec<RoleResponse>>, HTTPError> {
|
let granted = state
|
||||||
let groups = state.repositories.role.get_all().await?;
|
.repositories
|
||||||
Ok(Json(
|
.server
|
||||||
groups
|
.get_user_permission(server_id, user.id)
|
||||||
.into_iter()
|
.await?
|
||||||
.map(mapper::group_model_to_group_response)
|
.map(|value| ServerPermission::from_bits_truncate(value.permissions as u64))
|
||||||
.collect(),
|
.unwrap_or_default();
|
||||||
))
|
|
||||||
|
if granted.contains(permission) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(HTTPError::Forbidden)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Récupère un groupe par son ID
|
#[utoipa::path(get, path = "/roles", params(RoleQueryParams), responses((status = 200, body = [RoleResponse])), tag = "Roles")]
|
||||||
#[utoipa::path(
|
pub async fn get_all(
|
||||||
get,
|
State(state): State<AppState>,
|
||||||
path = "/groups/{id}",
|
Query(filters): Query<RoleQueryParams>,
|
||||||
responses(
|
) -> Result<Json<Vec<RoleResponse>>, HTTPError> {
|
||||||
(status = 200, description = "Rolee trouvé", body = RoleResponse),
|
let roles = match filters.server_id {
|
||||||
(status = 404, description = "Rolee non trouvé"),
|
Some(server_id) => state.repositories.role.get_all_by_server(server_id).await?,
|
||||||
(status = 500, description = "Erreur interne du serveur")
|
None => state.repositories.role.get_all().await?,
|
||||||
),
|
};
|
||||||
params(
|
|
||||||
("id" = Uuid, Path, description = "ID du groupe")
|
Ok(Json(roles.into_iter().map(mapper::role_model_to_role_response).collect()))
|
||||||
),
|
}
|
||||||
tag = "Roles"
|
|
||||||
)]
|
#[utoipa::path(get, path = "/roles/{id}", params(("id" = Uuid, Path)), responses((status = 200, body = RoleResponse), (status = 404)), tag = "Roles")]
|
||||||
pub async fn get_by_id(
|
pub async fn get_by_id(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
) -> Result<Json<RoleResponse>, HTTPError> {
|
) -> Result<Json<RoleResponse>, HTTPError> {
|
||||||
let group = state
|
let role = state.repositories.role.get_by_id(id).await?.ok_or(HTTPError::NotFound)?;
|
||||||
.repositories
|
Ok(Json(mapper::role_model_to_role_response(role)))
|
||||||
.role
|
|
||||||
.get_by_id(id)
|
|
||||||
.await?
|
|
||||||
.ok_or(HTTPError::NotFound)?;
|
|
||||||
|
|
||||||
Ok(Json(mapper::group_model_to_group_response(group)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crée un nouveau groupe
|
#[utoipa::path(post, path = "/roles", request_body = CreateRoleRequest, responses((status = 201, body = RoleResponse)), tag = "Roles", security(("bearerAuth" = [])))]
|
||||||
#[utoipa::path(
|
|
||||||
post,
|
|
||||||
path = "/groups",
|
|
||||||
request_body = CreateRoleRequest,
|
|
||||||
responses(
|
|
||||||
(status = 201, description = "Role créé avec succès", body = RoleResponse),
|
|
||||||
(status = 404, description = "Serveur non trouvé"),
|
|
||||||
(status = 500, description = "Erreur interne du serveur")
|
|
||||||
),
|
|
||||||
tag = "Roles",
|
|
||||||
security(
|
|
||||||
("bearerAuth" = [])
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
_admin: Superuser,
|
user: CurrentUser,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(payload): Json<CreateRoleRequest>,
|
Json(payload): Json<CreateRoleRequest>,
|
||||||
) -> Result<(StatusCode, Json<RoleResponse>), HTTPError> {
|
) -> Result<(StatusCode, Json<RoleResponse>), HTTPError> {
|
||||||
// Vérifier que le serveur existe
|
state.repositories.server.get_by_id(payload.server_id).await?
|
||||||
state
|
|
||||||
.repositories
|
|
||||||
.server
|
|
||||||
.get_by_id(payload.server_id)
|
|
||||||
.await?
|
|
||||||
.ok_or(HTTPError::BadRequest("Server not found".to_string()))?;
|
.ok_or(HTTPError::BadRequest("Server not found".to_string()))?;
|
||||||
|
require_permission(&state, &user, payload.server_id, ServerPermission::MANAGE_ROLES).await?;
|
||||||
|
|
||||||
let active_model = mapper::create_request_to_am(payload);
|
let role = state.services.role.create_role(mapper::create_request_to_am(payload)).await?;
|
||||||
let group = state.services.role.create_role(active_model).await?;
|
Ok((StatusCode::CREATED, Json(mapper::role_model_to_role_response(role))))
|
||||||
Ok((
|
|
||||||
StatusCode::CREATED,
|
|
||||||
Json(mapper::group_model_to_group_response(group)),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Met à jour un groupe existant
|
#[utoipa::path(put, path = "/roles/{id}", request_body = UpdateRoleRequest, responses((status = 200, body = RoleResponse), (status = 404)), tag = "Roles", security(("bearerAuth" = [])))]
|
||||||
#[utoipa::path(
|
|
||||||
put,
|
|
||||||
path = "/groups/{id}",
|
|
||||||
request_body = UpdateRoleRequest,
|
|
||||||
responses(
|
|
||||||
(status = 200, description = "Role mis à jour avec succès", body = RoleResponse),
|
|
||||||
(status = 404, description = "Role non trouvé"),
|
|
||||||
(status = 500, description = "Erreur interne du serveur")
|
|
||||||
),
|
|
||||||
params(
|
|
||||||
("id" = Uuid, Path, description = "ID du groupe")
|
|
||||||
),
|
|
||||||
tag = "Roles",
|
|
||||||
security(
|
|
||||||
("bearerAuth" = [])
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
pub async fn update(
|
pub async fn update(
|
||||||
_admin: Superuser,
|
user: CurrentUser,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
Json(payload): Json<UpdateRoleRequest>,
|
Json(payload): Json<UpdateRoleRequest>,
|
||||||
) -> Result<Json<RoleResponse>, HTTPError> {
|
) -> Result<Json<RoleResponse>, HTTPError> {
|
||||||
// Vérifier l'existence
|
let role = state.repositories.role.get_by_id(id).await?.ok_or(HTTPError::NotFound)?;
|
||||||
let group = state
|
require_permission(&state, &user, role.server_id, ServerPermission::MANAGE_ROLES).await?;
|
||||||
.repositories
|
|
||||||
.role
|
|
||||||
.get_by_id(id)
|
|
||||||
.await?
|
|
||||||
.ok_or(HTTPError::NotFound)?;
|
|
||||||
|
|
||||||
let active_model = mapper::update_request_to_am(group.id, group.server_id, payload);
|
let role = state.services.role.update_role(mapper::update_request_to_am(role.id, role.server_id, payload)).await?;
|
||||||
let group = state.services.role.update_role(active_model).await?;
|
Ok(Json(mapper::role_model_to_role_response(role)))
|
||||||
|
|
||||||
Ok(Json(mapper::group_model_to_group_response(group)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Supprime un groupe
|
#[utoipa::path(delete, path = "/roles/{id}", responses((status = 204), (status = 404)), tag = "Roles", security(("bearerAuth" = [])))]
|
||||||
#[utoipa::path(
|
|
||||||
delete,
|
|
||||||
path = "/groups/{id}",
|
|
||||||
responses(
|
|
||||||
(status = 204, description = "Role supprimé avec succès"),
|
|
||||||
(status = 404, description = "Role non trouvé"),
|
|
||||||
(status = 500, description = "Erreur interne du serveur")
|
|
||||||
),
|
|
||||||
params(
|
|
||||||
("id" = Uuid, Path, description = "ID du groupe")
|
|
||||||
),
|
|
||||||
tag = "Roles",
|
|
||||||
security(
|
|
||||||
("bearerAuth" = [])
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
pub async fn delete(
|
pub async fn delete(
|
||||||
_admin: Superuser,
|
user: CurrentUser,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
) -> Result<StatusCode, HTTPError> {
|
) -> Result<StatusCode, HTTPError> {
|
||||||
if state.services.role.delete_role(id).await? {
|
let role = state.repositories.role.get_by_id(id).await?.ok_or(HTTPError::NotFound)?;
|
||||||
|
require_permission(&state, &user, role.server_id, ServerPermission::MANAGE_ROLES).await?;
|
||||||
|
|
||||||
|
if state.services.role.delete_role(id).await? { Ok(StatusCode::NO_CONTENT) } else { Err(HTTPError::NotFound) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_members(
|
||||||
|
user: CurrentUser,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<Vec<UserResponse>>, HTTPError> {
|
||||||
|
let role = state.repositories.role.get_by_id(id).await?.ok_or(HTTPError::NotFound)?;
|
||||||
|
require_permission(&state, &user, role.server_id, ServerPermission::MANAGE_MEMBERS).await?;
|
||||||
|
let members = state.repositories.role.get_members(id).await?;
|
||||||
|
Ok(Json(members.into_iter().map(user_mapper::user_model_to_user_response).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_member(
|
||||||
|
user: CurrentUser,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((id, user_id)): Path<(Uuid, Uuid)>,
|
||||||
|
) -> Result<StatusCode, HTTPError> {
|
||||||
|
let role = state.repositories.role.get_by_id(id).await?.ok_or(HTTPError::NotFound)?;
|
||||||
|
require_permission(&state, &user, role.server_id, ServerPermission::MANAGE_MEMBERS).await?;
|
||||||
|
state.repositories.server.get_user(role.server_id, user_id).await?
|
||||||
|
.ok_or(HTTPError::BadRequest("User is not a member of this server".to_string()))?;
|
||||||
|
state.services.role.add_member(id, user_id, role.server_id).await?;
|
||||||
Ok(StatusCode::NO_CONTENT)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
} else {
|
}
|
||||||
Err(HTTPError::NotFound)
|
|
||||||
}
|
pub async fn remove_member(
|
||||||
|
user: CurrentUser,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((id, user_id)): Path<(Uuid, Uuid)>,
|
||||||
|
) -> Result<StatusCode, HTTPError> {
|
||||||
|
let role = state.repositories.role.get_by_id(id).await?.ok_or(HTTPError::NotFound)?;
|
||||||
|
require_permission(&state, &user, role.server_id, ServerPermission::MANAGE_MEMBERS).await?;
|
||||||
|
if state.services.role.remove_member(id, user_id, role.server_id).await? { Ok(StatusCode::NO_CONTENT) } else { Err(HTTPError::NotFound) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::domain::dto::role::{CreateRoleRequest, RoleResponse, UpdateRoleReques
|
|||||||
use sea_orm::Set;
|
use sea_orm::Set;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub fn group_model_to_group_response(model: role::Model) -> RoleResponse {
|
pub fn role_model_to_role_response(model: role::Model) -> RoleResponse {
|
||||||
RoleResponse {
|
RoleResponse {
|
||||||
id: model.id,
|
id: model.id,
|
||||||
server_id: model.server_id,
|
server_id: model.server_id,
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
use crate::core::state::AppState;
|
use crate::core::state::AppState;
|
||||||
use axum::{routing::get, Router};
|
use axum::{routing::{get, put}, Router};
|
||||||
|
|
||||||
use super::handlers;
|
use super::handlers;
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/groups", get(handlers::get_all).post(handlers::create))
|
.route("/roles", get(handlers::get_all).post(handlers::create))
|
||||||
.route(
|
.route(
|
||||||
"/groups/{id}",
|
"/roles/{id}",
|
||||||
get(handlers::get_by_id)
|
get(handlers::get_by_id)
|
||||||
.put(handlers::update)
|
.put(handlers::update)
|
||||||
.delete(handlers::delete),
|
.delete(handlers::delete),
|
||||||
)
|
)
|
||||||
|
.route("/roles/{id}/members", get(handlers::get_members))
|
||||||
|
.route("/roles/{id}/members/{user_id}", put(handlers::add_member).delete(handlers::remove_member))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use crate::domain::dto::server::{
|
|||||||
};
|
};
|
||||||
use crate::http::context::{CurrentUser, Superuser};
|
use crate::http::context::{CurrentUser, Superuser};
|
||||||
use crate::http::error::HTTPError;
|
use crate::http::error::HTTPError;
|
||||||
|
use crate::permissions::ServerPermission;
|
||||||
use crate::routes::server::mapper;
|
use crate::routes::server::mapper;
|
||||||
use axum::{
|
use axum::{
|
||||||
Json,
|
Json,
|
||||||
@@ -13,6 +14,27 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
async fn require_server_permission(
|
||||||
|
state: &AppState,
|
||||||
|
user: &CurrentUser,
|
||||||
|
server_id: Uuid,
|
||||||
|
permission: ServerPermission,
|
||||||
|
) -> Result<(), HTTPError> {
|
||||||
|
if user.is_superuser {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let granted = state
|
||||||
|
.repositories
|
||||||
|
.server
|
||||||
|
.get_user_permission(server_id, user.id)
|
||||||
|
.await?
|
||||||
|
.map(|value| ServerPermission::from_bits_truncate(value.permissions as u64))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if granted.contains(permission) { Ok(()) } else { Err(HTTPError::Forbidden) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Liste tous les serveurs
|
/// Liste tous les serveurs
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
get,
|
get,
|
||||||
@@ -112,7 +134,7 @@ pub async fn create(
|
|||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
pub async fn update(
|
pub async fn update(
|
||||||
_admin: Superuser,
|
user: CurrentUser,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
Json(payload): Json<UpdateServerRequest>,
|
Json(payload): Json<UpdateServerRequest>,
|
||||||
@@ -125,6 +147,8 @@ pub async fn update(
|
|||||||
.await?
|
.await?
|
||||||
.ok_or(HTTPError::NotFound)?;
|
.ok_or(HTTPError::NotFound)?;
|
||||||
|
|
||||||
|
require_server_permission(&state, &user, id, ServerPermission::MANAGE_SERVER).await?;
|
||||||
|
|
||||||
let server = state
|
let server = state
|
||||||
.services
|
.services
|
||||||
.server
|
.server
|
||||||
@@ -311,10 +335,12 @@ pub async fn get_role_permission(
|
|||||||
tag = "Server Permissions"
|
tag = "Server Permissions"
|
||||||
)]
|
)]
|
||||||
pub async fn set_role_permission(
|
pub async fn set_role_permission(
|
||||||
|
user: CurrentUser,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path((server_id, role_id)): Path<(Uuid, Uuid)>,
|
Path((server_id, role_id)): Path<(Uuid, Uuid)>,
|
||||||
Json(payload): Json<SetServerPermissionRequest>,
|
Json(payload): Json<SetServerPermissionRequest>,
|
||||||
) -> Result<Json<ServerRolePermissionResponse>, HTTPError> {
|
) -> Result<Json<ServerRolePermissionResponse>, HTTPError> {
|
||||||
|
require_server_permission(&state, &user, server_id, ServerPermission::MANAGE_ROLES).await?;
|
||||||
state
|
state
|
||||||
.repositories
|
.repositories
|
||||||
.server
|
.server
|
||||||
@@ -347,9 +373,11 @@ pub async fn set_role_permission(
|
|||||||
tag = "Server Permissions"
|
tag = "Server Permissions"
|
||||||
)]
|
)]
|
||||||
pub async fn remove_role_permission(
|
pub async fn remove_role_permission(
|
||||||
|
user: CurrentUser,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path((server_id, role_id)): Path<(Uuid, Uuid)>,
|
Path((server_id, role_id)): Path<(Uuid, Uuid)>,
|
||||||
) -> Result<StatusCode, HTTPError> {
|
) -> Result<StatusCode, HTTPError> {
|
||||||
|
require_server_permission(&state, &user, server_id, ServerPermission::MANAGE_ROLES).await?;
|
||||||
if state
|
if state
|
||||||
.repositories
|
.repositories
|
||||||
.server
|
.server
|
||||||
|
|||||||
+28
-8
@@ -1,6 +1,6 @@
|
|||||||
use crate::services::ServicesContext;
|
use crate::services::ServicesContext;
|
||||||
use crate::models::role;
|
use crate::models::role;
|
||||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait, Set};
|
use sea_orm::{ActiveModelTrait, EntityTrait, TransactionTrait};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -23,13 +23,13 @@ impl RoleService {
|
|||||||
|
|
||||||
let txn = db.begin().await?;
|
let txn = db.begin().await?;
|
||||||
|
|
||||||
let group = active.insert(&txn).await?;
|
let role = active.insert(&txn).await?;
|
||||||
|
|
||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
|
||||||
event_bus.emit("group_created", group.clone());
|
event_bus.emit("role_created", role.clone());
|
||||||
|
|
||||||
Ok(group)
|
Ok(role)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_role(
|
pub async fn update_role(
|
||||||
@@ -41,13 +41,13 @@ impl RoleService {
|
|||||||
|
|
||||||
let txn = db.begin().await?;
|
let txn = db.begin().await?;
|
||||||
|
|
||||||
let group = active.update(&txn).await?;
|
let role = active.update(&txn).await?;
|
||||||
|
|
||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
|
||||||
event_bus.emit("group_updated", group.clone());
|
event_bus.emit("role_updated", role.clone());
|
||||||
|
|
||||||
Ok(group)
|
Ok(role)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_role(&self, id: Uuid) -> Result<bool, anyhow::Error> {
|
pub async fn delete_role(&self, id: Uuid) -> Result<bool, anyhow::Error> {
|
||||||
@@ -65,9 +65,29 @@ impl RoleService {
|
|||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
|
||||||
if deleted {
|
if deleted {
|
||||||
event_bus.emit("group_deleted", id);
|
event_bus.emit("role_deleted", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(deleted)
|
Ok(deleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn add_member(&self, role_id: Uuid, user_id: Uuid, server_id: Uuid) -> Result<bool, anyhow::Error> {
|
||||||
|
let added = self.service_context.repositories.role.add_member(role_id, user_id).await?;
|
||||||
|
if added {
|
||||||
|
self.service_context
|
||||||
|
.event_bus
|
||||||
|
.emit("role_user_created", (role_id, user_id, server_id));
|
||||||
|
}
|
||||||
|
Ok(added)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_member(&self, role_id: Uuid, user_id: Uuid, server_id: Uuid) -> Result<bool, anyhow::Error> {
|
||||||
|
let removed = self.service_context.repositories.role.remove_member(role_id, user_id).await?;
|
||||||
|
if removed {
|
||||||
|
self.service_context
|
||||||
|
.event_bus
|
||||||
|
.emit("role_user_deleted", (role_id, user_id, server_id));
|
||||||
|
}
|
||||||
|
Ok(removed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user