diff --git a/src/api/v1/servers/uuid/mod.rs b/src/api/v1/servers/uuid/mod.rs index 2c6ebc7..ffa1ae7 100644 --- a/src/api/v1/servers/uuid/mod.rs +++ b/src/api/v1/servers/uuid/mod.rs @@ -2,6 +2,7 @@ use actix_web::{get, web, Error, HttpRequest, HttpResponse, Scope}; use uuid::Uuid; mod channels; +mod roles; use crate::{api::v1::auth::check_access_token, structs::{Guild, Member}, utils::get_auth_header, Data}; @@ -9,8 +10,12 @@ pub fn web() -> Scope { web::scope("") .service(res) .service(channels::response) + .service(channels::response_post) .service(channels::uuid::res) .service(channels::uuid::messages::res) + .service(roles::response) + .service(roles::response_post) + .service(roles::uuid::res) } #[get("/{uuid}")] diff --git a/src/api/v1/servers/uuid/roles/mod.rs b/src/api/v1/servers/uuid/roles/mod.rs new file mode 100644 index 0000000..23499de --- /dev/null +++ b/src/api/v1/servers/uuid/roles/mod.rs @@ -0,0 +1,99 @@ +use actix_web::{get, post, web, Error, HttpRequest, HttpResponse}; +use serde::Deserialize; +use crate::{api::v1::auth::check_access_token, structs::{Member, Role}, utils::get_auth_header, Data}; +use ::uuid::Uuid; +use log::error; + +pub mod uuid; + +#[derive(Deserialize)] +struct RoleInfo { + name: String, +} + +#[get("{uuid}/roles")] +pub async fn response(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data) -> Result { + let headers = req.headers(); + + let auth_header = get_auth_header(headers); + + if let Err(error) = auth_header { + return Ok(error) + } + + let guild_uuid = path.into_inner().0; + + let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; + + if let Err(error) = authorized { + return Ok(error) + } + + let uuid = authorized.unwrap(); + + let member = Member::fetch_one(&data.pool, uuid, guild_uuid).await; + + if let Err(error) = member { + return Ok(error); + } + + let cache_result = data.get_cache_key(format!("{}_roles", guild_uuid)).await; + + if let Ok(cache_hit) = cache_result { + return Ok(HttpResponse::Ok().content_type("application/json").body(cache_hit)) + } + + let roles_result = Role::fetch_all(&data.pool, guild_uuid).await; + + if let Err(error) = roles_result { + return Ok(error) + } + + let roles = roles_result.unwrap(); + + let cache_result = data.set_cache_key(format!("{}_roles", guild_uuid), roles.clone(), 1800).await; + + if let Err(error) = cache_result { + error!("{}", error); + return Ok(HttpResponse::InternalServerError().finish()); + } + + Ok(HttpResponse::Ok().json(roles)) +} + +#[post("{uuid}/roles")] +pub async fn response_post(req: HttpRequest, role_info: web::Json, path: web::Path<(Uuid,)>, data: web::Data) -> Result { + let headers = req.headers(); + + let auth_header = get_auth_header(headers); + + if let Err(error) = auth_header { + return Ok(error) + } + + let guild_uuid = path.into_inner().0; + + let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; + + if let Err(error) = authorized { + return Ok(error) + } + + let uuid = authorized.unwrap(); + + let member = Member::fetch_one(&data.pool, uuid, guild_uuid).await; + + if let Err(error) = member { + return Ok(error); + } + + // FIXME: Logic to check permissions, should probably be done in utils.rs + + let role = Role::new(&data.pool, guild_uuid, role_info.name.clone()).await; + + if let Err(error) = role { + return Ok(error); + } + + Ok(HttpResponse::Ok().json(role.unwrap())) +} diff --git a/src/api/v1/servers/uuid/roles/uuid.rs b/src/api/v1/servers/uuid/roles/uuid.rs new file mode 100644 index 0000000..3e55d5a --- /dev/null +++ b/src/api/v1/servers/uuid/roles/uuid.rs @@ -0,0 +1,54 @@ +use actix_web::{get, web, Error, HttpRequest, HttpResponse}; +use crate::{api::v1::auth::check_access_token, structs::{Member, Role}, utils::get_auth_header, Data}; +use ::uuid::Uuid; +use log::error; + +#[get("{uuid}/roles/{role_uuid}")] +pub async fn res(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Data) -> Result { + let headers = req.headers(); + + let auth_header = get_auth_header(headers); + + if let Err(error) = auth_header { + return Ok(error) + } + + let (guild_uuid, role_uuid) = path.into_inner(); + + let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; + + if let Err(error) = authorized { + return Ok(error) + } + + let uuid = authorized.unwrap(); + + let member = Member::fetch_one(&data.pool, uuid, guild_uuid).await; + + if let Err(error) = member { + return Ok(error); + } + + let cache_result = data.get_cache_key(format!("{}", role_uuid)).await; + + if let Ok(cache_hit) = cache_result { + return Ok(HttpResponse::Ok().content_type("application/json").body(cache_hit)) + } + + let role_result = Role::fetch_one(&data.pool, guild_uuid, role_uuid).await; + + if let Err(error) = role_result { + return Ok(error) + } + + let role = role_result.unwrap(); + + let cache_result = data.set_cache_key(format!("{}", role_uuid), role.clone(), 60).await; + + if let Err(error) = cache_result { + error!("{}", error); + return Ok(HttpResponse::InternalServerError().finish()); + } + + Ok(HttpResponse::Ok().json(role)) +} diff --git a/src/structs.rs b/src/structs.rs index b5ad6ea..790889b 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -298,9 +298,33 @@ impl Guild { } } -#[derive(Serialize, FromRow)] -pub struct Role { +#[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, @@ -309,17 +333,68 @@ pub struct Role { impl Role { pub async fn fetch_all(pool: &Pool, guild_uuid: Uuid) -> Result, HttpResponse> { - let roles = sqlx::query_as(&format!("SELECT (uuid, name, color, position, permissions) FROM roles WHERE guild_uuid = '{}'", guild_uuid)) + 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) = roles { + if let Err(error) = role_builders_result { error!("{}", error); return Err(HttpResponse::InternalServerError().finish()) } - Ok(roles.unwrap()) + 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) } }