Init
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
pub mod state;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::database::Database;
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
pub use state::AppState;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct App {
|
||||
pub state: AppState,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub async fn build(config: AppConfig) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let db_manager = Database::init(&config.database.url).await?;
|
||||
let db = db_manager.get_connection().clone();
|
||||
|
||||
Migrator::up(&db, None).await?;
|
||||
|
||||
let state = AppState {
|
||||
db,
|
||||
config: Arc::new(config),
|
||||
};
|
||||
|
||||
Ok(Self { state })
|
||||
}
|
||||
|
||||
pub async fn run(self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing::info!("Starting services...");
|
||||
|
||||
tokio::select! {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use crate::config::AppConfig;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppState {
|
||||
pub db: DatabaseConnection,
|
||||
pub config: Arc<AppConfig>,
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
use sea_orm::{ConnectOptions, Database as SeaDatabase, DatabaseConnection, DbErr};
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
@@ -19,10 +19,6 @@ impl Database {
|
||||
|
||||
let connection = SeaDatabase::connect(opt).await?;
|
||||
|
||||
// On lance les migrations ici.
|
||||
// Si ça échoue, le programme s'arrête proprement à l'init.
|
||||
Migrator::up(&connection, None).await?;
|
||||
|
||||
Ok(Self { connection })
|
||||
}
|
||||
|
||||
@@ -31,4 +27,4 @@ impl Database {
|
||||
pub fn get_connection(&self) -> &DatabaseConnection {
|
||||
&self.connection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod server;
|
||||
@@ -0,0 +1,23 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::Router;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::routes;
|
||||
|
||||
pub async fn start(config: &AppConfig) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = SocketAddr::new(
|
||||
std::net::IpAddr::V4(config.network.host),
|
||||
config.network.tcp_port,
|
||||
);
|
||||
|
||||
let app: Router = routes::router();
|
||||
|
||||
let listener = TcpListener::bind(addr).await?;
|
||||
tracing::info!(%addr, "HTTP server listening");
|
||||
|
||||
axum::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +1,11 @@
|
||||
pub mod config;
|
||||
pub mod database;
|
||||
pub mod http;
|
||||
pub mod models;
|
||||
// pub mod permissions_old;
|
||||
pub mod core;
|
||||
pub mod permissions;
|
||||
pub mod repositories;
|
||||
pub mod routes;
|
||||
pub mod udp;
|
||||
pub mod utils;
|
||||
|
||||
+9
-2
@@ -1,4 +1,7 @@
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
use oxspeak_server_lib::config::AppConfig;
|
||||
use oxspeak_server_lib::core::App;
|
||||
use oxspeak_server_lib::database::Database;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -19,10 +22,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
AppConfig::gen_config()?;
|
||||
tracing::info!(config_path = "config.toml", "Generated");
|
||||
tracing::info!("Config generated, please edit config.toml");
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
let config = AppConfig::load()?;
|
||||
tracing::info!(?config, "Loaded config");
|
||||
|
||||
// 3. Build & Run
|
||||
let app = App::build(config).await?;
|
||||
app.run().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -11,9 +11,13 @@ pub struct Model {
|
||||
pub id: Uuid,
|
||||
pub server_id: Uuid,
|
||||
pub name: String,
|
||||
pub permissions: u64,
|
||||
pub is_default: bool,
|
||||
pub created_at: DateTimeUtc,
|
||||
|
||||
/// Permissions serveur par défaut (stockées en i64, lues en u64)
|
||||
pub server_permissions: i64,
|
||||
pub channel_permissions: i64,
|
||||
pub voice_permissions: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
+20
-1
@@ -1,5 +1,6 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use crate::permissions::{ChannelPermission, PermissionSet, ServerPermission, VoicePermission};
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::prelude::async_trait::async_trait;
|
||||
use sea_orm::Set;
|
||||
@@ -13,7 +14,11 @@ pub struct Model {
|
||||
pub password: Option<String>,
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
pub default_permissions: u64,
|
||||
|
||||
/// Permissions serveur par défaut (stockées en i64, lues en u64)
|
||||
pub default_server_permissions: i64,
|
||||
pub default_channel_permissions: i64,
|
||||
pub default_voice_permissions: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -47,9 +52,23 @@ impl Related<super::server_user::Entity> for Entity {
|
||||
#[async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {
|
||||
fn new() -> Self {
|
||||
let default_perm = PermissionSet::DEFAULT;
|
||||
Self {
|
||||
id: Set(Uuid::new_v4()),
|
||||
default_server_permissions: Set(default_perm.server.bits() as i64),
|
||||
default_channel_permissions: Set(default_perm.channel.bits() as i64),
|
||||
default_voice_permissions: Set(default_perm.voice.bits() as i64),
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn default_permissions(&self) -> PermissionSet {
|
||||
PermissionSet {
|
||||
server: ServerPermission::from_bits_truncate(self.default_server_permissions as u64),
|
||||
channel: ChannelPermission::from_bits_truncate(self.default_channel_permissions as u64),
|
||||
voice: VoicePermission::from_bits_truncate(self.default_voice_permissions as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ pub struct Model {
|
||||
pub updated_at: DateTimeUtc,
|
||||
pub is_admin: bool,
|
||||
pub is_owner: bool,
|
||||
pub permissions: u64,
|
||||
|
||||
/// Permissions serveur par défaut (stockées en i64, lues en u64)
|
||||
pub server_permissions: i64,
|
||||
pub channel_permissions: i64,
|
||||
pub voice_permissions: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
bitflags! {
|
||||
/// Permissions liées aux serveurs (gestion globale)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ServerPermission: u64 {
|
||||
const ManageServer = 1 << 0;
|
||||
const ManageRoles = 1 << 1;
|
||||
const KickMember = 1 << 2;
|
||||
const BanMember = 1 << 3;
|
||||
// ... jusqu'à 64 perms serveur
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Permissions liées aux canaux (texte + vocal)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ChannelPermission: u64 {
|
||||
const ReadChannel = 1 << 0;
|
||||
const SendMessage = 1 << 1;
|
||||
const DeleteMessage = 1 << 2;
|
||||
const DeleteOthersMessage = 1 << 3;
|
||||
const ManageChannel = 1 << 4;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Permissions liées au vocal
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct VoicePermission: u64 {
|
||||
const JoinChannel = 1 << 0;
|
||||
const VoiceSpeak = 1 << 1;
|
||||
const VoiceMuteOthers = 1 << 2;
|
||||
const VoiceDeafenOthers = 1 << 3;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
/// Agrégat de permissions traversant tous les domaines.
|
||||
/// Utile pour les API et les contrôles complets.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct PermissionSet {
|
||||
pub server: ServerPermission,
|
||||
pub channel: ChannelPermission,
|
||||
pub voice: VoicePermission,
|
||||
}
|
||||
|
||||
impl PermissionSet {
|
||||
pub const fn new(
|
||||
server: ServerPermission,
|
||||
channel: ChannelPermission,
|
||||
voice: VoicePermission,
|
||||
) -> Self {
|
||||
Self {
|
||||
server,
|
||||
channel,
|
||||
voice,
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT: Self = Self::new(
|
||||
ServerPermission::empty(),
|
||||
ChannelPermission::from_bits_truncate(
|
||||
ChannelPermission::ReadChannel.bits()
|
||||
| ChannelPermission::SendMessage.bits()
|
||||
| ChannelPermission::DeleteMessage.bits(),
|
||||
),
|
||||
VoicePermission::from_bits_truncate(
|
||||
VoicePermission::JoinChannel.bits() | VoicePermission::VoiceSpeak.bits(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)] // Permet de sérialiser comme un simple entier
|
||||
pub struct Permission: u64 {
|
||||
/// Pouvoir voir le canal dans la liste et lire les messages
|
||||
const ReadChannel = 1 << 0;
|
||||
/// Pouvoir rejoindre le canal vocal
|
||||
const JoinChannel = 1 << 1;
|
||||
/// Pouvoir envoyer des messages
|
||||
const SendMessage = 1 << 2;
|
||||
/// Pouvoir supprimer ses propres messages
|
||||
const DeleteMessage = 1 << 3;
|
||||
/// Pouvoir supprimer les messages des autres (Modérateur)
|
||||
const DeleteOthersMessage = 1 << 4;
|
||||
/// Pouvoir modifier les paramètres du canal
|
||||
const ManageChannel = 1 << 5;
|
||||
/// Pouvoir gérer les groupes/permissions du serveur
|
||||
const ManageRoles = 1 << 6;
|
||||
/// Pouvoir expulser des membres du serveur
|
||||
const KickMember = 1 << 7;
|
||||
/// Pouvoir parler en vocal
|
||||
const VoiceSpeak = 1 << 8;
|
||||
/// Pouvoir rendre muet les autres utilisateurs en vocal
|
||||
const VoiceMuteOthers = 1 << 9;
|
||||
/// Pouvoir modifier les paramètres globaux du serveur
|
||||
const ManageServer = 1 << 10;
|
||||
}
|
||||
}
|
||||
|
||||
impl Permission {
|
||||
// ─── Presets de permissions ────────────────────────────────────────────
|
||||
|
||||
/// Permissions par défaut pour un utilisateur standard
|
||||
pub const DEFAULT: Self = Self::from_bits_truncate(
|
||||
Self::ReadChannel.bits()
|
||||
| Self::JoinChannel.bits()
|
||||
| Self::SendMessage.bits()
|
||||
| Self::DeleteMessage.bits()
|
||||
| Self::VoiceSpeak.bits(),
|
||||
);
|
||||
|
||||
/// Permissions de modération (sans gestion serveur)
|
||||
pub const MODERATOR: Self = Self::from_bits_truncate(
|
||||
Self::DEFAULT.bits()
|
||||
| Self::DeleteOthersMessage.bits()
|
||||
| Self::KickMember.bits()
|
||||
| Self::VoiceMuteOthers.bits(),
|
||||
);
|
||||
|
||||
/// Permissions d'administrateur (accès complet hormis l'ownership)
|
||||
pub const ADMIN: Self = Self::from_bits_truncate(
|
||||
Self::MODERATOR.bits() | Self::ManageChannel.bits() | Self::ManageRoles.bits(),
|
||||
);
|
||||
|
||||
/// Toutes les permissions (équivalent owner)
|
||||
pub const OWNER: Self = Self::all();
|
||||
|
||||
// ─── Groupes logiques ──────────────────────────────────────────────────
|
||||
|
||||
/// Toutes les permissions liées au chat
|
||||
pub const CHAT_PERMISSIONS: Self = Self::from_bits_truncate(
|
||||
Self::ReadChannel.bits()
|
||||
| Self::SendMessage.bits()
|
||||
| Self::DeleteMessage.bits()
|
||||
| Self::DeleteOthersMessage.bits(),
|
||||
);
|
||||
|
||||
/// Toutes les permissions liées au vocal
|
||||
pub const VOICE_PERMISSIONS: Self = Self::from_bits_truncate(
|
||||
Self::JoinChannel.bits() | Self::VoiceSpeak.bits() | Self::VoiceMuteOthers.bits(),
|
||||
);
|
||||
|
||||
/// Toutes les permissions de gestion (Manage*)
|
||||
pub const MANAGEMENT_PERMISSIONS: Self = Self::from_bits_truncate(
|
||||
Self::ManageChannel.bits() | Self::ManageRoles.bits() | Self::ManageServer.bits(),
|
||||
);
|
||||
|
||||
// ─── Helpers de vérification ───────────────────────────────────────────
|
||||
|
||||
/// Vérifie si l'utilisateur peut effectuer des actions de modération
|
||||
#[inline]
|
||||
pub fn is_moderator(&self) -> bool {
|
||||
self.contains(Self::DeleteOthersMessage) || self.contains(Self::KickMember)
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur a au moins une permission de gestion
|
||||
#[inline]
|
||||
pub fn is_admin(&self) -> bool {
|
||||
self.intersects(Self::MANAGEMENT_PERMISSIONS)
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur a tous les droits (owner)
|
||||
#[inline]
|
||||
pub fn is_owner(&self) -> bool {
|
||||
*self == Self::OWNER
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur peut accéder à un canal vocal
|
||||
#[inline]
|
||||
pub fn can_use_voice(&self) -> bool {
|
||||
self.contains(Self::JoinChannel) && self.contains(Self::VoiceSpeak)
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur peut interagir avec le chat (lire ET écrire)
|
||||
#[inline]
|
||||
pub fn can_chat(&self) -> bool {
|
||||
self.contains(Self::ReadChannel | Self::SendMessage)
|
||||
}
|
||||
|
||||
// ─── Conversion / Sérialisation ────────────────────────────────────────
|
||||
|
||||
/// Retourne la liste des noms des permissions actives
|
||||
/// Utile pour les API REST ou les logs
|
||||
pub fn to_names(&self) -> Vec<&'static str> {
|
||||
self.iter_names().map(|(name, _)| name).collect()
|
||||
}
|
||||
|
||||
/// Construit depuis une liste de noms (pour parsing JSON par ex)
|
||||
pub fn from_names(names: &[&str]) -> Self {
|
||||
names
|
||||
.iter()
|
||||
.filter_map(|name| Self::from_name(name))
|
||||
.fold(Self::empty(), |acc, p| acc | p)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Permission {
|
||||
fn from(bits: u64) -> Self {
|
||||
Self::from_bits_truncate(bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Permission> for u64 {
|
||||
fn from(perms: Permission) -> Self {
|
||||
perms.bits()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Ce module permet de :
|
||||
|
||||
- Gérer/simplifier les interactions avec la base de données
|
||||
- Avoir un système de Signal qui se déclenche lors de modifications
|
||||
dans la base de données (Création, Modification, Suppression)
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::sync::Arc;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter,
|
||||
};
|
||||
use crate::models::category;
|
||||
use crate::repositories::RepositoryContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CategoryRepository {
|
||||
pub context: Arc<RepositoryContext>
|
||||
}
|
||||
|
||||
impl CategoryRepository {
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<category::Model>, DbErr> {
|
||||
category::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_all(&self) -> Result<Vec<category::Model>, DbErr> {
|
||||
category::Entity::find().all(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_server_id(&self, server_id: uuid::Uuid) -> Result<Vec<category::Model>, DbErr> {
|
||||
category::Entity::find()
|
||||
.filter(category::Column::ServerId.eq(server_id))
|
||||
.all(&self.context.db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: category::ActiveModel) -> Result<category::Model, DbErr> {
|
||||
let category = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("Category_updated", category.clone());
|
||||
Ok(category)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: category::ActiveModel) -> Result<category::Model, DbErr> {
|
||||
let category = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("Category_created", category.clone());
|
||||
Ok(category)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
category::Entity::delete_by_id(id).exec(&self.context.db).await?;
|
||||
self.context.events.emit("Category_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::models::channel;
|
||||
use crate::repositories::RepositoryContext;
|
||||
use sea_orm::{ActiveModelTrait, DbErr, EntityTrait};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChannelRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl ChannelRepository {
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<channel::Model>, DbErr> {
|
||||
channel::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: channel::ActiveModel) -> Result<channel::Model, DbErr> {
|
||||
let channel = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("channel_updated", channel.clone());
|
||||
Ok(channel)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: channel::ActiveModel) -> Result<channel::Model, DbErr> {
|
||||
let channel = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("channel_created", channel.clone());
|
||||
Ok(channel)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
channel::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("channel_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use crate::models::group;
|
||||
use crate::repositories::RepositoryContext;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GroupRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl GroupRepository {
|
||||
pub async fn get_all_by_server(&self, server_id: Uuid) -> Result<Vec<group::Model>, DbErr> {
|
||||
group::Entity::find()
|
||||
.filter(group::Column::ServerId.eq(server_id))
|
||||
.all(&self.context.db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: Uuid) -> Result<Option<group::Model>, DbErr> {
|
||||
group::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: group::ActiveModel) -> Result<group::Model, DbErr> {
|
||||
let group = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("group_created", group.clone());
|
||||
Ok(group)
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: group::ActiveModel) -> Result<group::Model, DbErr> {
|
||||
let group = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("group_updated", group.clone());
|
||||
Ok(group)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: Uuid) -> Result<bool, DbErr> {
|
||||
let res = group::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("group_deleted", id);
|
||||
Ok(res.rows_affected > 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
use sea_orm::{DbErr, EntityTrait, ActiveModelTrait};
|
||||
use crate::models::message;
|
||||
use crate::repositories::RepositoryContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MessageRepository {
|
||||
pub context: Arc<RepositoryContext>
|
||||
}
|
||||
|
||||
impl MessageRepository {
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<message::Model>, DbErr> {
|
||||
message::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: message::ActiveModel) -> Result<message::Model, DbErr> {
|
||||
let message = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("message_updated", message.clone());
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: message::ActiveModel) -> Result<message::Model, DbErr> {
|
||||
let message = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("message_created", message.clone());
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
message::Entity::delete_by_id(id).exec(&self.context.db).await?;
|
||||
self.context.events.emit("message_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use crate::repositories::category::CategoryRepository;
|
||||
use crate::repositories::channel::ChannelRepository;
|
||||
use crate::repositories::group::GroupRepository;
|
||||
use crate::repositories::message::MessageRepository;
|
||||
use crate::repositories::server::ServerRepository;
|
||||
use crate::repositories::user::UserRepository;
|
||||
use event_bus::EventBus;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod category;
|
||||
mod channel;
|
||||
mod group;
|
||||
mod message;
|
||||
mod server;
|
||||
pub mod types;
|
||||
mod user;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RepositoryContext {
|
||||
db: DatabaseConnection,
|
||||
events: Arc<EventBus>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Repositories {
|
||||
pub server: ServerRepository,
|
||||
pub category: CategoryRepository,
|
||||
pub channel: ChannelRepository,
|
||||
pub group: GroupRepository,
|
||||
pub message: MessageRepository,
|
||||
pub user: UserRepository,
|
||||
}
|
||||
|
||||
impl Repositories {
|
||||
pub fn new(db: &DatabaseConnection, events: Arc<EventBus>) -> Self {
|
||||
let context = Arc::new(RepositoryContext {
|
||||
db: db.clone(),
|
||||
events,
|
||||
});
|
||||
|
||||
Self {
|
||||
server: ServerRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
category: CategoryRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
channel: ChannelRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
group: GroupRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
message: MessageRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
user: UserRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
use super::types::{ServerExplorerItem, ServerTree};
|
||||
use super::RepositoryContext;
|
||||
use crate::models::{category, channel, group, server};
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter, Set};
|
||||
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServerRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl ServerRepository {
|
||||
pub async fn get_all(&self) -> Result<Vec<server::Model>, DbErr> {
|
||||
server::Entity::find().all(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<server::Model>, DbErr> {
|
||||
server::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: server::ActiveModel) -> Result<server::Model, DbErr> {
|
||||
let server = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("server_updated", server.clone());
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: server::ActiveModel) -> Result<server::Model, DbErr> {
|
||||
let server = active.insert(&self.context.db).await?;
|
||||
|
||||
// Créer le groupe par défaut pour le serveur
|
||||
let default_group = group::ActiveModel {
|
||||
server_id: Set(server.id),
|
||||
name: Set("Membres".to_string()),
|
||||
is_default: Set(true),
|
||||
..Default::default()
|
||||
};
|
||||
default_group.insert(&self.context.db).await?;
|
||||
|
||||
self.context.events.emit("server_created", server.clone());
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<bool, DbErr> {
|
||||
let res = server::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("server_deleted", id);
|
||||
Ok(res.rows_affected > 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
impl ServerRepository {
|
||||
pub async fn get_tree(&self, server_id: Uuid) -> Result<ServerTree, DbErr> {
|
||||
// 1. Récupération des catégories avec leurs channels
|
||||
let categories_with_channels = category::Entity::find()
|
||||
.filter(category::Column::ServerId.eq(server_id))
|
||||
.find_with_related(channel::Entity)
|
||||
.all(&self.context.db)
|
||||
.await?;
|
||||
|
||||
// 2. Récupération des channels orphelins (sans catégorie)
|
||||
let orphan_channels = channel::Entity::find()
|
||||
.filter(channel::Column::ServerId.eq(server_id))
|
||||
.filter(channel::Column::CategoryId.is_null())
|
||||
.all(&self.context.db)
|
||||
.await?;
|
||||
|
||||
// 3. Transformation et tri des enfants
|
||||
let mut items: Vec<ServerExplorerItem> = Vec::new();
|
||||
|
||||
for (cat, mut channels) in categories_with_channels {
|
||||
// On trie les channels internes (obligatoire car SQL ne garantit aucun ordre ici)
|
||||
channels.sort_by(|a, b| {
|
||||
a.position
|
||||
.cmp(&b.position)
|
||||
.then(a.created_at.cmp(&b.created_at))
|
||||
});
|
||||
items.push(ServerExplorerItem::Category(cat, channels));
|
||||
}
|
||||
|
||||
for chan in orphan_channels {
|
||||
items.push(ServerExplorerItem::Channel(chan));
|
||||
}
|
||||
|
||||
// 4. Tri final de la liste globale (Mélange catégories et orphelins)
|
||||
items.sort_by(|a, b| {
|
||||
let pos_cmp = a.position().cmp(&b.position());
|
||||
|
||||
if pos_cmp == std::cmp::Ordering::Equal {
|
||||
// Départage par date si position identique
|
||||
let date_a = match a {
|
||||
ServerExplorerItem::Category(c, _) => c.created_at,
|
||||
ServerExplorerItem::Channel(c) => c.created_at,
|
||||
};
|
||||
let date_b = match b {
|
||||
ServerExplorerItem::Category(c, _) => c.created_at,
|
||||
ServerExplorerItem::Channel(c) => c.created_at,
|
||||
};
|
||||
date_a.cmp(&date_b)
|
||||
} else {
|
||||
pos_cmp
|
||||
}
|
||||
});
|
||||
|
||||
Ok(ServerTree { items })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::models::{category, channel};
|
||||
|
||||
pub enum ServerExplorerItem {
|
||||
Category(category::Model, Vec<channel::Model>),
|
||||
Channel(channel::Model),
|
||||
}
|
||||
|
||||
// Pour pouvoir trier facilement
|
||||
impl ServerExplorerItem {
|
||||
pub fn position(&self) -> i32 {
|
||||
match self {
|
||||
ServerExplorerItem::Category(cat, _) => cat.position,
|
||||
ServerExplorerItem::Channel(chan) => chan.position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerTree {
|
||||
pub items: Vec<ServerExplorerItem>,
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use crate::models::user;
|
||||
use crate::repositories::RepositoryContext;
|
||||
use crate::utils::password;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, PaginatorTrait,
|
||||
QueryFilter, Set,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UserRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl UserRepository {
|
||||
pub async fn get_all(&self) -> Result<Vec<user::Model>, DbErr> {
|
||||
user::Entity::find().all(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn count(&self) -> Result<u64, DbErr> {
|
||||
user::Entity::find().count(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<user::Model>, DbErr> {
|
||||
user::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_username(&self, username: String) -> Result<Option<user::Model>, DbErr> {
|
||||
user::Entity::find()
|
||||
.filter(user::Column::Username.eq(username))
|
||||
.one(&self.context.db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn check_password(
|
||||
&self,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> Result<user::Model, DbErr> {
|
||||
let user = self.get_by_username(username).await?;
|
||||
if let Some(user) = user {
|
||||
let password_ok = password::verify_password(password.as_str(), user.password.as_str())
|
||||
.map_err(|_| DbErr::Custom("Password hashing failed".to_string()))?;
|
||||
|
||||
if password_ok {
|
||||
return Ok(user);
|
||||
}
|
||||
}
|
||||
Err(DbErr::Custom("Invalid username or password".to_string()))
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: user::ActiveModel) -> Result<user::Model, DbErr> {
|
||||
let user = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("user_updated", user.clone());
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: user::ActiveModel) -> Result<user::Model, DbErr> {
|
||||
let user = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("user_created", user.clone());
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn set_password(&self, id: uuid::Uuid, password: String) -> Result<(), DbErr> {
|
||||
let user = self
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or_else(|| DbErr::Custom("User not found".to_string()))?;
|
||||
|
||||
let mut active = user.into_active_model();
|
||||
let password = password::hash_password(&password)
|
||||
.map_err(|_| DbErr::Custom("Password hashing failed".to_string()))?;
|
||||
active.password = Set(password);
|
||||
|
||||
let user = self.update(active).await?;
|
||||
|
||||
self.context.events.emit("user_changed", user);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
user::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("user_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Attachment {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateAttachmentRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateAttachmentRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AttachmentResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Attachment, dto::AttachmentResponse};
|
||||
|
||||
pub fn to_response(_item: Attachment) -> AttachmentResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/attachments", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/attachments/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Attachment;
|
||||
|
||||
pub async fn find_all() -> Vec<Attachment> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Attachment> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Attachment) -> Attachment {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Attachment) -> Option<Attachment> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Category {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateCategoryRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateCategoryRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CategoryResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Category, dto::CategoryResponse};
|
||||
|
||||
pub fn to_response(_item: Category) -> CategoryResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/categorys", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/categorys/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Category;
|
||||
|
||||
pub async fn find_all() -> Vec<Category> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Category> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Category) -> Category {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Category) -> Option<Category> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Channel {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateChannelRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateChannelRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChannelResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Channel, dto::ChannelResponse};
|
||||
|
||||
pub fn to_response(_item: Channel) -> ChannelResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/channels", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/channels/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Channel;
|
||||
|
||||
pub async fn find_all() -> Vec<Channel> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Channel> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Channel) -> Channel {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Channel) -> Option<Channel> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Group {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateGroupRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateGroupRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct GroupResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Group, dto::GroupResponse};
|
||||
|
||||
pub fn to_response(_item: Group) -> GroupResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/groups", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/groups/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Group;
|
||||
|
||||
pub async fn find_all() -> Vec<Group> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Group> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Group) -> Group {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Group) -> Option<Group> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Message {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateMessageRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateMessageRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct MessageResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Message, dto::MessageResponse};
|
||||
|
||||
pub fn to_response(_item: Message) -> MessageResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/messages", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/messages/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Message;
|
||||
|
||||
pub async fn find_all() -> Vec<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Message) -> Message {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Message) -> Option<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use axum::Router;
|
||||
|
||||
pub mod attachment;
|
||||
pub mod category;
|
||||
pub mod channel;
|
||||
pub mod group;
|
||||
pub mod message;
|
||||
pub mod server;
|
||||
pub mod user;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.merge(user::routes::router())
|
||||
.merge(server::routes::router())
|
||||
.merge(channel::routes::router())
|
||||
.merge(message::routes::router())
|
||||
.merge(group::routes::router())
|
||||
.merge(category::routes::router())
|
||||
.merge(attachment::routes::router())
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Server {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateServerRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateServerRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ServerResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Server, dto::ServerResponse};
|
||||
|
||||
pub fn to_response(_item: Server) -> ServerResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/servers", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/servers/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Server;
|
||||
|
||||
pub async fn find_all() -> Vec<Server> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Server> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Server) -> Server {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Server) -> Option<Server> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct User {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateUserRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateUserRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::User, dto::UserResponse};
|
||||
|
||||
pub fn to_response(_user: User) -> UserResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/users", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/users/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::User;
|
||||
|
||||
pub async fn find_all() -> Vec<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_user: User) -> User {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _user: User) -> Option<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod password;
|
||||
@@ -0,0 +1,25 @@
|
||||
use argon2::{
|
||||
password_hash::{phc::PasswordHash, PasswordHasher, PasswordVerifier}, Algorithm, Argon2, Params,
|
||||
Version,
|
||||
};
|
||||
|
||||
/// Hache un password avec Argon2id
|
||||
/// Génère automatiquement un salt cryptographiquement sûr
|
||||
pub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {
|
||||
let params = Params::new(65540, 18, 1, None)?;
|
||||
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
|
||||
argon2
|
||||
.hash_password(password.as_bytes())
|
||||
.map(|hash| hash.to_string())
|
||||
}
|
||||
|
||||
/// Vérifie un password contre son hash
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, argon2::password_hash::Error> {
|
||||
let parsed_hash = PasswordHash::new(hash)?;
|
||||
let argon2 = Argon2::default();
|
||||
|
||||
Ok(argon2
|
||||
.verify_password(password.as_bytes(), &parsed_hash)
|
||||
.is_ok())
|
||||
}
|
||||
Reference in New Issue
Block a user