Merge branch 'main' into wip/invite-deletion

This commit is contained in:
Radical 2025-05-20 22:55:08 +02:00
commit 6eaa8c9956
24 changed files with 656 additions and 289 deletions

View file

@ -10,9 +10,9 @@ codegen-units = 1
[dependencies] [dependencies]
actix-cors = "0.7.1" actix-cors = "0.7.1"
actix-web = "4.10" actix-web = "4.11"
argon2 = { version = "0.5.3", features = ["std"] } argon2 = { version = "0.5.3", features = ["std"] }
clap = { version = "4.5.37", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }
futures = "0.3" futures = "0.3"
getrandom = "0.3" getrandom = "0.3"
hex = "0.4" hex = "0.4"
@ -22,7 +22,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
simple_logger = "5.0.0" simple_logger = "5.0.0"
sqlx = { version = "0.8", features = ["runtime-tokio", "tls-native-tls", "postgres"] } sqlx = { version = "0.8", features = ["runtime-tokio", "tls-native-tls", "postgres"] }
redis = { version = "0.30", features= ["tokio-comp"] } redis = { version = "0.31.0", features= ["tokio-comp"] }
tokio-tungstenite = { version = "0.26", features = ["native-tls", "url"] } tokio-tungstenite = { version = "0.26", features = ["native-tls", "url"] }
toml = "0.8" toml = "0.8"
url = { version = "2.5", features = ["serde"] } url = { version = "2.5", features = ["serde"] }

View file

@ -5,7 +5,5 @@ mod v1;
mod versions; mod versions;
pub fn web() -> Scope { pub fn web() -> Scope {
web::scope("/api") web::scope("/api").service(v1::web()).service(versions::res)
.service(v1::web())
.service(versions::res)
} }

View file

@ -1,12 +1,14 @@
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use actix_web::{post, web, Error, HttpResponse}; use actix_web::{Error, HttpResponse, post, web};
use argon2::{PasswordHash, PasswordVerifier}; use argon2::{PasswordHash, PasswordVerifier};
use log::error; use log::error;
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
api::v1::auth::{EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX}, utils::{generate_access_token, generate_refresh_token, refresh_token_cookie}, Data Data,
api::v1::auth::{EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX},
utils::{generate_access_token, generate_refresh_token, refresh_token_cookie},
}; };
use super::Response; use super::Response;
@ -166,7 +168,7 @@ async fn login(
return HttpResponse::InternalServerError().finish() return HttpResponse::InternalServerError().finish()
} }
HttpResponse::Ok().cookie(refresh_token_cookie(refresh_token)).json(Response { HttpResponse::Ok()
access_token, .cookie(refresh_token_cookie(refresh_token))
}) .json(Response { access_token })
} }

View file

