diff --git a/src/api/v1/servers/mod.rs b/src/api/v1/servers/mod.rs index 8e536be..7c74ff0 100644 --- a/src/api/v1/servers/mod.rs +++ b/src/api/v1/servers/mod.rs @@ -1,9 +1,9 @@ -use actix_web::{Error, HttpRequest, HttpResponse, Scope, post, web}; +use actix_web::{get, post, web, Error, HttpRequest, HttpResponse, Scope}; use serde::Deserialize; mod uuid; -use crate::{Data, api::v1::auth::check_access_token, structs::Guild, utils::get_auth_header}; +use crate::{api::v1::auth::check_access_token, structs::{Guild, StartAmountQuery}, utils::get_auth_header, Data}; #[derive(Deserialize)] struct GuildInfo { @@ -12,11 +12,14 @@ struct GuildInfo { } pub fn web() -> Scope { - web::scope("/servers").service(res).service(uuid::web()) + web::scope("/servers") + .service(create) + .service(get) + .service(uuid::web()) } #[post("")] -pub async fn res( +pub async fn create( req: HttpRequest, guild_info: web::Json, data: web::Data, @@ -51,3 +54,37 @@ pub async fn res( Ok(HttpResponse::Ok().json(guild.unwrap())) } + +#[get("")] +pub async fn get( + req: HttpRequest, + request_query: web::Query, + data: web::Data, +) -> Result { + let headers = req.headers(); + + let auth_header = get_auth_header(headers); + + let start = request_query.start.unwrap_or(0); + + let amount = request_query.amount.unwrap_or(10); + + if let Err(error) = auth_header { + return Ok(error); + } + + let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; + + if let Err(error) = authorized { + return Ok(error); + } + + let guilds = Guild::fetch_amount(&data.pool, start, amount).await; + + if let Err(error) = guilds { + return Ok(error); + } + + Ok(HttpResponse::Ok().json(guilds.unwrap())) +} + diff --git a/src/api/v1/users/mod.rs b/src/api/v1/users/mod.rs index 079cb77..d6eb6bd 100644 --- a/src/api/v1/users/mod.rs +++ b/src/api/v1/users/mod.rs @@ -1,18 +1,12 @@ -use crate::{Data, api::v1::auth::check_access_token, utils::get_auth_header}; +use crate::{api::v1::auth::check_access_token, structs::StartAmountQuery, utils::get_auth_header, Data}; use actix_web::{Error, HttpRequest, HttpResponse, Scope, get, web}; use log::error; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use sqlx::prelude::FromRow; mod me; mod uuid; -#[derive(Deserialize)] -struct RequestQuery { - start: Option, - amount: Option, -} - #[derive(Serialize, FromRow)] struct Response { uuid: String, @@ -31,7 +25,7 @@ pub fn web() -> Scope { #[get("")] pub async fn res( req: HttpRequest, - request_query: web::Query, + request_query: web::Query, data: web::Data, ) -> Result { let headers = req.headers(); diff --git a/src/structs.rs b/src/structs.rs index 0ef738f..1b339b1 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -329,6 +329,57 @@ impl Guild { }) } + pub async fn fetch_amount( + pool: &Pool, + start: i32, + amount: i32, + ) -> Result, HttpResponse> { + // Fetch guild data from database + let rows = sqlx::query_as::<_, (String, String, String, Option)>( + "SELECT CAST(uuid AS VARCHAR), CAST(owner_uuid AS VARCHAR), name, description + FROM guilds + ORDER BY name + LIMIT $1 OFFSET $2", + ) + .bind(amount) + .bind(start) + .fetch_all(pool) + .await + .map_err(|error| { + error!("{}", error); + HttpResponse::InternalServerError().finish() + })?; + + // Process each guild concurrently + let guild_futures = rows.into_iter().map(|(guild_uuid_raw, owner_uuid_raw, name, description)| async move { + let uuid = Uuid::from_str(&guild_uuid_raw).map_err(|_| { + HttpResponse::BadRequest().body("Invalid guild UUID format") + })?; + + let owner_uuid = Uuid::from_str(&owner_uuid_raw).map_err(|_| { + HttpResponse::BadRequest().body("Invalid owner UUID format") + })?; + + let (member_count, roles) = tokio::try_join!( + Member::count(pool, uuid), + Role::fetch_all(pool, uuid) + )?; + + Ok::(Self { + uuid, + name, + description, + icon: String::from("bogus"), // FIXME: Replace with actual icon handling + owner_uuid, + roles, + member_count, + }) + }); + + // Execute all futures concurrently and collect results + futures::future::try_join_all(guild_futures).await + } + pub async fn new( pool: &Pool, name: String, @@ -710,3 +761,9 @@ impl Invite { Ok(invite.unwrap().build()) } } + +#[derive(Deserialize)] +pub struct StartAmountQuery { + pub start: Option, + pub amount: Option, +}