diff --git a/sync-server/src/server/endpoints.rs b/sync-server/src/server/endpoints.rs index ed873473..23323a69 100644 --- a/sync-server/src/server/endpoints.rs +++ b/sync-server/src/server/endpoints.rs @@ -11,3 +11,19 @@ pub mod put_file_manifest; mod utils; pub mod vault_snapshot; pub mod websocket; + +use serde::{Deserialize, Deserializer}; + +use crate::{app_state::database::models::VaultId, utils::normalize_vault_id::normalize_vault_id}; + +#[derive(Debug)] +pub struct VaultPath(pub VaultId); + +impl<'de> Deserialize<'de> for VaultPath { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + normalize_vault_id(deserializer).map(Self) + } +} diff --git a/sync-server/src/server/endpoints/events.rs b/sync-server/src/server/endpoints/events.rs index d73367df..2505afba 100644 --- a/sync-server/src/server/endpoints/events.rs +++ b/sync-server/src/server/endpoints/events.rs @@ -5,21 +5,12 @@ use axum::{ use log::debug; use serde::Deserialize; +use super::VaultPath; use crate::{ - app_state::{ - AppState, - database::models::{EventBatch, VaultId}, - }, + app_state::{AppState, database::models::EventBatch}, errors::{SyncServerError, client_error, server_error}, - utils::normalize_vault_id::normalize_vault_id, }; -#[derive(Deserialize)] -pub struct EventsPath { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, -} - #[derive(Deserialize)] pub struct EventsQuery { pub after: i64, @@ -27,13 +18,13 @@ pub struct EventsQuery { #[axum::debug_handler] pub async fn events( - Path(path): Path, + Path(VaultPath(vault_id)): Path, Query(query): Query, State(state): State, ) -> Result, SyncServerError> { debug!( "Fetching events after `{}` for vault `{}`", - query.after, path.vault_id + query.after, vault_id ); if query.after < 0 { @@ -42,7 +33,7 @@ pub async fn events( state .database - .events_after(&path.vault_id, query.after) + .events_after(&vault_id, query.after) .await .map(Json) .map_err(server_error) diff --git a/sync-server/src/server/endpoints/fetch_document_version_content.rs b/sync-server/src/server/endpoints/fetch_document_version_content.rs index ed9d3ae1..9e981c5f 100644 --- a/sync-server/src/server/endpoints/fetch_document_version_content.rs +++ b/sync-server/src/server/endpoints/fetch_document_version_content.rs @@ -4,33 +4,23 @@ use axum::{ extract::{Path, State}, }; use log::debug; -use serde::Deserialize; +use super::VaultPath; use crate::{ app_state::{ AppState, - database::models::{DocumentId, VaultId, VaultUpdateId}, + database::models::{DocumentId, VaultUpdateId}, }, errors::{SyncServerError, not_found_error, server_error}, - utils::normalize_vault_id::normalize_vault_id, }; -#[derive(Deserialize)] -pub struct FetchDocumentVersionContentPathParams { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, - - document_id: DocumentId, - vault_update_id: VaultUpdateId, -} - #[axum::debug_handler] pub async fn fetch_document_version_content( - Path(FetchDocumentVersionContentPathParams { - vault_id, - document_id, - vault_update_id, - }): Path, + Path((VaultPath(vault_id), document_id, vault_update_id)): Path<( + VaultPath, + DocumentId, + VaultUpdateId, + )>, State(state): State, ) -> Result { debug!( diff --git a/sync-server/src/server/endpoints/fetch_latest_document_version.rs b/sync-server/src/server/endpoints/fetch_latest_document_version.rs index a05f632f..870ac669 100644 --- a/sync-server/src/server/endpoints/fetch_latest_document_version.rs +++ b/sync-server/src/server/endpoints/fetch_latest_document_version.rs @@ -4,31 +4,19 @@ use axum::{ extract::{Path, State}, }; use log::debug; -use serde::Deserialize; +use super::VaultPath; use crate::{ app_state::{ AppState, - database::models::{DocumentId, DocumentVersion, VaultId}, + database::models::{DocumentId, DocumentVersion}, }, errors::{SyncServerError, not_found_error, server_error}, - utils::normalize_vault_id::normalize_vault_id, }; -#[derive(Deserialize)] -pub struct FetchLatestDocumentVersionPathParams { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, - - document_id: DocumentId, -} - #[axum::debug_handler] pub async fn fetch_latest_document_version( - Path(FetchLatestDocumentVersionPathParams { - vault_id, - document_id, - }): Path, + Path((VaultPath(vault_id), document_id)): Path<(VaultPath, DocumentId)>, State(state): State, ) -> Result, SyncServerError> { debug!("Fetching latest document version for document `{document_id}` in vault `{vault_id}`"); diff --git a/sync-server/src/server/endpoints/get_file_manifest.rs b/sync-server/src/server/endpoints/get_file_manifest.rs index d844ff41..3f706696 100644 --- a/sync-server/src/server/endpoints/get_file_manifest.rs +++ b/sync-server/src/server/endpoints/get_file_manifest.rs @@ -3,36 +3,26 @@ use axum::{ extract::{Path, State}, }; use log::debug; -use serde::Deserialize; +use super::VaultPath; use crate::{ app_state::{ AppState, - database::{ - Database, - models::{FileManifest, VaultId}, - }, + database::{Database, models::FileManifest}, }, errors::{SyncServerError, server_error}, - utils::normalize_vault_id::normalize_vault_id, }; -#[derive(Deserialize)] -pub struct GetFileManifestPath { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, -} - #[axum::debug_handler] pub async fn get_file_manifest( - Path(path): Path, + Path(VaultPath(vault_id)): Path, State(state): State, ) -> Result, SyncServerError> { - debug!("Fetching file manifest for vault `{}`", path.vault_id); + debug!("Fetching file manifest for vault `{vault_id}`"); let mut transaction = state .database - .create_readonly_transaction(&path.vault_id) + .create_readonly_transaction(&vault_id) .await .map_err(server_error)?; diff --git a/sync-server/src/server/endpoints/ping.rs b/sync-server/src/server/endpoints/ping.rs index 8e910feb..412ed0d2 100644 --- a/sync-server/src/server/endpoints/ping.rs +++ b/sync-server/src/server/endpoints/ping.rs @@ -7,26 +7,15 @@ use axum_extra::{ headers::{Authorization, authorization::Bearer}, }; use log::debug; -use serde::Deserialize; +use super::VaultPath; use crate::server::{auth::auth, responses::PingResponse}; -use crate::{ - app_state::{AppState, database::models::VaultId}, - consts::SUPPORTED_API_VERSION, - errors::SyncServerError, - utils::normalize_vault_id::normalize_vault_id, -}; - -#[derive(Deserialize)] -pub struct PingPathParams { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, -} +use crate::{app_state::AppState, consts::SUPPORTED_API_VERSION, errors::SyncServerError}; #[axum::debug_handler] pub async fn ping( maybe_auth_header: Option>>, - Path(PingPathParams { vault_id }): Path, + Path(VaultPath(vault_id)): Path, State(state): State, ) -> Result, SyncServerError> { debug!("Pinging vault `{vault_id}`"); diff --git a/sync-server/src/server/endpoints/put_file_content.rs b/sync-server/src/server/endpoints/put_file_content.rs index 26c9cfa3..b7883299 100644 --- a/sync-server/src/server/endpoints/put_file_content.rs +++ b/sync-server/src/server/endpoints/put_file_content.rs @@ -1,4 +1,4 @@ -use super::utils::find_already_processed_event; +use super::{VaultPath, utils::find_already_processed_event}; use crate::{ app_state::{ AppState, @@ -14,7 +14,6 @@ use crate::{ requests::{PushContent, PutFileContent}, responses::DocumentUpdateResponse, }, - utils::normalize_vault_id::normalize_vault_id, }; use anyhow::anyhow; use axum::{ @@ -25,37 +24,26 @@ use axum_extra::TypedHeader; use base64::{Engine as _, engine::general_purpose::STANDARD}; use log::debug; use reconcile_text::{BuiltinTokenizer, EditedText, NumberOrText}; -use serde::Deserialize; use sha2::{Digest, Sha256}; -#[derive(Deserialize)] -pub struct PutFileContentPath { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, - document_id: DocumentId, -} - #[axum::debug_handler] pub async fn put_file_content( - Path(path): Path, + Path((VaultPath(vault_id), document_id)): Path<(VaultPath, DocumentId)>, Extension(user): Extension, TypedHeader(device): TypedHeader, State(state): State, Json(push): Json, ) -> Result, SyncServerError> { - debug!( - "Pushing document `{}` in vault `{}`", - path.document_id, path.vault_id - ); + debug!("Pushing document `{}` in vault `{}`", document_id, vault_id); let fingerprint = Sha256::digest( - serde_json::to_vec(&("content", path.document_id, &push)) + serde_json::to_vec(&("content", document_id, &push)) .map_err(|error| server_error(error.into()))?, ); let mut tx = state .database - .create_write_transaction(&path.vault_id) + .create_write_transaction(&vault_id) .await .map_err(server_error)?; @@ -74,7 +62,7 @@ pub async fn put_file_content( let parent = state .database - .get_latest_document_version(&path.vault_id, &path.document_id, Some(&mut tx)) + .get_latest_document_version(&vault_id, &document_id, Some(&mut tx)) .await .map_err(server_error)?; @@ -87,7 +75,7 @@ pub async fn put_file_content( } else if push.parent_version_id.is_some() { return Err(not_found_error(anyhow!( "Document {} does not exist", - path.document_id + document_id ))); } @@ -123,7 +111,7 @@ pub async fn put_file_content( vault_update_id: Database::allocate_event(&mut tx, push.request_id, &fingerprint) .await .map_err(server_error)?, - document_id: path.document_id, + document_id, content, updated_date: chrono::Utc::now(), user_id: user.name, @@ -152,10 +140,7 @@ pub async fn put_file_content( .await .map_err(|error| server_error(error.into()))?; - state - .broadcasts - .notify_about_vault_update(path.vault_id) - .await; + state.broadcasts.notify_about_vault_update(vault_id).await; Ok(Json(response)) } @@ -185,10 +170,7 @@ mod tests { push: PutFileContent, ) -> Result { put_file_content( - Path(PutFileContentPath { - vault_id: vault.clone(), - document_id, - }), + Path((VaultPath(vault.clone()), document_id)), Extension(User { name: user_id, token: String::new(), diff --git a/sync-server/src/server/endpoints/put_file_manifest.rs b/sync-server/src/server/endpoints/put_file_manifest.rs index 667c7827..945d4dcb 100644 --- a/sync-server/src/server/endpoints/put_file_manifest.rs +++ b/sync-server/src/server/endpoints/put_file_manifest.rs @@ -4,36 +4,28 @@ use axum::{ extract::{Path, State}, }; use log::debug; -use serde::Deserialize; use sha2::{Digest, Sha256}; -use super::utils::find_already_processed_event; +use super::{VaultPath, utils::find_already_processed_event}; use crate::{ app_state::{ AppState, database::{ Database, - models::{EventRecord, FileManifest, VaultEvent, VaultId}, + models::{EventRecord, FileManifest, VaultEvent}, }, }, errors::{SyncServerError, client_error, server_error}, server::{requests::PushFileManifest, responses::FileManifestUpdateResponse}, - utils::normalize_vault_id::normalize_vault_id, }; -#[derive(Deserialize)] -pub struct PutFileManifestPath { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, -} - #[axum::debug_handler] pub async fn put_file_manifest( - Path(path): Path, + Path(VaultPath(vault_id)): Path, State(state): State, Json(push): Json, ) -> Result, SyncServerError> { - debug!("Pushing file manifest for vault `{}`", path.vault_id); + debug!("Pushing file manifest for vault `{vault_id}`"); // BTreeMap canonicalizes file manifest entries for request fingerprinting. let fingerprint = Sha256::digest( @@ -43,7 +35,7 @@ pub async fn put_file_manifest( let mut tx = state .database - .create_write_transaction(&path.vault_id) + .create_write_transaction(&vault_id) .await .map_err(server_error)?; @@ -73,7 +65,7 @@ pub async fn put_file_manifest( for id in push.entries.keys() { if state .database - .get_latest_document_version(&path.vault_id, id, Some(&mut tx)) + .get_latest_document_version(&vault_id, id, Some(&mut tx)) .await .map_err(server_error)? .is_none() @@ -113,10 +105,7 @@ pub async fn put_file_manifest( .await .map_err(|error| server_error(error.into()))?; - state - .broadcasts - .notify_about_vault_update(path.vault_id) - .await; + state.broadcasts.notify_about_vault_update(vault_id).await; Ok(Json(response)) } diff --git a/sync-server/src/server/endpoints/vault_snapshot.rs b/sync-server/src/server/endpoints/vault_snapshot.rs index 1d90f9e0..93e5e4d1 100644 --- a/sync-server/src/server/endpoints/vault_snapshot.rs +++ b/sync-server/src/server/endpoints/vault_snapshot.rs @@ -3,33 +3,23 @@ use axum::{ extract::{Path, State}, }; use log::debug; -use serde::Deserialize; +use super::VaultPath; use crate::{ - app_state::{ - AppState, - database::models::{VaultId, VaultSnapshot}, - }, + app_state::{AppState, database::models::VaultSnapshot}, errors::{SyncServerError, server_error}, - utils::normalize_vault_id::normalize_vault_id, }; -#[derive(Deserialize)] -pub struct VaultSnapshotPath { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, -} - #[axum::debug_handler] pub async fn vault_snapshot( - Path(path): Path, + Path(VaultPath(vault_id)): Path, State(state): State, ) -> Result, SyncServerError> { - debug!("Fetching vault snapshot for vault `{}`", path.vault_id); + debug!("Fetching vault snapshot for vault `{vault_id}`"); state .database - .vault_snapshot(&path.vault_id) + .vault_snapshot(&vault_id) .await .map(Json) .map_err(server_error) diff --git a/sync-server/src/server/endpoints/websocket.rs b/sync-server/src/server/endpoints/websocket.rs index 6d7f4a10..ad1fe8c0 100644 --- a/sync-server/src/server/endpoints/websocket.rs +++ b/sync-server/src/server/endpoints/websocket.rs @@ -9,7 +9,6 @@ use crate::{ }, }, errors::{SyncServerError, client_error, server_error}, - utils::normalize_vault_id::normalize_vault_id, }; use axum::{ extract::{ @@ -20,29 +19,21 @@ use axum::{ }; use futures::StreamExt; use log::{debug, info}; -use serde::Deserialize; use std::time::Duration; use tokio::sync::broadcast::error::RecvError; -#[derive(Deserialize)] -pub struct WebSocketPathParams { - #[serde(deserialize_with = "normalize_vault_id")] - vault_id: VaultId, -} +use super::VaultPath; #[axum::debug_handler] pub async fn websocket_handler( ws: WebSocketUpgrade, - Path(path): Path, + Path(VaultPath(vault_id)): Path, State(state): State, ) -> Result { - debug!( - "Upgrading WebSocket connection for vault `{}`", - path.vault_id - ); + debug!("Upgrading WebSocket connection for vault `{vault_id}`"); Ok(ws.on_upgrade(move |socket| async move { - if let Err(error) = websocket(state, socket, path.vault_id).await { + if let Err(error) = websocket(state, socket, vault_id).await { debug!("WebSocket disconnected: {error}"); } }))