diff --git a/Dockerfile b/Dockerfile index d7209ef..a8e8dc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,8 +18,7 @@ RUN useradd --create-home --home-dir /gorb gorb USER gorb -ENV WEB_FRONTEND_URL=https://gorb.app/web/ \ -WEB_BASE_PATH=/api \ +ENV WEB_URL=https://gorb.app/web/ \ DATABASE_USERNAME=gorb \ DATABASE_PASSWORD=gorb \ DATABASE=gorb \ diff --git a/compose.dev.yml b/compose.dev.yml index 93a1a85..e80f2a7 100644 --- a/compose.dev.yml +++ b/compose.dev.yml @@ -18,7 +18,7 @@ services: - gorb-backend:/gorb environment: #- RUST_LOG=debug - - WEB_FRONTEND_URL=https://gorb.app/web/ + - WEB_URL=https://gorb.app/web/ - DATABASE_USERNAME=gorb - DATABASE_PASSWORD=gorb - DATABASE=gorb diff --git a/compose.yml b/compose.yml index b1dc07d..2bc7339 100644 --- a/compose.yml +++ b/compose.yml @@ -16,7 +16,7 @@ services: - gorb-backend:/gorb environment: #- RUST_LOG=debug - - WEB_FRONTEND_URL=https://gorb.app/web/ + - WEB_URL=https://gorb.app/web/ - DATABASE_USERNAME=gorb - DATABASE_PASSWORD=gorb - DATABASE=gorb diff --git a/entrypoint.sh b/entrypoint.sh index 38ba890..9c7a401 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -11,8 +11,7 @@ fi if [ ! -f "/gorb/config/config.toml" ]; then cat > /gorb/config/config.toml < Scope { - web::scope(path.trim_end_matches('/')).service(v1::web()).service(versions::get) +pub fn web() -> Scope { + web::scope("/api").service(v1::web()).service(versions::get) } diff --git a/src/api/v1/channels/mod.rs b/src/api/v1/channels/mod.rs deleted file mode 100644 index d3d5d23..0000000 --- a/src/api/v1/channels/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -use actix_web::{web, Scope}; - -mod uuid; - -pub fn web() -> Scope { - web::scope("/channels") - .service(uuid::get) - .service(uuid::delete) - .service(uuid::messages::get) - .service(uuid::socket::ws) -} diff --git a/src/api/v1/channels/uuid/mod.rs b/src/api/v1/channels/uuid/mod.rs deleted file mode 100644 index f6c93fe..0000000 --- a/src/api/v1/channels/uuid/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -pub mod messages; -pub mod socket; - -use crate::{ - api::v1::auth::check_access_token, error::Error, structs::{Channel, Member}, utils::{get_auth_header, global_checks}, Data -}; -use actix_web::{HttpRequest, HttpResponse, delete, get, web}; -use uuid::Uuid; - -#[get("/{uuid}")] -pub async fn get( - req: HttpRequest, - path: web::Path<(Uuid,)>, - data: web::Data, -) -> Result { - let headers = req.headers(); - - let auth_header = get_auth_header(headers)?; - - let channel_uuid = path.into_inner().0; - - let mut conn = data.pool.get().await?; - - let uuid = check_access_token(auth_header, &mut conn).await?; - - global_checks(&data, uuid).await?; - - let channel = Channel::fetch_one(&data, channel_uuid).await?; - - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; - - Ok(HttpResponse::Ok().json(channel)) -} - -#[delete("/{uuid}")] -pub async fn delete( - req: HttpRequest, - path: web::Path<(Uuid,)>, - data: web::Data, -) -> Result { - let headers = req.headers(); - - let auth_header = get_auth_header(headers)?; - - let channel_uuid = path.into_inner().0; - - let mut conn = data.pool.get().await?; - - let uuid = check_access_token(auth_header, &mut conn).await?; - - global_checks(&data, uuid).await?; - - let channel = Channel::fetch_one(&data, channel_uuid).await?; - - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; - - channel.delete(&data).await?; - - Ok(HttpResponse::Ok().finish()) -} diff --git a/src/api/v1/me/mod.rs b/src/api/v1/me/mod.rs index ac24342..cd86e43 100644 --- a/src/api/v1/me/mod.rs +++ b/src/api/v1/me/mod.rs @@ -6,13 +6,10 @@ use crate::{ api::v1::auth::check_access_token, error::Error, structs::Me, utils::{get_auth_header, global_checks}, Data }; -mod guilds; +mod servers; pub fn web() -> Scope { - web::scope("/me") - .service(get) - .service(update) - .service(guilds::get) + web::scope("/me").service(get).service(update) } #[get("")] diff --git a/src/api/v1/me/guilds.rs b/src/api/v1/me/servers.rs similarity index 89% rename from src/api/v1/me/guilds.rs rename to src/api/v1/me/servers.rs index 1e92a34..ae026a7 100644 --- a/src/api/v1/me/guilds.rs +++ b/src/api/v1/me/servers.rs @@ -1,11 +1,11 @@ -//! `/api/v1/me/guilds` Contains endpoint related to guild memberships +//! `/api/v1/me/servers` Contains endpoint related to guild memberships use actix_web::{get, web, HttpRequest, HttpResponse}; use crate::{api::v1::auth::check_access_token, error::Error, structs::Me, utils::{get_auth_header, global_checks}, Data}; -/// `GET /api/v1/me/guilds` Returns all guild memberships in a list +/// `GET /api/v1/me/servers` Returns all guild memberships in a list /// /// requires auth: yes /// @@ -27,7 +27,7 @@ use crate::{api::v1::auth::check_access_token, error::Error, structs::Me, utils: /// ]); /// ``` /// NOTE: UUIDs in this response are made using `uuidgen`, UUIDs made by the actual backend will be UUIDv7 and have extractable timestamps -#[get("/guilds")] +#[get("/servers")] pub async fn get(req: HttpRequest, data: web::Data) -> Result { let headers = req.headers(); diff --git a/src/api/v1/mod.rs b/src/api/v1/mod.rs index 421c10d..f30ad58 100644 --- a/src/api/v1/mod.rs +++ b/src/api/v1/mod.rs @@ -3,9 +3,8 @@ use actix_web::{Scope, web}; mod auth; -mod channels; mod invites; -mod guilds; +mod servers; mod stats; mod users; mod me; @@ -15,8 +14,7 @@ pub fn web() -> Scope { .service(stats::res) .service(auth::web()) .service(users::web()) - .service(channels::web()) - .service(guilds::web()) + .service(servers::web()) .service(invites::web()) .service(me::web()) } diff --git a/src/api/v1/guilds/mod.rs b/src/api/v1/servers/mod.rs similarity index 96% rename from src/api/v1/guilds/mod.rs rename to src/api/v1/servers/mod.rs index 4c2dbf1..f0d27a8 100644 --- a/src/api/v1/guilds/mod.rs +++ b/src/api/v1/servers/mod.rs @@ -1,4 +1,4 @@ -//! `/api/v1/guilds` Guild related endpoints +//! `/api/v1/servers` Guild related endpoints use actix_web::{HttpRequest, HttpResponse, Scope, get, post, web}; use serde::Deserialize; @@ -15,13 +15,13 @@ struct GuildInfo { } pub fn web() -> Scope { - web::scope("/guilds") + web::scope("/servers") .service(post) .service(get) .service(uuid::web()) } -/// `POST /api/v1/guilds` Creates a new guild +/// `POST /api/v1/servers` Creates a new guild /// /// requires auth: yes /// diff --git a/src/api/v1/guilds/uuid/channels.rs b/src/api/v1/servers/uuid/channels/mod.rs similarity index 99% rename from src/api/v1/guilds/uuid/channels.rs rename to src/api/v1/servers/uuid/channels/mod.rs index 813de13..6327e33 100644 --- a/src/api/v1/guilds/uuid/channels.rs +++ b/src/api/v1/servers/uuid/channels/mod.rs @@ -5,6 +5,8 @@ use ::uuid::Uuid; use actix_web::{HttpRequest, HttpResponse, get, post, web}; use serde::Deserialize; +pub mod uuid; + #[derive(Deserialize)] struct ChannelInfo { name: String, diff --git a/src/api/v1/channels/uuid/messages.rs b/src/api/v1/servers/uuid/channels/uuid/messages.rs similarity index 68% rename from src/api/v1/channels/uuid/messages.rs rename to src/api/v1/servers/uuid/channels/uuid/messages.rs index 90055c8..27bf0cf 100644 --- a/src/api/v1/channels/uuid/messages.rs +++ b/src/api/v1/servers/uuid/channels/uuid/messages.rs @@ -1,4 +1,4 @@ -//! `/api/v1/channels/{uuid}/messages` Endpoints related to channel messages +//! `/api/v1/servers/{uuid}/channels/{uuid}/messages` Endpoints related to channel messages use crate::{ api::v1::auth::check_access_token, error::Error, structs::{Channel, Member}, utils::{get_auth_header, global_checks}, Data @@ -13,7 +13,7 @@ struct MessageRequest { offset: i64, } -/// `GET /api/v1/channels/{uuid}/messages` Returns user with the given UUID +/// `GET /api/v1/servers/{uuid}/channels/{uuid}/messages` Returns user with the given UUID /// /// requires auth: yes /// @@ -43,10 +43,10 @@ struct MessageRequest { /// }); /// ``` /// -#[get("/{uuid}/messages")] +#[get("{uuid}/channels/{channel_uuid}/messages")] pub async fn get( req: HttpRequest, - path: web::Path<(Uuid,)>, + path: web::Path<(Uuid, Uuid)>, message_request: web::Query, data: web::Data, ) -> Result { @@ -54,7 +54,7 @@ pub async fn get( let auth_header = get_auth_header(headers)?; - let channel_uuid = path.into_inner().0; + let (guild_uuid, channel_uuid) = path.into_inner(); let mut conn = data.pool.get().await?; @@ -62,9 +62,18 @@ pub async fn get( global_checks(&data, uuid).await?; - let channel = Channel::fetch_one(&data, channel_uuid).await?; + Member::fetch_one(&mut conn, uuid, guild_uuid).await?; - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; + let channel: Channel; + + if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await { + channel = serde_json::from_str(&cache_hit)? + } else { + channel = Channel::fetch_one(&mut conn, channel_uuid).await?; + + data.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60) + .await?; + } let messages = channel .fetch_messages(&data, message_request.amount, message_request.offset) diff --git a/src/api/v1/servers/uuid/channels/uuid/mod.rs b/src/api/v1/servers/uuid/channels/uuid/mod.rs new file mode 100644 index 0000000..946adf3 --- /dev/null +++ b/src/api/v1/servers/uuid/channels/uuid/mod.rs @@ -0,0 +1,77 @@ +pub mod messages; +pub mod socket; + +use crate::{ + api::v1::auth::check_access_token, error::Error, structs::{Channel, Member}, utils::{get_auth_header, global_checks}, Data +}; +use actix_web::{HttpRequest, HttpResponse, delete, get, web}; +use uuid::Uuid; + +#[get("{uuid}/channels/{channel_uuid}")] +pub async fn get( + req: HttpRequest, + path: web::Path<(Uuid, Uuid)>, + data: web::Data, +) -> Result { + let headers = req.headers(); + + let auth_header = get_auth_header(headers)?; + + let (guild_uuid, channel_uuid) = path.into_inner(); + + let mut conn = data.pool.get().await?; + + let uuid = check_access_token(auth_header, &mut conn).await?; + + global_checks(&data, uuid).await?; + + Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + + if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await { + return Ok(HttpResponse::Ok() + .content_type("application/json") + .body(cache_hit)); + } + + let channel = Channel::fetch_one(&mut conn, channel_uuid).await?; + + data.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60) + .await?; + + Ok(HttpResponse::Ok().json(channel)) +} + +#[delete("{uuid}/channels/{channel_uuid}")] +pub async fn delete( + req: HttpRequest, + path: web::Path<(Uuid, Uuid)>, + data: web::Data, +) -> Result { + let headers = req.headers(); + + let auth_header = get_auth_header(headers)?; + + let (guild_uuid, channel_uuid) = path.into_inner(); + + let mut conn = data.pool.get().await?; + + let uuid = check_access_token(auth_header, &mut conn).await?; + + global_checks(&data, uuid).await?; + + Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + + let channel: Channel; + + if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await { + channel = serde_json::from_str(&cache_hit)?; + + data.del_cache_key(format!("{}", channel_uuid)).await?; + } else { + channel = Channel::fetch_one(&mut conn, channel_uuid).await?; + } + + channel.delete(&mut conn).await?; + + Ok(HttpResponse::Ok().finish()) +} diff --git a/src/api/v1/channels/uuid/socket.rs b/src/api/v1/servers/uuid/channels/uuid/socket.rs similarity index 82% rename from src/api/v1/channels/uuid/socket.rs rename to src/api/v1/servers/uuid/channels/uuid/socket.rs index c7ca1e8..3cbdb8f 100644 --- a/src/api/v1/channels/uuid/socket.rs +++ b/src/api/v1/servers/uuid/channels/uuid/socket.rs @@ -11,10 +11,10 @@ use crate::{ api::v1::auth::check_access_token, structs::{Channel, Member}, utils::{get_ws_protocol_header, global_checks}, Data }; -#[get("/{uuid}/socket")] +#[get("{uuid}/channels/{channel_uuid}/socket")] pub async fn ws( req: HttpRequest, - path: web::Path<(Uuid,)>, + path: web::Path<(Uuid, Uuid)>, stream: web::Payload, data: web::Data, ) -> Result { @@ -24,8 +24,8 @@ pub async fn ws( // Retrieve auth header let auth_header = get_ws_protocol_header(headers)?; - // Get uuid from path - let channel_uuid = path.into_inner().0; + // Get uuids from path + let (guild_uuid, channel_uuid) = path.into_inner(); let mut conn = data.pool.get().await.map_err(crate::error::Error::from)?; @@ -34,10 +34,20 @@ pub async fn ws( global_checks(&data, uuid).await?; - let channel = Channel::fetch_one(&data, channel_uuid).await?; - // Get server member from psql - Member::fetch_one(&mut conn, uuid, channel.guild_uuid).await?; + Member::fetch_one(&mut conn, uuid, guild_uuid).await?; + + let channel: Channel; + + // Return channel cache or result from psql as `channel` variable + if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await { + channel = serde_json::from_str(&cache_hit)? + } else { + channel = Channel::fetch_one(&mut conn, channel_uuid).await?; + + data.set_cache_key(format!("{}", channel_uuid), channel.clone(), 60) + .await?; + } let (mut res, mut session_1, stream) = actix_ws::handle(&req, stream)?; diff --git a/src/api/v1/guilds/uuid/icon.rs b/src/api/v1/servers/uuid/icon.rs similarity index 88% rename from src/api/v1/guilds/uuid/icon.rs rename to src/api/v1/servers/uuid/icon.rs index 0ac4470..876aec4 100644 --- a/src/api/v1/guilds/uuid/icon.rs +++ b/src/api/v1/servers/uuid/icon.rs @@ -1,4 +1,4 @@ -//! `/api/v1/guilds/{uuid}/icon` icon related endpoints, will probably be replaced by a multipart post to above endpoint +//! `/api/v1/servers/{uuid}/icon` icon related endpoints, will probably be replaced by a multipart post to above endpoint use actix_web::{HttpRequest, HttpResponse, put, web}; use futures_util::StreamExt as _; @@ -8,7 +8,7 @@ use crate::{ api::v1::auth::check_access_token, error::Error, structs::{Guild, Member}, utils::{get_auth_header, global_checks}, Data }; -/// `PUT /api/v1/guilds/{uuid}/icon` Icon upload +/// `PUT /api/v1/servers/{uuid}/icon` Icon upload /// /// requires auth: no /// diff --git a/src/api/v1/guilds/uuid/invites/id.rs b/src/api/v1/servers/uuid/invites/id.rs similarity index 100% rename from src/api/v1/guilds/uuid/invites/id.rs rename to src/api/v1/servers/uuid/invites/id.rs diff --git a/src/api/v1/guilds/uuid/invites/mod.rs b/src/api/v1/servers/uuid/invites/mod.rs similarity index 100% rename from src/api/v1/guilds/uuid/invites/mod.rs rename to src/api/v1/servers/uuid/invites/mod.rs diff --git a/src/api/v1/guilds/uuid/mod.rs b/src/api/v1/servers/uuid/mod.rs similarity index 90% rename from src/api/v1/guilds/uuid/mod.rs rename to src/api/v1/servers/uuid/mod.rs index 7ab719d..39b2925 100644 --- a/src/api/v1/guilds/uuid/mod.rs +++ b/src/api/v1/servers/uuid/mod.rs @@ -1,4 +1,4 @@ -//! `/api/v1/guilds/{uuid}` Specific server endpoints +//! `/api/v1/servers/{uuid}` Specific server endpoints use actix_web::{HttpRequest, HttpResponse, Scope, get, web}; use uuid::Uuid; @@ -19,6 +19,10 @@ pub fn web() -> Scope { // Channels .service(channels::get) .service(channels::create) + .service(channels::uuid::get) + .service(channels::uuid::delete) + .service(channels::uuid::messages::get) + .service(channels::uuid::socket::ws) // Roles .service(roles::get) .service(roles::create) @@ -30,7 +34,7 @@ pub fn web() -> Scope { .service(icon::upload) } -/// `GET /api/v1/guilds/{uuid}` DESCRIPTION +/// `GET /api/v1/servers/{uuid}` DESCRIPTION /// /// requires auth: yes /// diff --git a/src/api/v1/guilds/uuid/roles/mod.rs b/src/api/v1/servers/uuid/roles/mod.rs similarity index 100% rename from src/api/v1/guilds/uuid/roles/mod.rs rename to src/api/v1/servers/uuid/roles/mod.rs diff --git a/src/api/v1/guilds/uuid/roles/uuid.rs b/src/api/v1/servers/uuid/roles/uuid.rs similarity index 100% rename from src/api/v1/guilds/uuid/roles/uuid.rs rename to src/api/v1/servers/uuid/roles/uuid.rs diff --git a/src/config.rs b/src/config.rs index 464c98d..44f73ed 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,8 +38,7 @@ pub struct CacheDatabase { struct WebBuilder { ip: Option, port: Option, - base_path: Option, - frontend_url: Url, + url: Url, _ssl: Option, } @@ -86,8 +85,7 @@ impl ConfigBuilder { let web = Web { ip: self.web.ip.unwrap_or(String::from("0.0.0.0")), port: self.web.port.unwrap_or(8080), - base_path: self.web.base_path.unwrap_or("".to_string()), - frontend_url: self.web.frontend_url, + url: self.web.url, }; let endpoint = match &*self.bunny.endpoint { @@ -148,8 +146,7 @@ pub struct Config { pub struct Web { pub ip: String, pub port: u16, - pub base_path: String, - pub frontend_url: Url, + pub url: Url, } #[derive(Debug, Clone)] diff --git a/src/main.rs b/src/main.rs index 5670e7b..0f94be8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -150,7 +150,7 @@ async fn main() -> Result<(), Error> { App::new() .app_data(web::Data::new(data.clone())) .wrap(cors) - .service(api::web(&data.config.web.base_path)) + .service(api::web()) }) .bind((web.ip, web.port))? .run() diff --git a/src/structs.rs b/src/structs.rs index 50615ff..500396f 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -183,26 +183,15 @@ impl Channel { futures::future::try_join_all(channel_futures).await } - pub async fn fetch_one(data: &Data, channel_uuid: Uuid) -> Result { - if let Ok(cache_hit) = data.get_cache_key(channel_uuid.to_string()).await { - return Ok(serde_json::from_str(&cache_hit)?); - } - - let mut conn = data.pool.get().await?; - + pub async fn fetch_one(conn: &mut Conn, channel_uuid: Uuid) -> Result { use channels::dsl; let channel_builder: ChannelBuilder = dsl::channels .filter(dsl::uuid.eq(channel_uuid)) .select(ChannelBuilder::as_select()) - .get_result(&mut conn) + .get_result(conn) .await?; - let channel = channel_builder.build(&mut conn).await?; - - data.set_cache_key(channel_uuid.to_string(), channel.clone(), 60) - .await?; - - Ok(channel) + channel_builder.build(conn).await } pub async fn new( @@ -256,27 +245,19 @@ impl Channel { data.set_cache_key(channel_uuid.to_string(), channel.clone(), 1800) .await?; - if let Ok(_) = data.get_cache_key(format!("{}_channels", guild_uuid)).await { - data.del_cache_key(format!("{}_channels", guild_uuid)) - .await?; - } + data.del_cache_key(format!("{}_channels", guild_uuid)) + .await?; Ok(channel) } - pub async fn delete(self, data: &Data) -> Result<(), Error> { - let mut conn = data.pool.get().await?; - + pub async fn delete(self, conn: &mut Conn) -> Result<(), Error> { use channels::dsl; delete(channels::table) .filter(dsl::uuid.eq(self.uuid)) - .execute(&mut conn) + .execute(conn) .await?; - if let Ok(_) = data.get_cache_key(self.uuid.to_string()).await { - data.del_cache_key(self.uuid.to_string()).await?; - } - Ok(()) } @@ -1014,7 +995,7 @@ impl EmailToken { .execute(&mut conn) .await?; - let mut verify_endpoint = data.config.web.frontend_url.join("verify-email")?; + let mut verify_endpoint = data.config.web.url.join("verify-email")?; verify_endpoint.set_query(Some(&format!("token={}", token))); @@ -1104,7 +1085,7 @@ impl PasswordResetToken { .execute(&mut conn) .await?; - let mut reset_endpoint = data.config.web.frontend_url.join("reset-password")?; + let mut reset_endpoint = data.config.web.url.join("reset-password")?; reset_endpoint.set_query(Some(&format!("token={}", token))); @@ -1153,7 +1134,7 @@ impl PasswordResetToken { .get_result(&mut conn) .await?; - let login_page = data.config.web.frontend_url.join("login")?; + let login_page = data.config.web.url.join("login")?; let email = data .mail_client