@ -42,11 +42,12 @@ pub async fn check_access_token(
access_token: &str, access_token: &str,
pool: &sqlx::Pool<Postgres>, pool: &sqlx::Pool<Postgres>,
) -> Result<Uuid, HttpResponse> { ) -> Result<Uuid, HttpResponse> {
let row = let row = sqlx::query_as(
sqlx::query_as("SELECT CAST(uuid as VARCHAR), created_at FROM access_tokens WHERE token = $1") "SELECT CAST(uuid as VARCHAR), created_at FROM access_tokens WHERE token = $1",
.bind(&access_token) )
.fetch_one(pool) .bind(access_token)
.await; .fetch_one(pool)
.await;
if let Err(error) = row { if let Err(error) = row {
if error.to_string() if error.to_string()

View file

@ -1,9 +1,10 @@
use actix_web::{post, web, Error, HttpRequest, HttpResponse}; use actix_web::{Error, HttpRequest, HttpResponse, post, web};
use log::error; use log::error;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use crate::{ use crate::{
utils::{generate_access_token, generate_refresh_token, refresh_token_cookie}, Data Data,
utils::{generate_access_token, generate_refresh_token, refresh_token_cookie},
}; };
use super::Response; use super::Response;
@ -12,8 +13,8 @@ use super::Response;
pub async fn res(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn res(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse, Error> {
let recv_refresh_token_cookie = req.cookie("refresh_token"); let recv_refresh_token_cookie = req.cookie("refresh_token");
if let None = recv_refresh_token_cookie { if recv_refresh_token_cookie.is_none() {
return Ok(HttpResponse::Unauthorized().finish()) return Ok(HttpResponse::Unauthorized().finish());
} }
let mut refresh_token = String::from(recv_refresh_token_cookie.unwrap().value()); let mut refresh_token = String::from(recv_refresh_token_cookie.unwrap().value());
@ -23,11 +24,10 @@ pub async fn res(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse
.unwrap() .unwrap()
.as_secs() as i64; .as_secs() as i64;
if let Ok(row) = if let Ok(row) = sqlx::query_scalar("SELECT created_at FROM refresh_tokens WHERE token = $1")
sqlx::query_scalar("SELECT created_at FROM refresh_tokens WHERE token = $1") .bind(&refresh_token)
.bind(&refresh_token) .fetch_one(&data.pool)
.fetch_one(&data.pool) .await
.await
{ {
let created_at: i64 = row; let created_at: i64 = row;
@ -46,7 +46,9 @@ pub async fn res(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse
refresh_token_cookie.make_removal(); refresh_token_cookie.make_removal();
return Ok(HttpResponse::Unauthorized().cookie(refresh_token_cookie).finish()); return Ok(HttpResponse::Unauthorized()
.cookie(refresh_token_cookie)
.finish());
} }
let current_time = SystemTime::now() let current_time = SystemTime::now()
@ -64,12 +66,14 @@ pub async fn res(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse
let new_refresh_token = new_refresh_token.unwrap(); let new_refresh_token = new_refresh_token.unwrap();
match sqlx::query("UPDATE refresh_tokens SET token = $1, created_at = $2 WHERE token = $3") match sqlx::query(
.bind(&new_refresh_token) "UPDATE refresh_tokens SET token = $1, created_at = $2 WHERE token = $3",
.bind(current_time) )
.bind(&refresh_token) .bind(&new_refresh_token)
.execute(&data.pool) .bind(current_time)
.await .bind(&refresh_token)
.execute(&data.pool)
.await
{ {
Ok(_) => { Ok(_) => {
refresh_token = new_refresh_token; refresh_token = new_refresh_token;
@ -89,24 +93,29 @@ pub async fn res(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse
let access_token = access_token.unwrap(); let access_token = access_token.unwrap();
if let Err(error) = sqlx::query("UPDATE access_tokens SET token = $1, created_at = $2 WHERE refresh_token = $3") if let Err(error) = sqlx::query(
.bind(&access_token) "UPDATE access_tokens SET token = $1, created_at = $2 WHERE refresh_token = $3",
.bind(current_time) )
.bind(&refresh_token) .bind(&access_token)
.execute(&data.pool) .bind(current_time)
.await { .bind(&refresh_token)
.execute(&data.pool)
.await
{
error!("{}", error); error!("{}", error);
return Ok(HttpResponse::InternalServerError().finish()) return Ok(HttpResponse::InternalServerError().finish());
} }
return Ok(HttpResponse::Ok().cookie(refresh_token_cookie(refresh_token)).json(Response { return Ok(HttpResponse::Ok()
access_token, .cookie(refresh_token_cookie(refresh_token))
})); .json(Response { access_token }));
} }
let mut refresh_token_cookie = refresh_token_cookie(refresh_token); let mut refresh_token_cookie = refresh_token_cookie(refresh_token);
refresh_token_cookie.make_removal(); refresh_token_cookie.make_removal();
Ok(HttpResponse::Unauthorized().cookie(refresh_token_cookie).finish()) Ok(HttpResponse::Unauthorized()
.cookie(refresh_token_cookie)
.finish())
} }

View file

@ -11,7 +11,9 @@ use uuid::Uuid;
use super::Response; use super::Response;
use crate::{ use crate::{
api::v1::auth::{EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX}, utils::{generate_access_token, generate_refresh_token, refresh_token_cookie}, Data Data,
api::v1::auth::{EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX},
utils::{generate_access_token, generate_refresh_token, refresh_token_cookie},
}; };
#[derive(Deserialize)] #[derive(Deserialize)]
@ -54,7 +56,10 @@ impl Default for ResponseError {
} }
#[post("/register")] #[post("/register")]
pub async fn res(account_information: web::Json<AccountInformation>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn res(
account_information: web::Json<AccountInformation>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let uuid = Uuid::now_v7(); let uuid = Uuid::now_v7();
if !EMAIL_REGEX.is_match(&account_information.email) { if !EMAIL_REGEX.is_match(&account_information.email) {
@ -142,9 +147,9 @@ pub async fn res(account_information: web::Json<AccountInformation>, data: web::
return Ok(HttpResponse::InternalServerError().finish()) return Ok(HttpResponse::InternalServerError().finish())
} }
HttpResponse::Ok().cookie(refresh_token_cookie(refresh_token)).json(Response { HttpResponse::Ok()
access_token, .cookie(refresh_token_cookie(refresh_token))
}) .json(Response { access_token })
} }
Err(error) => { Err(error) => {
let err_msg = error.as_database_error().unwrap().message(); let err_msg = error.as_database_error().unwrap().message();

View file

@ -1,6 +1,6 @@
use actix_web::{Error, HttpRequest, HttpResponse, post, web}; use actix_web::{Error, HttpRequest, HttpResponse, post, web};
use argon2::{PasswordHash, PasswordVerifier}; use argon2::{PasswordHash, PasswordVerifier};
use futures::{future}; use futures::future;
use log::error; use log::error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -105,10 +105,7 @@ pub async fn res(
let results = future::join_all(refresh_tokens_delete).await; let results = future::join_all(refresh_tokens_delete).await;
let errors: Vec<&Result<sqlx::postgres::PgQueryResult, sqlx::Error>> = let errors: Vec<&Result<sqlx::postgres::PgQueryResult, sqlx::Error>> =
results results.iter().filter(|r| r.is_err()).collect();
.iter()
.filter(|r| r.is_err())
.collect();
if !errors.is_empty() { if !errors.is_empty() {
error!("{:?}", errors); error!("{:?}", errors);

View file

@ -1,15 +1,24 @@
use actix_web::{get, post, web, Error, HttpRequest, HttpResponse}; use actix_web::{Error, HttpRequest, HttpResponse, get, post, web};
use crate::{api::v1::auth::check_access_token, structs::{Guild, Invite, Member}, utils::get_auth_header, Data}; use crate::{
Data,
api::v1::auth::check_access_token,
structs::{Guild, Invite, Member},
utils::get_auth_header,
};
#[get("{id}")] #[get("{id}")]
pub async fn get(req: HttpRequest, path: web::Path<(String,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn get(
req: HttpRequest,
path: web::Path<(String,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let invite_id = path.into_inner().0; let invite_id = path.into_inner().0;
@ -17,7 +26,7 @@ pub async fn get(req: HttpRequest, path: web::Path<(String,)>, data: web::Data<D
let result = Invite::fetch_one(&data.pool, invite_id).await; let result = Invite::fetch_one(&data.pool, invite_id).await;
if let Err(error) = result { if let Err(error) = result {
return Ok(error) return Ok(error);
} }
let invite = result.unwrap(); let invite = result.unwrap();
@ -34,13 +43,17 @@ pub async fn get(req: HttpRequest, path: web::Path<(String,)>, data: web::Data<D
} }
#[post("{id}")] #[post("{id}")]
pub async fn join(req: HttpRequest, path: web::Path<(String,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn join(
req: HttpRequest,
path: web::Path<(String,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let invite_id = path.into_inner().0; let invite_id = path.into_inner().0;
@ -48,7 +61,7 @@ pub async fn join(req: HttpRequest, path: web::Path<(String,)>, data: web::Data<
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -56,7 +69,7 @@ pub async fn join(req: HttpRequest, path: web::Path<(String,)>, data: web::Data<
let result = Invite::fetch_one(&data.pool, invite_id).await; let result = Invite::fetch_one(&data.pool, invite_id).await;
if let Err(error) = result { if let Err(error) = result {
return Ok(error) return Ok(error);
} }
let invite = result.unwrap(); let invite = result.unwrap();
@ -70,7 +83,7 @@ pub async fn join(req: HttpRequest, path: web::Path<(String,)>, data: web::Data<
let guild = guild_result.unwrap(); let guild = guild_result.unwrap();
let member = Member::new(&data.pool, uuid, guild.uuid).await; let member = Member::new(&data.pool, uuid, guild.uuid).await;
if let Err(error) = member { if let Err(error) = member {
return Ok(error); return Ok(error);
} }

View file

@ -1,9 +1,7 @@
use actix_web::{web, Scope}; use actix_web::{Scope, web};
mod id; mod id;
pub fn web() -> Scope { pub fn web() -> Scope {
web::scope("/invites") web::scope("/invites").service(id::get).service(id::join)
.service(id::get)
.service(id::join)
} }

View file

@ -1,10 +1,10 @@
use actix_web::{Scope, web}; use actix_web::{Scope, web};
mod auth; mod auth;
mod invites;
mod servers;
mod stats; mod stats;
mod users; mod users;
mod servers;
mod invites;
pub fn web() -> Scope { pub fn web() -> Scope {
web::scope("/v1") web::scope("/v1")

View file

@ -1,9 +1,9 @@
use actix_web::{post, web, Error, HttpRequest, HttpResponse, Scope}; use actix_web::{get, post, web, Error, HttpRequest, HttpResponse, Scope};
use serde::Deserialize; use serde::Deserialize;
mod uuid; mod uuid;
use crate::{api::v1::auth::check_access_token, structs::Guild, utils::get_auth_header, Data}; use crate::{api::v1::auth::check_access_token, structs::{Guild, StartAmountQuery}, utils::get_auth_header, Data};
#[derive(Deserialize)] #[derive(Deserialize)]
struct GuildInfo { struct GuildInfo {
@ -13,34 +13,78 @@ struct GuildInfo {
pub fn web() -> Scope { pub fn web() -> Scope {
web::scope("/servers") web::scope("/servers")
.service(res) .service(create)
.service(get)
.service(uuid::web()) .service(uuid::web())
} }
#[post("")] #[post("")]
pub async fn res(req: HttpRequest, guild_info: web::Json<GuildInfo>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn create(
req: HttpRequest,
guild_info: web::Json<GuildInfo>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
let guild = Guild::new(&data.pool, guild_info.name.clone(), guild_info.description.clone(), uuid).await; let guild = Guild::new(
&data.pool,
guild_info.name.clone(),
guild_info.description.clone(),
uuid,
)
.await;
if let Err(error) = guild { if let Err(error) = guild {
return Ok(error) return Ok(error);
} }
Ok(HttpResponse::Ok().json(guild.unwrap())) Ok(HttpResponse::Ok().json(guild.unwrap()))
} }
#[get("")]
pub async fn get(
req: HttpRequest,
request_query: web::Query<StartAmountQuery>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
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()))
}

View file

@ -1,25 +1,34 @@
use actix_web::{get, post, web, Error, HttpRequest, HttpResponse}; use crate::{
use serde::Deserialize; Data,
use crate::{api::v1::auth::check_access_token, structs::{Channel, Member}, utils::get_auth_header, Data}; api::v1::auth::check_access_token,
structs::{Channel, Member},
utils::get_auth_header,
};
use ::uuid::Uuid; use ::uuid::Uuid;
use actix_web::{Error, HttpRequest, HttpResponse, get, post, web};
use log::error; use log::error;
use serde::Deserialize;
pub mod uuid; pub mod uuid;
#[derive(Deserialize)] #[derive(Deserialize)]
struct ChannelInfo { struct ChannelInfo {
name: String, name: String,
description: Option<String> description: Option<String>,
} }
#[get("{uuid}/channels")] #[get("{uuid}/channels")]
pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn get(
req: HttpRequest,
path: web::Path<(Uuid,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let guild_uuid = path.into_inner().0; let guild_uuid = path.into_inner().0;
@ -27,7 +36,7 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -41,18 +50,22 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
let cache_result = data.get_cache_key(format!("{}_channels", guild_uuid)).await; let cache_result = data.get_cache_key(format!("{}_channels", guild_uuid)).await;
if let Ok(cache_hit) = cache_result { if let Ok(cache_hit) = cache_result {
return Ok(HttpResponse::Ok().content_type("application/json").body(cache_hit)) return Ok(HttpResponse::Ok()
.content_type("application/json")
.body(cache_hit));
} }
let channels_result = Channel::fetch_all(&data.pool, guild_uuid).await; let channels_result = Channel::fetch_all(&data.pool, guild_uuid).await;
if let Err(error) = channels_result { if let Err(error) = channels_result {
return Ok(error) return Ok(error);
} }
let channels = channels_result.unwrap(); let channels = channels_result.unwrap();
let cache_result = data.set_cache_key(format!("{}_channels", guild_uuid), channels.clone(), 1800).await; let cache_result = data
.set_cache_key(format!("{}_channels", guild_uuid), channels.clone(), 1800)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);
@ -63,13 +76,18 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
} }
#[post("{uuid}/channels")] #[post("{uuid}/channels")]
pub async fn create(req: HttpRequest, channel_info: web::Json<ChannelInfo>, path: web::Path<(Uuid,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn create(
req: HttpRequest,
channel_info: web::Json<ChannelInfo>,
path: web::Path<(Uuid,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let guild_uuid = path.into_inner().0; let guild_uuid = path.into_inner().0;
@ -77,7 +95,7 @@ pub async fn create(req: HttpRequest, channel_info: web::Json<ChannelInfo>, path
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -90,7 +108,13 @@ pub async fn create(req: HttpRequest, channel_info: web::Json<ChannelInfo>, path
// FIXME: Logic to check permissions, should probably be done in utils.rs // FIXME: Logic to check permissions, should probably be done in utils.rs
let channel = Channel::new(data.clone(), guild_uuid, channel_info.name.clone(), channel_info.description.clone()).await; let channel = Channel::new(
data.clone(),
guild_uuid,
channel_info.name.clone(),
channel_info.description.clone(),
)
.await;
if let Err(error) = channel { if let Err(error) = channel {
return Ok(error); return Ok(error);

View file

@ -1,8 +1,13 @@
use actix_web::{get, web, Error, HttpRequest, HttpResponse}; use crate::{
use serde::Deserialize; Data,
use crate::{api::v1::auth::check_access_token, structs::{Channel, Member}, utils::get_auth_header, Data}; api::v1::auth::check_access_token,
structs::{Channel, Member},
utils::get_auth_header,
};
use ::uuid::Uuid; use ::uuid::Uuid;
use actix_web::{Error, HttpRequest, HttpResponse, get, web};
use log::error; use log::error;
use serde::Deserialize;
#[derive(Deserialize)] #[derive(Deserialize)]
struct MessageRequest { struct MessageRequest {
@ -11,13 +16,18 @@ struct MessageRequest {
} }
#[get("{uuid}/channels/{channel_uuid}/messages")] #[get("{uuid}/channels/{channel_uuid}/messages")]
pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, message_request: web::Query<MessageRequest>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn get(
req: HttpRequest,
path: web::Path<(Uuid, Uuid)>,
message_request: web::Query<MessageRequest>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let (guild_uuid, channel_uuid) = path.into_inner(); let (guild_uuid, channel_uuid) = path.into_inner();
@ -25,7 +35,7 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, message_reques
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -46,23 +56,27 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, message_reques
let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await; let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await;
if let Err(error) = channel_result { if let Err(error) = channel_result {
return Ok(error) return Ok(error);
} }
channel = channel_result.unwrap(); channel = channel_result.unwrap();
let cache_result = data.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60).await; let cache_result = data
.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);
return Ok(HttpResponse::InternalServerError().finish()); return Ok(HttpResponse::InternalServerError().finish());
} }
} }
let messages = channel.fetch_messages(&data.pool, message_request.amount, message_request.offset).await; let messages = channel
.fetch_messages(&data.pool, message_request.amount, message_request.offset)
.await;
if let Err(error) = messages { if let Err(error) = messages {
return Ok(error) return Ok(error);
} }
Ok(HttpResponse::Ok().json(messages.unwrap())) Ok(HttpResponse::Ok().json(messages.unwrap()))

View file

@ -1,19 +1,28 @@
pub mod messages; pub mod messages;
pub mod socket; pub mod socket;
use actix_web::{delete, get, web, Error, HttpRequest, HttpResponse}; use crate::{
use crate::{api::v1::auth::check_access_token, structs::{Channel, Member}, utils::get_auth_header, Data}; Data,
api::v1::auth::check_access_token,
structs::{Channel, Member},
utils::get_auth_header,
};
use ::uuid::Uuid; use ::uuid::Uuid;
use actix_web::{Error, HttpRequest, HttpResponse, delete, get, web};
use log::error; use log::error;
#[get("{uuid}/channels/{channel_uuid}")] #[get("{uuid}/channels/{channel_uuid}")]
pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn get(
req: HttpRequest,
path: web::Path<(Uuid, Uuid)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let (guild_uuid, channel_uuid) = path.into_inner(); let (guild_uuid, channel_uuid) = path.into_inner();
@ -21,7 +30,7 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Dat
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -35,18 +44,22 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Dat
let cache_result = data.get_cache_key(format!("{}", channel_uuid)).await; let cache_result = data.get_cache_key(format!("{}", channel_uuid)).await;
if let Ok(cache_hit) = cache_result { if let Ok(cache_hit) = cache_result {
return Ok(HttpResponse::Ok().content_type("application/json").body(cache_hit)) return Ok(HttpResponse::Ok()
.content_type("application/json")
.body(cache_hit));
} }
let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await; let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await;
if let Err(error) = channel_result { if let Err(error) = channel_result {
return Ok(error) return Ok(error);
} }
let channel = channel_result.unwrap(); let channel = channel_result.unwrap();
let cache_result = data.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60).await; let cache_result = data
.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);
@ -57,13 +70,17 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Dat
} }
#[delete("{uuid}/channels/{channel_uuid}")] #[delete("{uuid}/channels/{channel_uuid}")]
pub async fn delete(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn delete(
req: HttpRequest,
path: web::Path<(Uuid, Uuid)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let (guild_uuid, channel_uuid) = path.into_inner(); let (guild_uuid, channel_uuid) = path.into_inner();
@ -71,7 +88,7 @@ pub async fn delete(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -98,7 +115,7 @@ pub async fn delete(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::
let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await; let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await;
if let Err(error) = channel_result { if let Err(error) = channel_result {
return Ok(error) return Ok(error);
} }
channel = channel_result.unwrap(); channel = channel_result.unwrap();
@ -107,9 +124,8 @@ pub async fn delete(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::
let delete_result = channel.delete(&data.pool).await; let delete_result = channel.delete(&data.pool).await;
if let Err(error) = delete_result { if let Err(error) = delete_result {
return Ok(error) return Ok(error);
} }
Ok(HttpResponse::Ok().finish()) Ok(HttpResponse::Ok().finish())
} }

View file

@ -1,13 +1,23 @@
use actix_web::{get, rt, web, Error, HttpRequest, HttpResponse}; use actix_web::{Error, HttpRequest, HttpResponse, get, rt, web};
use actix_ws::AggregatedMessage; use actix_ws::AggregatedMessage;
use futures_util::StreamExt as _; use futures_util::StreamExt as _;
use uuid::Uuid;
use log::error; use log::error;
use uuid::Uuid;
use crate::{api::v1::auth::check_access_token, structs::{Channel, Member}, utils::get_auth_header, Data}; use crate::{
Data,
api::v1::auth::check_access_token,
structs::{Channel, Member},
utils::get_auth_header,
};
#[get("{uuid}/channels/{channel_uuid}/socket")] #[get("{uuid}/channels/{channel_uuid}/socket")]
pub async fn echo(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, stream: web::Payload, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn echo(
req: HttpRequest,
path: web::Path<(Uuid, Uuid)>,
stream: web::Payload,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
// Get all headers // Get all headers
let headers = req.headers(); let headers = req.headers();
@ -15,7 +25,7 @@ pub async fn echo(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, stream: web::
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
// Get uuids from path // Get uuids from path
@ -25,7 +35,7 @@ pub async fn echo(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, stream: web::
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
// Unwrap user uuid from authorization // Unwrap user uuid from authorization
@ -50,13 +60,15 @@ pub async fn echo(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, stream: web::
let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await; let channel_result = Channel::fetch_one(&data.pool, guild_uuid, channel_uuid).await;
if let Err(error) = channel_result { if let Err(error) = channel_result {
return Ok(error) return Ok(error);
} }
channel = channel_result.unwrap(); channel = channel_result.unwrap();
let cache_result = data.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60).await; let cache_result = data
.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);
return Ok(HttpResponse::InternalServerError().finish()); return Ok(HttpResponse::InternalServerError().finish());
@ -74,7 +86,7 @@ pub async fn echo(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, stream: web::
if let Err(error) = pubsub_result { if let Err(error) = pubsub_result {
error!("{}", error); error!("{}", error);
return Ok(HttpResponse::InternalServerError().finish()) return Ok(HttpResponse::InternalServerError().finish());
} }
let mut session_2 = session_1.clone(); let mut session_2 = session_1.clone();
@ -90,14 +102,25 @@ pub async fn echo(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, stream: web::
// start task but don't wait for it // start task but don't wait for it
rt::spawn(async move { rt::spawn(async move {
let mut conn = data.cache_pool.get_multiplexed_tokio_connection().await.unwrap(); let mut conn = data
.cache_pool
.get_multiplexed_tokio_connection()
.await
.unwrap();
// receive messages from websocket // receive messages from websocket
while let Some(msg) = stream.next().await { while let Some(msg) = stream.next().await {
match msg { match msg {
Ok(AggregatedMessage::Text(text)) => { Ok(AggregatedMessage::Text(text)) => {
// echo text message // echo text message
redis::cmd("PUBLISH").arg(&[channel_uuid.to_string(), text.to_string()]).exec_async(&mut conn).await.unwrap(); redis::cmd("PUBLISH")
channel.new_message(&data.pool, uuid, text.to_string()).await.unwrap(); .arg(&[channel_uuid.to_string(), text.to_string()])
.exec_async(&mut conn)
.await
.unwrap();
channel
.new_message(&data.pool, uuid, text.to_string())
.await
.unwrap();
} }
Ok(AggregatedMessage::Binary(bin)) => { Ok(AggregatedMessage::Binary(bin)) => {

View file

@ -1,10 +1,15 @@
use actix_web::{get, post, web, Error, HttpRequest, HttpResponse}; use actix_web::{Error, HttpRequest, HttpResponse, get, post, web};
use serde::Deserialize; use serde::Deserialize;
use uuid::Uuid; use uuid::Uuid;
mod id; mod id;
use crate::{api::v1::auth::check_access_token, structs::{Guild, Member}, utils::get_auth_header, Data}; use crate::{
Data,
api::v1::auth::check_access_token,
structs::{Guild, Member},
utils::get_auth_header,
};
#[derive(Deserialize)] #[derive(Deserialize)]
struct InviteRequest { struct InviteRequest {
@ -12,13 +17,17 @@ struct InviteRequest {
} }
#[get("{uuid}/invites")] #[get("{uuid}/invites")]
pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn get(
req: HttpRequest,
path: web::Path<(Uuid,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let guild_uuid = path.into_inner().0; let guild_uuid = path.into_inner().0;
@ -26,7 +35,7 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -49,19 +58,24 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
if let Err(error) = invites { if let Err(error) = invites {
return Ok(error); return Ok(error);
} }
Ok(HttpResponse::Ok().json(invites.unwrap())) Ok(HttpResponse::Ok().json(invites.unwrap()))
} }
#[post("{uuid}/invites")] #[post("{uuid}/invites")]
pub async fn create(req: HttpRequest, path: web::Path<(Uuid,)>, invite_request: web::Json<Option<InviteRequest>>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn create(
req: HttpRequest,
path: web::Path<(Uuid,)>,
invite_request: web::Json<Option<InviteRequest>>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let guild_uuid = path.into_inner().0; let guild_uuid = path.into_inner().0;
@ -69,7 +83,7 @@ pub async fn create(req: HttpRequest, path: web::Path<(Uuid,)>, invite_request:
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -90,7 +104,7 @@ pub async fn create(req: HttpRequest, path: web::Path<(Uuid,)>, invite_request:
let guild = guild_result.unwrap(); let guild = guild_result.unwrap();
let custom_id = invite_request.as_ref().and_then(|ir| Some(ir.custom_id.clone())); let custom_id = invite_request.as_ref().map(|ir| ir.custom_id.clone());
let invite = guild.create_invite(&data.pool, &member, custom_id).await; let invite = guild.create_invite(&data.pool, &member, custom_id).await;

View file

@ -1,11 +1,16 @@
use actix_web::{get, web, Error, HttpRequest, HttpResponse, Scope}; use actix_web::{Error, HttpRequest, HttpResponse, Scope, get, web};
use uuid::Uuid; use uuid::Uuid;
mod channels; mod channels;
mod roles;
mod invites; mod invites;
mod roles;
use crate::{api::v1::auth::check_access_token, structs::{Guild, Member}, utils::get_auth_header, Data}; use crate::{
Data,
api::v1::auth::check_access_token,
structs::{Guild, Member},
utils::get_auth_header,
};
pub fn web() -> Scope { pub fn web() -> Scope {
web::scope("") web::scope("")
@ -28,13 +33,17 @@ pub fn web() -> Scope {
} }
#[get("/{uuid}")] #[get("/{uuid}")]
pub async fn res(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn res(
req: HttpRequest,
path: web::Path<(Uuid,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let guild_uuid = path.into_inner().0; let guild_uuid = path.into_inner().0;
@ -42,7 +51,7 @@ pub async fn res(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -61,4 +70,3 @@ pub async fn res(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
Ok(HttpResponse::Ok().json(guild.unwrap())) Ok(HttpResponse::Ok().json(guild.unwrap()))
} }

View file

@ -1,8 +1,13 @@
use actix_web::{get, post, web, Error, HttpRequest, HttpResponse}; use crate::{
use serde::Deserialize; Data,
use crate::{api::v1::auth::check_access_token, structs::{Member, Role}, utils::get_auth_header, Data}; api::v1::auth::check_access_token,
structs::{Member, Role},
utils::get_auth_header,
};
use ::uuid::Uuid; use ::uuid::Uuid;
use actix_web::{Error, HttpRequest, HttpResponse, get, post, web};
use log::error; use log::error;
use serde::Deserialize;
pub mod uuid; pub mod uuid;
@ -12,13 +17,17 @@ struct RoleInfo {
} }
#[get("{uuid}/roles")] #[get("{uuid}/roles")]
pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn get(
req: HttpRequest,
path: web::Path<(Uuid,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let guild_uuid = path.into_inner().0; let guild_uuid = path.into_inner().0;
@ -26,7 +35,7 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -40,18 +49,22 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
let cache_result = data.get_cache_key(format!("{}_roles", guild_uuid)).await; let cache_result = data.get_cache_key(format!("{}_roles", guild_uuid)).await;
if let Ok(cache_hit) = cache_result { if let Ok(cache_hit) = cache_result {
return Ok(HttpResponse::Ok().content_type("application/json").body(cache_hit)) return Ok(HttpResponse::Ok()
.content_type("application/json")
.body(cache_hit));
} }
let roles_result = Role::fetch_all(&data.pool, guild_uuid).await; let roles_result = Role::fetch_all(&data.pool, guild_uuid).await;
if let Err(error) = roles_result { if let Err(error) = roles_result {
return Ok(error) return Ok(error);
} }
let roles = roles_result.unwrap(); let roles = roles_result.unwrap();
let cache_result = data.set_cache_key(format!("{}_roles", guild_uuid), roles.clone(), 1800).await; let cache_result = data
.set_cache_key(format!("{}_roles", guild_uuid), roles.clone(), 1800)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);
@ -62,13 +75,18 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Dat
} }
#[post("{uuid}/roles")] #[post("{uuid}/roles")]
pub async fn create(req: HttpRequest, role_info: web::Json<RoleInfo>, path: web::Path<(Uuid,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn create(
req: HttpRequest,
role_info: web::Json<RoleInfo>,
path: web::Path<(Uuid,)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let guild_uuid = path.into_inner().0; let guild_uuid = path.into_inner().0;
@ -76,7 +94,7 @@ pub async fn create(req: HttpRequest, role_info: web::Json<RoleInfo>, path: web:
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();

View file

@ -1,16 +1,25 @@
use actix_web::{get, web, Error, HttpRequest, HttpResponse}; use crate::{
use crate::{api::v1::auth::check_access_token, structs::{Member, Role}, utils::get_auth_header, Data}; Data,
api::v1::auth::check_access_token,
structs::{Member, Role},
utils::get_auth_header,
};
use ::uuid::Uuid; use ::uuid::Uuid;
use actix_web::{Error, HttpRequest, HttpResponse, get, web};
use log::error; use log::error;
#[get("{uuid}/roles/{role_uuid}")] #[get("{uuid}/roles/{role_uuid}")]
pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Data<Data>) -> Result<HttpResponse, Error> { pub async fn get(
req: HttpRequest,
path: web::Path<(Uuid, Uuid)>,
data: web::Data<Data>,
) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();
let auth_header = get_auth_header(headers); let auth_header = get_auth_header(headers);
if let Err(error) = auth_header { if let Err(error) = auth_header {
return Ok(error) return Ok(error);
} }
let (guild_uuid, role_uuid) = path.into_inner(); let (guild_uuid, role_uuid) = path.into_inner();
@ -18,7 +27,7 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Dat
let authorized = check_access_token(auth_header.unwrap(), &data.pool).await; let authorized = check_access_token(auth_header.unwrap(), &data.pool).await;
if let Err(error) = authorized { if let Err(error) = authorized {
return Ok(error) return Ok(error);
} }
let uuid = authorized.unwrap(); let uuid = authorized.unwrap();
@ -32,18 +41,22 @@ pub async fn get(req: HttpRequest, path: web::Path<(Uuid, Uuid)>, data: web::Dat
let cache_result = data.get_cache_key(format!("{}", role_uuid)).await; let cache_result = data.get_cache_key(format!("{}", role_uuid)).await;
if let Ok(cache_hit) = cache_result { if let Ok(cache_hit) = cache_result {
return Ok(HttpResponse::Ok().content_type("application/json").body(cache_hit)) return Ok(HttpResponse::Ok()
.content_type("application/json")
.body(cache_hit));
} }
let role_result = Role::fetch_one(&data.pool, guild_uuid, role_uuid).await; let role_result = Role::fetch_one(&data.pool, guild_uuid, role_uuid).await;
if let Err(error) = role_result { if let Err(error) = role_result {
return Ok(error) return Ok(error);
} }
let role = role_result.unwrap(); let role = role_result.unwrap();
let cache_result = data.set_cache_key(format!("{}", role_uuid), role.clone(), 60).await; let cache_result = data
.set_cache_key(format!("{}", role_uuid), role.clone(), 60)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);

View file

@ -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::{get, web, Error, HttpRequest, HttpResponse, Scope}; use actix_web::{Error, HttpRequest, HttpResponse, Scope, get, web};
use log::error; use log::error;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use sqlx::prelude::FromRow; use sqlx::prelude::FromRow;
mod me; mod me;
mod uuid; mod uuid;
#[derive(Deserialize)]
struct RequestQuery {
start: Option<i32>,
amount: Option<i32>,
}
#[derive(Serialize, FromRow)] #[derive(Serialize, FromRow)]
struct Response { struct Response {
uuid: String, uuid: String,
@ -31,7 +25,7 @@ pub fn web() -> Scope {
#[get("")] #[get("")]
pub async fn res( pub async fn res(
req: HttpRequest, req: HttpRequest,
request_query: web::Query<RequestQuery>, request_query: web::Query<StartAmountQuery>,
data: web::Data<Data>, data: web::Data<Data>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let headers = req.headers(); let headers = req.headers();

View file

@ -37,7 +37,9 @@ pub async fn res(
let cache_result = data.get_cache_key(uuid.to_string()).await; let cache_result = data.get_cache_key(uuid.to_string()).await;
if let Ok(cache_hit) = cache_result { if let Ok(cache_hit) = cache_result {
return Ok(HttpResponse::Ok().content_type("application/json").body(cache_hit)) return Ok(HttpResponse::Ok()
.content_type("application/json")
.body(cache_hit));
} }
let row = sqlx::query_as(&format!( let row = sqlx::query_as(&format!(
@ -60,7 +62,9 @@ pub async fn res(
display_name: display_name.unwrap_or_default(), display_name: display_name.unwrap_or_default(),
}; };
let cache_result = data.set_cache_key(uuid.to_string(), user.clone(), 1800).await; let cache_result = data
.set_cache_key(uuid.to_string(), user.clone(), 1800)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);

View file

@ -1,5 +1,5 @@
use actix_cors::Cors; use actix_cors::Cors;
use actix_web::{web, App, HttpServer}; use actix_web::{App, HttpServer, web};
use argon2::Argon2; use argon2::Argon2;
use clap::Parser; use clap::Parser;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
@ -9,8 +9,8 @@ mod config;
use config::{Config, ConfigBuilder}; use config::{Config, ConfigBuilder};
mod api; mod api;
pub mod utils;
pub mod structs; pub mod structs;
pub mod utils;
type Error = Box<dyn std::error::Error>; type Error = Box<dyn std::error::Error>;
@ -169,7 +169,7 @@ async fn main() -> Result<(), Error> {
argon2: Argon2::default(), argon2: Argon2::default(),
start_time: SystemTime::now(), start_time: SystemTime::now(),
}; };
HttpServer::new(move || { HttpServer::new(move || {
// Set CORS headers // Set CORS headers
let cors = Cors::default() let cors = Cors::default()
@ -179,9 +179,7 @@ async fn main() -> Result<(), Error> {
rather than setting it to "*" due to CORS not allowing rather than setting it to "*" due to CORS not allowing
sending of credentials (cookies) with wildcard origin. sending of credentials (cookies) with wildcard origin.
*/ */
.allowed_origin_fn(|_origin, _req_head| { .allowed_origin_fn(|_origin, _req_head| true)
true
})
/* /*
Allows any request method in CORS preflight requests. Allows any request method in CORS preflight requests.
This will be restricted to only ones actually in use later. This will be restricted to only ones actually in use later.

View file

@ -1,10 +1,10 @@
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize};
use sqlx::{prelude::FromRow, Pool, Postgres};
use uuid::Uuid;
use actix_web::HttpResponse; use actix_web::HttpResponse;
use log::error; use log::error;
use serde::{Deserialize, Serialize};
use sqlx::{Pool, Postgres, prelude::FromRow};
use uuid::Uuid;
use crate::Data; use crate::Data;
@ -14,13 +14,13 @@ pub struct Channel {
pub guild_uuid: Uuid, pub guild_uuid: Uuid,
name: String, name: String,
description: Option<String>, description: Option<String>,
pub permissions: Vec<ChannelPermission> pub permissions: Vec<ChannelPermission>,
} }
#[derive(Serialize, Clone, FromRow)] #[derive(Serialize, Clone, FromRow)]
struct ChannelPermissionBuilder { struct ChannelPermissionBuilder {
role_uuid: String, role_uuid: String,
permissions: i32 permissions: i32,
} }
impl ChannelPermissionBuilder { impl ChannelPermissionBuilder {
@ -35,19 +35,25 @@ impl ChannelPermissionBuilder {
#[derive(Serialize, Deserialize, Clone, FromRow)] #[derive(Serialize, Deserialize, Clone, FromRow)]
pub struct ChannelPermission { pub struct ChannelPermission {
pub role_uuid: Uuid, pub role_uuid: Uuid,
pub permissions: i32 pub permissions: i32,
} }
impl Channel { impl Channel {
pub async fn fetch_all(pool: &Pool<Postgres>, guild_uuid: Uuid) -> Result<Vec<Self>, HttpResponse> { pub async fn fetch_all(
let row = sqlx::query_as(&format!("SELECT CAST(uuid AS VARCHAR), name, description FROM channels WHERE guild_uuid = '{}'", guild_uuid)) pool: &Pool<Postgres>,
.fetch_all(pool) guild_uuid: Uuid,
.await; ) -> Result<Vec<Self>, 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 { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let channels: Vec<(String, String, Option<String>)> = row.unwrap(); let channels: Vec<(String, String, Option<String>)> = row.unwrap();
@ -80,18 +86,25 @@ impl Channel {
let channels: Result<Vec<Channel>, HttpResponse> = channels.into_iter().collect(); let channels: Result<Vec<Channel>, HttpResponse> = channels.into_iter().collect();
Ok(channels?) channels
} }
pub async fn fetch_one(pool: &Pool<Postgres>, guild_uuid: Uuid, channel_uuid: Uuid) -> Result<Self, HttpResponse> { pub async fn fetch_one(
let row = sqlx::query_as(&format!("SELECT name, description FROM channels WHERE guild_uuid = '{}' AND uuid = '{}'", guild_uuid, channel_uuid)) pool: &Pool<Postgres>,
.fetch_one(pool) guild_uuid: Uuid,
.await; channel_uuid: Uuid,
) -> Result<Self, HttpResponse> {
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 { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let (name, description): (String, Option<String>) = row.unwrap(); let (name, description): (String, Option<String>) = row.unwrap();
@ -103,7 +116,7 @@ impl Channel {
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let channel_permission_builders: Vec<ChannelPermissionBuilder> = row.unwrap(); let channel_permission_builders: Vec<ChannelPermissionBuilder> = row.unwrap();
@ -113,11 +126,19 @@ impl Channel {
guild_uuid, guild_uuid,
name, name,
description, description,
permissions: channel_permission_builders.iter().map(|b| b.build()).collect(), permissions: channel_permission_builders
.iter()
.map(|b| b.build())
.collect(),
}) })
} }
pub async fn new(data: actix_web::web::Data<Data>, guild_uuid: Uuid, name: String, description: Option<String>) -> Result<Self, HttpResponse> { pub async fn new(
data: actix_web::web::Data<Data>,
guild_uuid: Uuid,
name: String,
description: Option<String>,
) -> Result<Self, HttpResponse> {
let channel_uuid = Uuid::now_v7(); 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)) let row = sqlx::query(&format!("INSERT INTO channels (uuid, guild_uuid, name, description) VALUES ('{}', '{}', $1, $2)", channel_uuid, guild_uuid))
@ -125,10 +146,10 @@ impl Channel {
.bind(&description) .bind(&description)
.execute(&data.pool) .execute(&data.pool)
.await; .await;
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let channel = Self { let channel = Self {
@ -139,7 +160,9 @@ impl Channel {
permissions: vec![], permissions: vec![],
}; };
let cache_result = data.set_cache_key(channel_uuid.to_string(), channel.clone(), 1800).await; let cache_result = data
.set_cache_key(channel_uuid.to_string(), channel.clone(), 1800)
.await;
if let Err(error) = cache_result { if let Err(error) = cache_result {
error!("{}", error); error!("{}", error);
@ -157,20 +180,28 @@ impl Channel {
} }
pub async fn delete(self, pool: &Pool<Postgres>) -> Result<(), HttpResponse> { pub async fn delete(self, pool: &Pool<Postgres>) -> Result<(), HttpResponse> {
let result = sqlx::query(&format!("DELETE FROM channels WHERE channel_uuid = '{}'", self.uuid)) let result = sqlx::query(&format!(
.execute(pool) "DELETE FROM channels WHERE channel_uuid = '{}'",
.await; self.uuid
))
.execute(pool)
.await;
if let Err(error) = result { if let Err(error) = result {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
Ok(()) Ok(())
} }
pub async fn fetch_messages(&self, pool: &Pool<Postgres>, amount: i64, offset: i64) -> Result<Vec<Message>, HttpResponse> { pub async fn fetch_messages(
&self,
pool: &Pool<Postgres>,
amount: i64,
offset: i64,
) -> Result<Vec<Message>, HttpResponse> {
let row = sqlx::query_as(&format!("SELECT CAST(uuid AS VARCHAR), CAST(user_uuid AS VARCHAR), CAST(channel_uuid AS VARCHAR), message FROM messages WHERE channel_uuid = '{}' ORDER BY uuid DESC LIMIT $1 OFFSET $2", self.uuid)) let row = sqlx::query_as(&format!("SELECT CAST(uuid AS VARCHAR), CAST(user_uuid AS VARCHAR), CAST(channel_uuid AS VARCHAR), message FROM messages WHERE channel_uuid = '{}' ORDER BY uuid DESC LIMIT $1 OFFSET $2", self.uuid))
.bind(amount) .bind(amount)
.bind(offset) .bind(offset)
@ -187,14 +218,19 @@ impl Channel {
Ok(message_builders.iter().map(|b| b.build()).collect()) Ok(message_builders.iter().map(|b| b.build()).collect())
} }
pub async fn new_message(&self, pool: &Pool<Postgres>, user_uuid: Uuid, message: String) -> Result<Message, HttpResponse> { pub async fn new_message(
&self,
pool: &Pool<Postgres>,
user_uuid: Uuid,
message: String,
) -> Result<Message, HttpResponse> {
let message_uuid = Uuid::now_v7(); let message_uuid = Uuid::now_v7();
let row = sqlx::query(&format!("INSERT INTO messages (uuid, channel_uuid, user_uuid, message) VALUES ('{}', '{}', '{}', $1)", message_uuid, self.uuid, user_uuid)) let row = sqlx::query(&format!("INSERT INTO messages (uuid, channel_uuid, user_uuid, message) VALUES ('{}', '{}', '{}', $1)", message_uuid, self.uuid, user_uuid))
.bind(&message) .bind(&message)
.execute(pool) .execute(pool)
.await; .await;
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()); return Err(HttpResponse::InternalServerError().finish());
@ -240,7 +276,8 @@ impl Permissions {
Self::ManageMember, Self::ManageMember,
]; ];
all_perms.into_iter() all_perms
.into_iter()
.filter(|p| permissions & (*p as i64) != 0) .filter(|p| permissions & (*p as i64) != 0)
.collect() .collect()
} }
@ -259,14 +296,17 @@ pub struct Guild {
impl Guild { impl Guild {
pub async fn fetch_one(pool: &Pool<Postgres>, guild_uuid: Uuid) -> Result<Self, HttpResponse> { pub async fn fetch_one(pool: &Pool<Postgres>, guild_uuid: Uuid) -> Result<Self, HttpResponse> {
let row = sqlx::query_as(&format!("SELECT CAST(owner_uuid AS VARCHAR), name, description FROM guilds WHERE uuid = '{}'", guild_uuid)) let row = sqlx::query_as(&format!(
.fetch_one(pool) "SELECT CAST(owner_uuid AS VARCHAR), name, description FROM guilds WHERE uuid = '{}'",
.await; guild_uuid
))
.fetch_one(pool)
.await;
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let (owner_uuid_raw, name, description): (String, String, Option<String>) = row.unwrap(); let (owner_uuid_raw, name, description): (String, String, Option<String>) = row.unwrap();
@ -289,24 +329,88 @@ impl Guild {
}) })
} }
pub async fn new(pool: &Pool<Postgres>, name: String, description: Option<String>, owner_uuid: Uuid) -> Result<Self, HttpResponse> { pub async fn fetch_amount(
pool: &Pool<Postgres>,
start: i32,
amount: i32,
) -> Result<Vec<Self>, HttpResponse> {
// Fetch guild data from database
let rows = sqlx::query_as::<_, (String, String, String, Option<String>)>(
"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::<Guild, HttpResponse>(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<Postgres>,
name: String,
description: Option<String>,
owner_uuid: Uuid,
) -> Result<Self, HttpResponse> {
let guild_uuid = Uuid::now_v7(); 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)) let row = sqlx::query(&format!(
.bind(&name) "INSERT INTO guilds (uuid, owner_uuid, name, description) VALUES ('{}', '{}', $1, $2)",
.bind(&description) guild_uuid, owner_uuid
.execute(pool) ))
.await; .bind(&name)
.bind(&description)
.execute(pool)
.await;
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) 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)) let row = sqlx::query(&format!(
.execute(pool) "INSERT INTO guild_members (uuid, guild_uuid, user_uuid) VALUES ('{}', '{}', '{}')",
.await; Uuid::now_v7(),
guild_uuid,
owner_uuid
))
.execute(pool)
.await;
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
@ -318,7 +422,7 @@ impl Guild {
error!("{}", error); error!("{}", error);
} }
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
Ok(Guild { Ok(Guild {
@ -328,26 +432,38 @@ impl Guild {
icon: "bogus".to_string(), icon: "bogus".to_string(),
owner_uuid, owner_uuid,
roles: vec![], roles: vec![],
member_count: 1 member_count: 1,
}) })
} }
pub async fn get_invites(&self, pool: &Pool<Postgres>) -> Result<Vec<Invite>, HttpResponse> { pub async fn get_invites(&self, pool: &Pool<Postgres>) -> Result<Vec<Invite>, HttpResponse> {
let invites = sqlx::query_as(&format!("SELECT (id, guild_uuid, user_uuid) FROM invites WHERE guild_uuid = '{}'", self.uuid)) let invites = sqlx::query_as(&format!(
.fetch_all(pool) "SELECT (id, guild_uuid, user_uuid) FROM invites WHERE guild_uuid = '{}'",
.await; self.uuid
))
.fetch_all(pool)
.await;
if let Err(error) = invites { if let Err(error) = invites {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
Ok(invites.unwrap().iter().map(|b: &InviteBuilder| b.build()).collect()) Ok(invites
.unwrap()
.iter()
.map(|b: &InviteBuilder| b.build())
.collect())
} }
pub async fn create_invite(&self, pool: &Pool<Postgres>, member: &Member, custom_id: Option<String>) -> Result<Invite, HttpResponse> { pub async fn create_invite(
&self,
pool: &Pool<Postgres>,
member: &Member,
custom_id: Option<String>,
) -> Result<Invite, HttpResponse> {
let invite_id; let invite_id;
if custom_id.is_none() { if custom_id.is_none() {
let charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; let charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@ -355,18 +471,21 @@ impl Guild {
} else { } else {
invite_id = custom_id.unwrap(); invite_id = custom_id.unwrap();
if invite_id.len() > 32 { if invite_id.len() > 32 {
return Err(HttpResponse::BadRequest().finish()) return Err(HttpResponse::BadRequest().finish());
} }
} }
let result = sqlx::query(&format!("INSERT INTO invites (id, guild_uuid, user_uuid) VALUES ($1, '{}', '{}'", self.uuid, member.user_uuid)) let result = sqlx::query(&format!(
.bind(&invite_id) "INSERT INTO invites (id, guild_uuid, user_uuid) VALUES ($1, '{}', '{}'",
.execute(pool) self.uuid, member.user_uuid
.await; ))
.bind(&invite_id)
.execute(pool)
.await;
if let Err(error) = result { if let Err(error) = result {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
Ok(Invite { Ok(Invite {
@ -384,7 +503,7 @@ struct RoleBuilder {
name: String, name: String,
color: i64, color: i64,
position: i32, position: i32,
permissions: i64, permissions: i64,
} }
impl RoleBuilder { impl RoleBuilder {
@ -411,7 +530,10 @@ pub struct Role {
} }
impl Role { impl Role {
pub async fn fetch_all(pool: &Pool<Postgres>, guild_uuid: Uuid) -> Result<Vec<Self>, HttpResponse> { pub async fn fetch_all(
pool: &Pool<Postgres>,
guild_uuid: Uuid,
) -> Result<Vec<Self>, HttpResponse> {
let role_builders_result = sqlx::query_as(&format!("SELECT (uuid, guild_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) .fetch_all(pool)
.await; .await;
@ -419,7 +541,7 @@ impl Role {
if let Err(error) = role_builders_result { if let Err(error) = role_builders_result {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let role_builders: Vec<RoleBuilder> = role_builders_result.unwrap(); let role_builders: Vec<RoleBuilder> = role_builders_result.unwrap();
@ -427,7 +549,11 @@ impl Role {
Ok(role_builders.iter().map(|b| b.build()).collect()) Ok(role_builders.iter().map(|b| b.build()).collect())
} }
pub async fn fetch_one(pool: &Pool<Postgres>, role_uuid: Uuid, guild_uuid: Uuid) -> Result<Self, HttpResponse> { pub async fn fetch_one(
pool: &Pool<Postgres>,
role_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<Self, HttpResponse> {
let row = sqlx::query_as(&format!("SELECT (name, color, position, permissions) FROM roles WHERE guild_uuid = '{}' AND uuid = '{}'", guild_uuid, role_uuid)) 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) .fetch_one(pool)
.await; .await;
@ -435,7 +561,7 @@ impl Role {
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let (name, color, position, permissions) = row.unwrap(); let (name, color, position, permissions) = row.unwrap();
@ -450,18 +576,25 @@ impl Role {
}) })
} }
pub async fn new(pool: &Pool<Postgres>, guild_uuid: Uuid, name: String) -> Result<Self, HttpResponse> { pub async fn new(
pool: &Pool<Postgres>,
guild_uuid: Uuid,
name: String,
) -> Result<Self, HttpResponse> {
let role_uuid = Uuid::now_v7(); 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)) let row = sqlx::query(&format!(
.bind(&name) "INSERT INTO channels (uuid, guild_uuid, name, position) VALUES ('{}', '{}', $1, $2)",
.bind(0) role_uuid, guild_uuid
.execute(pool) ))
.await; .bind(&name)
.bind(0)
.execute(pool)
.await;
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let role = Self { let role = Self {
@ -486,20 +619,27 @@ pub struct Member {
impl Member { impl Member {
async fn count(pool: &Pool<Postgres>, guild_uuid: Uuid) -> Result<i64, HttpResponse> { async fn count(pool: &Pool<Postgres>, guild_uuid: Uuid) -> Result<i64, HttpResponse> {
let member_count = sqlx::query_scalar(&format!("SELECT COUNT(uuid) FROM guild_members WHERE guild_uuid = '{}'", guild_uuid)) let member_count = sqlx::query_scalar(&format!(
.fetch_one(pool) "SELECT COUNT(uuid) FROM guild_members WHERE guild_uuid = '{}'",
.await; guild_uuid
))
.fetch_one(pool)
.await;
if let Err(error) = member_count { if let Err(error) = member_count {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
Ok(member_count.unwrap()) Ok(member_count.unwrap())
} }
pub async fn fetch_one(pool: &Pool<Postgres>, user_uuid: Uuid, guild_uuid: Uuid) -> Result<Self, HttpResponse> { pub async fn fetch_one(
pool: &Pool<Postgres>,
user_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<Self, HttpResponse> {
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)) 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) .fetch_one(pool)
.await; .await;
@ -507,7 +647,7 @@ impl Member {
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
let (uuid, nickname): (String, Option<String>) = row.unwrap(); let (uuid, nickname): (String, Option<String>) = row.unwrap();
@ -520,17 +660,24 @@ impl Member {
}) })
} }
pub async fn new(pool: &Pool<Postgres>, user_uuid: Uuid, guild_uuid: Uuid) -> Result<Self, HttpResponse> { pub async fn new(
pool: &Pool<Postgres>,
user_uuid: Uuid,
guild_uuid: Uuid,
) -> Result<Self, HttpResponse> {
let member_uuid = Uuid::now_v7(); let member_uuid = Uuid::now_v7();
let row = sqlx::query(&format!("INSERT INTO guild_members uuid, guild_uuid, user_uuid VALUES ('{}', '{}', '{}')", member_uuid, guild_uuid, user_uuid)) let row = sqlx::query(&format!(
.execute(pool) "INSERT INTO guild_members uuid, guild_uuid, user_uuid VALUES ('{}', '{}', '{}')",
.await; member_uuid, guild_uuid, user_uuid
))
.execute(pool)
.await;
if let Err(error) = row { if let Err(error) = row {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
Ok(Self { Ok(Self {
@ -599,15 +746,16 @@ pub struct Invite {
impl Invite { impl Invite {
pub async fn fetch_one(pool: &Pool<Postgres>, invite_id: String) -> Result<Self, HttpResponse> { pub async fn fetch_one(pool: &Pool<Postgres>, invite_id: String) -> Result<Self, HttpResponse> {
let invite: Result<InviteBuilder, sqlx::Error> = sqlx::query_as("SELECT id, user_uuid, guild_uuid FROM invites WHERE id = $1") let invite: Result<InviteBuilder, sqlx::Error> =
.bind(invite_id) sqlx::query_as("SELECT id, user_uuid, guild_uuid FROM invites WHERE id = $1")
.fetch_one(pool) .bind(invite_id)
.await; .fetch_one(pool)
.await;
if let Err(error) = invite { if let Err(error) = invite {
error!("{}", error); error!("{}", error);
return Err(HttpResponse::InternalServerError().finish()) return Err(HttpResponse::InternalServerError().finish());
} }
Ok(invite.unwrap().build()) Ok(invite.unwrap().build())
@ -628,3 +776,9 @@ impl Invite {
Ok(()) Ok(())
} }
} }
#[derive(Deserialize)]
pub struct StartAmountQuery {
pub start: Option<i32>,
pub amount: Option<i32>,
}

View file

@ -1,4 +1,8 @@
use actix_web::{cookie::{time::Duration, Cookie, SameSite}, http::header::HeaderMap, HttpResponse}; use actix_web::{
HttpResponse,
cookie::{Cookie, SameSite, time::Duration},
http::header::HeaderMap,
};
use getrandom::fill; use getrandom::fill;
use hex::encode; use hex::encode;
use redis::RedisError; use redis::RedisError;
@ -9,7 +13,7 @@ use crate::Data;
pub fn get_auth_header(headers: &HeaderMap) -> Result<&str, HttpResponse> { pub fn get_auth_header(headers: &HeaderMap) -> Result<&str, HttpResponse> {
let auth_token = headers.get(actix_web::http::header::AUTHORIZATION); let auth_token = headers.get(actix_web::http::header::AUTHORIZATION);
if let None = auth_token { if auth_token.is_none() {
return Err(HttpResponse::Unauthorized().finish()); return Err(HttpResponse::Unauthorized().finish());
} }
@ -21,7 +25,7 @@ pub fn get_auth_header(headers: &HeaderMap) -> Result<&str, HttpResponse> {
let auth_value = auth.unwrap().split_whitespace().nth(1); let auth_value = auth.unwrap().split_whitespace().nth(1);
if let None = auth_value { if auth_value.is_none() {
return Err(HttpResponse::BadRequest().finish()); return Err(HttpResponse::BadRequest().finish());
} }
@ -51,16 +55,27 @@ pub fn generate_refresh_token() -> Result<String, getrandom::Error> {
} }
impl Data { impl Data {
pub async fn set_cache_key(&self, key: String, value: impl Serialize, expire: u32) -> Result<(), RedisError> { pub async fn set_cache_key(
&self,
key: String,
value: impl Serialize,
expire: u32,
) -> Result<(), RedisError> {
let mut conn = self.cache_pool.get_multiplexed_tokio_connection().await?; let mut conn = self.cache_pool.get_multiplexed_tokio_connection().await?;
let key_encoded = encode(key); let key_encoded = encode(key);
let value_json = serde_json::to_string(&value).unwrap(); let value_json = serde_json::to_string(&value).unwrap();
redis::cmd("SET",).arg(&[key_encoded.clone(), value_json]).exec_async(&mut conn).await?; redis::cmd("SET")
.arg(&[key_encoded.clone(), value_json])
.exec_async(&mut conn)
.await?;
redis::cmd("EXPIRE").arg(&[key_encoded, expire.to_string()]).exec_async(&mut conn).await redis::cmd("EXPIRE")
.arg(&[key_encoded, expire.to_string()])
.exec_async(&mut conn)
.await
} }
pub async fn get_cache_key(&self, key: String) -> Result<String, RedisError> { pub async fn get_cache_key(&self, key: String) -> Result<String, RedisError> {
@ -68,7 +83,10 @@ impl Data {
let key_encoded = encode(key); let key_encoded = encode(key);
redis::cmd("GET").arg(key_encoded).query_async(&mut conn).await redis::cmd("GET")
.arg(key_encoded)
.query_async(&mut conn)
.await
} }
pub async fn del_cache_key(&self, key: String) -> Result<(), RedisError> { pub async fn del_cache_key(&self, key: String) -> Result<(), RedisError> {
@ -76,7 +94,9 @@ impl Data {
let key_encoded = encode(key); let key_encoded = encode(key);
redis::cmd("DEL").arg(key_encoded).query_async(&mut conn).await redis::cmd("DEL")
.arg(key_encoded)
.query_async(&mut conn)
.await
} }
} }