use std::str::FromStr; use serde::{Deserialize, Serialize}; use sqlx::{prelude::FromRow, Pool, Postgres}; use uuid::Uuid; use actix_web::HttpResponse; use log::error; use crate::Data; #[derive(Serialize, Deserialize, Clone)] pub struct Channel { pub uuid: Uuid, pub guild_uuid: Uuid, name: String, description: Option, pub permissions: Vec } #[derive(Serialize, Clone, FromRow)] struct ChannelPermissionBuilder { role_uuid: String, permissions: i32 } impl ChannelPermissionBuilder { fn build(&self) -> ChannelPermission { ChannelPermission { role_uuid: Uuid::from_str(&self.role_uuid).unwrap(), permissions: self.permissions, } } } #[derive(Serialize, Deserialize, Clone, FromRow)] pub struct ChannelPermission { pub role_uuid: Uuid, pub permissions: i32 } impl Channel { pub async fn fetch_all(pool: &Pool, guild_uuid: Uuid) -> Result, HttpResponse> { let row = sqlx::query_as(&format!("SELECT CAST(uuid AS VARCHAR), name, description FROM channels WHERE guild_uuid = '{}'", guild_uuid)) .fetch_all(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let channels: Vec<(String, String, Option)> = row.unwrap(); let futures = channels.iter().map(async |t| { let (uuid, name, description) = t.to_owned(); let row = sqlx::query_as(&format!("SELECT CAST(role_uuid AS VARCHAR), permissions FROM channel_permissions WHERE channel_uuid = '{}'", uuid)) .fetch_all(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let channel_permission_builders: Vec = row.unwrap(); Ok(Self { uuid: Uuid::from_str(&uuid).unwrap(), guild_uuid, name, description, permissions: channel_permission_builders.iter().map(|b| b.build()).collect(), }) }); let channels = futures::future::join_all(futures).await; let channels: Result, HttpResponse> = channels.into_iter().collect(); Ok(channels?) } pub async fn fetch_one(pool: &Pool, guild_uuid: Uuid, channel_uuid: Uuid) -> Result { let row = sqlx::query_as(&format!("SELECT name, description FROM channels WHERE guild_uuid = '{}' AND uuid = '{}'", guild_uuid, channel_uuid)) .fetch_one(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let (name, description): (String, Option) = row.unwrap(); let row = sqlx::query_as(&format!("SELECT CAST(role_uuid AS VARCHAR), permissions FROM channel_permissions WHERE channel_uuid = '{}'", channel_uuid)) .fetch_all(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let channel_permission_builders: Vec = row.unwrap(); Ok(Self { uuid: channel_uuid, guild_uuid, name, description, permissions: channel_permission_builders.iter().map(|b| b.build()).collect(), }) } pub async fn new(data: actix_web::web::Data, guild_uuid: Uuid, name: String, description: Option) -> Result { let channel_uuid = Uuid::now_v7(); let row = sqlx::query(&format!("INSERT INTO channels (uuid, guild_uuid, name, description) VALUES ('{}', '{}', $1, $2)", channel_uuid, guild_uuid)) .bind(&name) .bind(&description) .execute(&data.pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let channel = Self { uuid: channel_uuid, guild_uuid, name, description, permissions: vec![], }; let cache_result = data.set_cache_key(channel_uuid.to_string(), channel.clone(), 1800).await; if let Err(error) = cache_result { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()); } let cache_deletion_result = data.del_cache_key(format!("{}_channels", guild_uuid)).await; if let Err(error) = cache_deletion_result { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()); } Ok(channel) } pub async fn fetch_messages(&self, pool: &Pool, amount: i64, offset: i64) -> Result, HttpResponse> { let row = sqlx::query_as(&format!("SELECT uuid, user_uuid, message FROM channels WHERE channel_uuid = '{}' ORDER BY uuid LIMIT $1 OFFSET $2", self.uuid)) .bind(amount) .bind(offset) .fetch_all(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()); } let message_builders: Vec = row.unwrap(); Ok(message_builders.iter().map(|b| b.build()).collect()) } } #[derive(Clone, Copy)] pub enum Permissions { SendMessage = 1, CreateChannel = 2, DeleteChannel = 4, ManageChannel = 8, CreateRole = 16, DeleteRole = 32, ManageRole = 64, CreateInvite = 128, ManageInvite = 256, ManageServer = 512, ManageMember = 1024, } impl Permissions { pub fn fetch_permissions(permissions: i64) -> Vec { let all_perms = vec![ Self::SendMessage, Self::CreateChannel, Self::DeleteChannel, Self::ManageChannel, Self::CreateRole, Self::DeleteRole, Self::ManageRole, Self::CreateInvite, Self::ManageInvite, Self::ManageServer, Self::ManageMember, ]; all_perms.into_iter() .filter(|p| permissions & (*p as i64) != 0) .collect() } } #[derive(Serialize)] pub struct Guild { pub uuid: Uuid, name: String, description: Option, icon: String, owner_uuid: Uuid, pub roles: Vec, member_count: i64, } impl Guild { pub async fn fetch_one(pool: &Pool, guild_uuid: Uuid) -> Result { let row = sqlx::query_as(&format!("SELECT CAST(owner_uuid AS VARCHAR), name, description FROM guilds WHERE uuid = '{}'", guild_uuid)) .fetch_one(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let (owner_uuid_raw, name, description): (String, String, Option) = row.unwrap(); let owner_uuid = Uuid::from_str(&owner_uuid_raw).unwrap(); let member_count = Member::count(pool, guild_uuid).await?; let roles = Role::fetch_all(pool, guild_uuid).await?; Ok(Self { uuid: guild_uuid, name, description, // FIXME: This isnt supposed to be bogus icon: String::from("bogus"), owner_uuid, roles, member_count, }) } pub async fn new(pool: &Pool, name: String, description: Option, owner_uuid: Uuid) -> Result { let guild_uuid = Uuid::now_v7(); let row = sqlx::query(&format!("INSERT INTO guilds (uuid, owner_uuid, name, description) VALUES ('{}', '{}', $1, $2)", guild_uuid, owner_uuid)) .bind(&name) .bind(&description) .execute(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let row = sqlx::query(&format!("INSERT INTO guild_members (uuid, guild_uuid, user_uuid) VALUES ('{}', '{}', '{}')", Uuid::now_v7(), guild_uuid, owner_uuid)) .execute(pool) .await; if let Err(error) = row { error!("{}", error); let row = sqlx::query(&format!("DELETE FROM guilds WHERE uuid = '{}'", guild_uuid)) .execute(pool) .await; if let Err(error) = row { error!("{}", error); } return Err(HttpResponse::InternalServerError().finish()) } Ok(Guild { uuid: guild_uuid, name, description, icon: "bogus".to_string(), owner_uuid, roles: vec![], member_count: 1 }) } } #[derive(FromRow)] struct RoleBuilder { uuid: String, guild_uuid: String, name: String, color: i64, position: i32, permissions: i64, } impl RoleBuilder { fn build(&self) -> Role { Role { uuid: Uuid::from_str(&self.uuid).unwrap(), guild_uuid: Uuid::from_str(&self.guild_uuid).unwrap(), name: self.name.clone(), color: self.color, position: self.position, permissions: self.permissions, } } } #[derive(Serialize, Clone)] pub struct Role { uuid: Uuid, guild_uuid: Uuid, name: String, color: i64, position: i32, permissions: i64, } impl Role { pub async fn fetch_all(pool: &Pool, guild_uuid: Uuid) -> Result, HttpResponse> { let role_builders_result = sqlx::query_as(&format!("SELECT (uuid, guild_uuid, name, color, position, permissions) FROM roles WHERE guild_uuid = '{}'", guild_uuid)) .fetch_all(pool) .await; if let Err(error) = role_builders_result { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let role_builders: Vec = role_builders_result.unwrap(); Ok(role_builders.iter().map(|b| b.build()).collect()) } pub async fn fetch_one(pool: &Pool, role_uuid: Uuid, guild_uuid: Uuid) -> Result { let row = sqlx::query_as(&format!("SELECT (name, color, position, permissions) FROM roles WHERE guild_uuid = '{}' AND uuid = '{}'", guild_uuid, role_uuid)) .fetch_one(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let (name, color, position, permissions) = row.unwrap(); Ok(Role { uuid: role_uuid, guild_uuid, name, color, position, permissions, }) } pub async fn new(pool: &Pool, guild_uuid: Uuid, name: String) -> Result { let role_uuid = Uuid::now_v7(); let row = sqlx::query(&format!("INSERT INTO channels (uuid, guild_uuid, name, position) VALUES ('{}', '{}', $1, $2)", role_uuid, guild_uuid)) .bind(&name) .bind(0) .execute(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let role = Self { uuid: role_uuid, guild_uuid, name, color: 16777215, position: 0, permissions: 0, }; Ok(role) } } pub struct Member { pub uuid: Uuid, pub nickname: String, pub user_uuid: Uuid, pub guild_uuid: Uuid, } impl Member { async fn count(pool: &Pool, guild_uuid: Uuid) -> Result { let member_count = sqlx::query_scalar(&format!("SELECT COUNT(uuid) FROM guild_members WHERE guild_uuid = '{}'", guild_uuid)) .fetch_one(pool) .await; if let Err(error) = member_count { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } Ok(member_count.unwrap()) } pub async fn fetch_one(pool: &Pool, user_uuid: Uuid, guild_uuid: Uuid) -> Result { let row = sqlx::query_as(&format!("SELECT CAST(uuid AS VARCHAR), nickname FROM guild_members WHERE guild_uuid = '{}' AND user_uuid = '{}'", guild_uuid, user_uuid)) .fetch_one(pool) .await; if let Err(error) = row { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } let (uuid, nickname): (String, String) = row.unwrap(); Ok(Member { uuid: Uuid::from_str(&uuid).unwrap(), nickname, user_uuid, guild_uuid, }) } } #[derive(FromRow)] struct MessageBuilder { uuid: String, channel_uuid: String, user_uuid: String, message: String, } impl MessageBuilder { fn build(&self) -> Message { Message { uuid: Uuid::from_str(&self.uuid).unwrap(), channel_uuid: Uuid::from_str(&self.channel_uuid).unwrap(), user_uuid: Uuid::from_str(&self.user_uuid).unwrap(), message: self.message.clone(), } } } #[derive(Serialize)] pub struct Message { uuid: Uuid, channel_uuid: Uuid, user_uuid: Uuid, message: String, }