From 4ca55768c56f26502f5ce900f76c9954d572cc3c Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 7 Jun 2025 17:15:16 +0100 Subject: [PATCH] Remove aide --- backend/Cargo.lock | 286 ------------------ backend/sync_server/Cargo.toml | 8 +- .../src/app_state/database/models.rs | 5 +- backend/sync_server/src/server.rs | 87 ++---- .../sync_server/src/server/create_document.rs | 80 +---- .../sync_server/src/server/delete_document.rs | 7 +- .../src/server/fetch_document_version.rs | 10 +- .../server/fetch_document_version_content.rs | 4 +- .../server/fetch_latest_document_version.rs | 10 +- .../src/server/fetch_latest_documents.rs | 13 +- backend/sync_server/src/server/ping.rs | 4 +- backend/sync_server/src/server/requests.rs | 31 +- backend/sync_server/src/server/responses.rs | 11 +- .../sync_server/src/server/update_document.rs | 85 +----- scripts/update-api-types.sh | 6 +- 15 files changed, 85 insertions(+), 562 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index cebf93bf..bab8d80a 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -17,20 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "getrandom 0.2.15", - "once_cell", - "serde", - "version_check", - "zerocopy 0.7.35", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -40,41 +26,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aide" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5678d2978845ddb4bd736a026f467dd652d831e9e6254b0e41b07f7ee7523309" -dependencies = [ - "axum", - "axum-extra", - "bytes", - "cfg-if", - "http", - "indexmap 2.7.0", - "schemars", - "serde", - "serde_json", - "serde_qs", - "thiserror 1.0.69", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "aide-axum-typed-multipart" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8f5c830a08754addfa31fa09e6c183bac8d2ae7bd007131f9eb84fcb87a40e" -dependencies = [ - "aide", - "axum", - "axum_typed_multipart", - "indexmap 2.7.0", - "schemars", -] - [[package]] name = "allocator-api2" version = "0.2.21" @@ -265,26 +216,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum-jsonschema" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcffe29ca1b60172349fea781ec34441d598809bd227ccbb5bf5dc2879cd9c78" -dependencies = [ - "aide", - "async-trait", - "axum", - "http", - "http-body", - "itertools", - "jsonschema", - "schemars", - "serde", - "serde_json", - "serde_path_to_error", - "tracing", -] - [[package]] name = "axum-macros" version = "0.4.2" @@ -368,21 +299,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "2.6.0" @@ -407,12 +323,6 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "bytecount" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" - [[package]] name = "byteorder" version = "1.5.0" @@ -700,12 +610,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - [[package]] name = "either" version = "1.13.0" @@ -768,16 +672,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fancy-regex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" -dependencies = [ - "bit-set", - "regex", -] - [[package]] name = "fastrand" version = "2.2.0" @@ -816,16 +710,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fraction" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" -dependencies = [ - "lazy_static", - "num", -] - [[package]] name = "futures" version = "0.3.31" @@ -943,10 +827,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -1346,24 +1228,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "iso8601" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" -dependencies = [ - "nom", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.14" @@ -1380,34 +1244,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonschema" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978" -dependencies = [ - "ahash", - "anyhow", - "base64 0.21.7", - "bytecount", - "fancy-regex", - "fraction", - "getrandom 0.2.15", - "iso8601", - "itoa", - "memchr", - "num-cmp", - "once_cell", - "parking_lot", - "percent-encoding", - "regex", - "serde", - "serde_json", - "time", - "url", - "uuid", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1521,12 +1357,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1564,16 +1394,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1584,30 +1404,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1625,21 +1421,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "num-cmp" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -1666,17 +1447,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2077,34 +1847,6 @@ dependencies = [ "regex", ] -[[package]] -name = "schemars" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" -dependencies = [ - "bytes", - "chrono", - "dyn-clone", - "indexmap 2.7.0", - "schemars_derive", - "serde", - "serde_json", - "uuid", -] - -[[package]] -name = "schemars_derive" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.90", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -2137,17 +1879,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "serde_json" version = "1.0.140" @@ -2170,19 +1901,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_qs" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6" -dependencies = [ - "axum", - "futures", - "percent-encoding", - "serde", - "thiserror 1.0.69", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2610,12 +2328,9 @@ dependencies = [ name = "sync_server" version = "0.3.15" dependencies = [ - "aide", - "aide-axum-typed-multipart", "anyhow", "axum", "axum-extra", - "axum-jsonschema", "axum_typed_multipart", "bimap", "chrono", @@ -2626,7 +2341,6 @@ dependencies = [ "rand 0.9.0", "regex", "sanitize-filename", - "schemars", "serde", "serde_json", "serde_with", diff --git a/backend/sync_server/Cargo.toml b/backend/sync_server/Cargo.toml index 4aa1a373..3ca2c75a 100644 --- a/backend/sync_server/Cargo.toml +++ b/backend/sync_server/Cargo.toml @@ -18,22 +18,18 @@ log = { version = "0.4.27" } anyhow = { version = "1.0.98", features = ["backtrace"] } axum = { version = "0.7.4", features = ["ws", "macros", "tracing", "multipart"]} axum-extra = { version = "0.9.6", features = ["typed-header"] } -aide-axum-typed-multipart = "0.13.0" axum_typed_multipart = "0.11.0" tower-http = { version = "0.6.1", features = ["cors", "trace", "limit", "timeout"] } +tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["fmt", "env-filter"]} -serde_yaml = "0.9.34" sqlx = { version = "0.8.6", features = ["sqlite", "runtime-tokio", "uuid", "chrono"] } chrono = { version = "0.4.41", features = ["serde"] } -aide = { version = "0.13.5", features = ["axum", "axum-ws", "scalar", "axum-headers"] } -schemars = { version = "0.8.22", features = ["chrono", "uuid1", "bytes"] } -tracing = "0.1.41" rand = "0.9.0" sanitize-filename = "0.6.0" -axum-jsonschema = { version = "0.8.0", features = ["aide"] } regex = "1.11.1" clap = { version = "4.5.38", features = ["derive"] } futures = "0.3.31" +serde_yaml = "0.9.34" serde_json = "1.0.140" clap-verbosity-flag = "3.0.3" bimap = "0.6.3" diff --git a/backend/sync_server/src/app_state/database/models.rs b/backend/sync_server/src/app_state/database/models.rs index a83202b9..bf555d0e 100644 --- a/backend/sync_server/src/app_state/database/models.rs +++ b/backend/sync_server/src/app_state/database/models.rs @@ -1,5 +1,4 @@ use chrono::{DateTime, Utc}; -use schemars::JsonSchema; use serde::Serialize; use sync_lib::bytes_to_base64; use ts_rs::TS; @@ -27,7 +26,7 @@ impl PartialEq for StoredDocumentVersion { fn eq(&self, other: &Self) -> bool { self.vault_update_id == other.vault_update_id } } -#[derive(TS, Debug, Clone, Serialize, JsonSchema)] +#[derive(TS, Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct DocumentVersionWithoutContent { #[ts(as = "i32")] @@ -59,7 +58,7 @@ impl From for DocumentVersionWithoutContent { } } -#[derive(Debug, Clone, Serialize, JsonSchema)] +#[derive(TS, Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct DocumentVersion { pub vault_update_id: VaultUpdateId, diff --git a/backend/sync_server/src/server.rs b/backend/sync_server/src/server.rs index 3b1f7201..efc5f071 100644 --- a/backend/sync_server/src/server.rs +++ b/backend/sync_server/src/server.rs @@ -12,29 +12,20 @@ mod responses; mod update_document; mod websocket; -use std::{ffi::OsString, sync::Arc, time::Duration}; +use std::{ffi::OsString, time::Duration}; -use aide::{ - axum::{ - ApiRouter, - routing::{delete, get, post, put}, - }, - openapi::{Info, OpenApi}, - scalar::Scalar, - transform::TransformOpenApi, -}; use anyhow::{Context as _, Result, anyhow}; use auth::auth_middleware; use axum::{ - Extension, Json, + Router, extract::{DefaultBodyLimit, Request}, http::{self, HeaderValue, Method}, middleware, response::IntoResponse, - routing::IntoMakeService, + routing::{IntoMakeService, delete, get, post, put}, }; use device_id_header::DEVICE_ID_HEADER_NAME; -use log::{error, info}; +use log::info; use tokio::signal; use tower_http::{ LatencyUnit, @@ -51,26 +42,20 @@ use tracing::{Level, info_span}; use crate::{ app_state::AppState, config::server_config::ServerConfig, - errors::{SerializedError, client_error, not_found_error}, + errors::{client_error, not_found_error}, }; pub async fn create_server(config_path: Option) -> Result<()> { - aide::r#gen::on_error(|err| error!("{err}")); - aide::r#gen::extract_schemas(true); - let app_state = AppState::try_new(config_path) .await .context("Failed to initialise app state")?; let server_config = app_state.config.server.clone(); - let mut api = create_open_api(); - let app = ApiRouter::new() + let app = Router::new() .nest("/", get_authed_routes(app_state.clone())) - .api_route("/vaults/:vault_id/ping", get(ping::ping)) + .route("/vaults/:vault_id/ping", get(ping::ping)) .route("/vaults/:vault_id/ws", get(websocket::websocket_handler)) - .route("/", Scalar::new("/api.json").axum_route()) - .route("/api.json", axum::routing::get(serve_api)) .layer(DefaultBodyLimit::disable()) .layer(RequestBodyLimitLayer::new( app_state.config.server.max_body_size_mb * 1024 * 1024, @@ -108,8 +93,6 @@ pub async fn create_server(config_path: Option) -> Result<()> { .on_failure(DefaultOnFailure::new().level(Level::ERROR)), ) .with_state(app_state) - .finish_api_with(&mut api, add_api_docs_error_example) - .layer(Extension(Arc::new(api))) // https://github.com/tamasfe/aide/blob/507f4a8822bc0c13cbda0f589da1e0f4cbcdb812/examples/example-axum/src/main.rs#L39 .fallback(handle_404) .fallback(handle_405) .into_make_service(); @@ -117,67 +100,33 @@ pub async fn create_server(config_path: Option) -> Result<()> { start_server(app, &server_config).await } -async fn serve_api(Extension(api): Extension>) -> impl IntoResponse { Json(api) } - -fn create_open_api() -> OpenApi { - OpenApi { - info: Info { - title: "VaultLink sync server".to_owned(), - summary: Some( - "Simple API for syncing documents between concurrent clients.".to_owned(), - ), - description: Some(include_str!("../README.md").to_owned()), - version: env!("CARGO_PKG_VERSION").to_owned(), - ..Info::default() - }, - ..OpenApi::default() - } -} - -fn add_api_docs_error_example(api: TransformOpenApi<'_>) -> TransformOpenApi<'_> { - api.default_response_with::, _>(|res| { - res.example(SerializedError { - message: "An error has occurred".to_owned(), - causes: vec![], - }) - }) -} - -fn get_authed_routes(app_state: AppState) -> ApiRouter { - ApiRouter::new() - .api_route( +fn get_authed_routes(app_state: AppState) -> Router { + Router::new() + .route( "/vaults/:vault_id/documents", get(fetch_latest_documents::fetch_latest_documents), ) - .api_route( + .route( "/vaults/:vault_id/documents", - post(create_document::create_document_multipart), + post(create_document::create_document), ) - .api_route( - "/vaults/:vault_id/documents/json", - post(create_document::create_document_json), - ) - .api_route( + .route( "/vaults/:vault_id/documents/:document_id", get(fetch_latest_document_version::fetch_latest_document_version), ) - .api_route( + .route( "/vaults/:vault_id/documents/:document_id", - put(update_document::update_document_multipart), + put(update_document::update_document), ) - .api_route( - "/vaults/:vault_id/documents/:document_id/json", - put(update_document::update_document_json), - ) - .api_route( + .route( "/vaults/:vault_id/documents/:document_id/versions/:version_id", put(fetch_document_version::fetch_document_version), ) - .api_route( + .route( "/vaults/:vault_id/documents/:document_id/versions/:version_id/content", put(fetch_document_version_content::fetch_document_version_content), ) - .api_route( + .route( "/vaults/:vault_id/documents/:document_id", delete(delete_document::delete_document), ) diff --git a/backend/sync_server/src/server/create_document.rs b/backend/sync_server/src/server/create_document.rs index 84f16d6a..7018d8cf 100644 --- a/backend/sync_server/src/server/create_document.rs +++ b/backend/sync_server/src/server/create_document.rs @@ -1,33 +1,24 @@ -use aide_axum_typed_multipart::TypedMultipart; use anyhow::Context as _; use axum::{ - Extension, + Extension, Json, extract::{Path, State}, }; use axum_extra::TypedHeader; -use axum_jsonschema::Json; -use schemars::JsonSchema; +use axum_typed_multipart::TypedMultipart; use serde::Deserialize; -use sync_lib::base64_to_bytes; -use super::{ - device_id_header::DeviceIdHeader, - requests::{CreateDocumentVersion, CreateDocumentVersionMultipart}, -}; +use super::{device_id_header::DeviceIdHeader, requests::CreateDocumentVersion}; use crate::{ app_state::{ AppState, - database::models::{ - DocumentId, DocumentVersionWithoutContent, StoredDocumentVersion, VaultId, - }, + database::models::{DocumentVersionWithoutContent, StoredDocumentVersion, VaultId}, }, config::user_config::User, errors::{SyncServerError, client_error, server_error}, utils::{normalize::normalize, sanitize_path::sanitize_path}, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct CreateDocumentPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, @@ -37,63 +28,12 @@ pub struct CreateDocumentPathParams { /// already. If a document with the same path exists, a new version is created /// with their content merged. #[axum::debug_handler] -pub async fn create_document_multipart( +pub async fn create_document( Path(CreateDocumentPathParams { vault_id }): Path, Extension(user): Extension, TypedHeader(device_id): TypedHeader, State(state): State, - TypedMultipart(axum_typed_multipart::TypedMultipart(request)): TypedMultipart< - CreateDocumentVersionMultipart, - >, -) -> Result, SyncServerError> { - internal_create_document( - user, - device_id, - state, - vault_id, - request.document_id, - request.relative_path, - request.content.contents.to_vec(), - ) - .await -} - -/// Create a new document in case a document with the same doesn't exist -/// already. If a document with the same path exists, a new version is created -/// with their content merged. -#[axum::debug_handler] -pub async fn create_document_json( - Path(CreateDocumentPathParams { vault_id }): Path, - Extension(user): Extension, - TypedHeader(device_id): TypedHeader, - State(state): State, - Json(request): Json, -) -> Result, SyncServerError> { - let content_bytes = base64_to_bytes(&request.content_base64) - .context("Failed to decode base64 content in request") - .map_err(client_error)?; - - internal_create_document( - user, - device_id, - state, - vault_id, - request.document_id, - request.relative_path, - content_bytes, - ) - .await -} - -#[allow(clippy::too_many_arguments)] -async fn internal_create_document( - user: User, - device_id: DeviceIdHeader, - state: AppState, - vault_id: VaultId, - document_id: Option, - relative_path: String, - content: Vec, + TypedMultipart(request): TypedMultipart, ) -> Result, SyncServerError> { let mut transaction = state .database @@ -101,7 +41,7 @@ async fn internal_create_document( .await .map_err(server_error)?; - let document_id = match document_id { + let document_id = match request.document_id { Some(document_id) => { let existing_version = state .database @@ -126,13 +66,13 @@ async fn internal_create_document( .await .map_err(server_error)?; - let sanitized_relative_path = sanitize_path(&relative_path); + let sanitized_relative_path = sanitize_path(&request.relative_path); let new_version = StoredDocumentVersion { vault_update_id: last_update_id + 1, document_id, relative_path: sanitized_relative_path, - content, + content: request.content.contents.to_vec(), updated_date: chrono::Utc::now(), is_deleted: false, user_id: user.name, diff --git a/backend/sync_server/src/server/delete_document.rs b/backend/sync_server/src/server/delete_document.rs index d27e97cd..5b7cd6ef 100644 --- a/backend/sync_server/src/server/delete_document.rs +++ b/backend/sync_server/src/server/delete_document.rs @@ -1,11 +1,9 @@ use anyhow::Context as _; use axum::{ - Extension, + Extension, Json, extract::{Path, State}, }; use axum_extra::TypedHeader; -use axum_jsonschema::Json; -use schemars::JsonSchema; use serde::Deserialize; use super::{device_id_header::DeviceIdHeader, requests::DeleteDocumentVersion}; @@ -21,8 +19,7 @@ use crate::{ utils::{normalize::normalize, sanitize_path::sanitize_path}, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct DeleteDocumentPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, diff --git a/backend/sync_server/src/server/fetch_document_version.rs b/backend/sync_server/src/server/fetch_document_version.rs index ee8f6c55..5b571a7b 100644 --- a/backend/sync_server/src/server/fetch_document_version.rs +++ b/backend/sync_server/src/server/fetch_document_version.rs @@ -1,7 +1,8 @@ use anyhow::anyhow; -use axum::extract::{Path, State}; -use axum_jsonschema::Json; -use schemars::JsonSchema; +use axum::{ + Json, + extract::{Path, State}, +}; use serde::Deserialize; use crate::{ @@ -13,8 +14,7 @@ use crate::{ utils::normalize::normalize, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct FetchDocumentVersionPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, diff --git a/backend/sync_server/src/server/fetch_document_version_content.rs b/backend/sync_server/src/server/fetch_document_version_content.rs index 50cacca1..a419b7bf 100644 --- a/backend/sync_server/src/server/fetch_document_version_content.rs +++ b/backend/sync_server/src/server/fetch_document_version_content.rs @@ -3,7 +3,6 @@ use axum::{ body::Bytes, extract::{Path, State}, }; -use schemars::JsonSchema; use serde::Deserialize; use crate::{ @@ -15,8 +14,7 @@ use crate::{ utils::normalize::normalize, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct FetchDocumentVersionContentPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, diff --git a/backend/sync_server/src/server/fetch_latest_document_version.rs b/backend/sync_server/src/server/fetch_latest_document_version.rs index 3b85ed37..07f07860 100644 --- a/backend/sync_server/src/server/fetch_latest_document_version.rs +++ b/backend/sync_server/src/server/fetch_latest_document_version.rs @@ -1,7 +1,8 @@ use anyhow::anyhow; -use axum::extract::{Path, State}; -use axum_jsonschema::Json; -use schemars::JsonSchema; +use axum::{ + Json, + extract::{Path, State}, +}; use serde::Deserialize; use crate::{ @@ -13,8 +14,7 @@ use crate::{ utils::normalize::normalize, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct FetchLatestDocumentVersionPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, diff --git a/backend/sync_server/src/server/fetch_latest_documents.rs b/backend/sync_server/src/server/fetch_latest_documents.rs index e78b7594..6101f55c 100644 --- a/backend/sync_server/src/server/fetch_latest_documents.rs +++ b/backend/sync_server/src/server/fetch_latest_documents.rs @@ -1,6 +1,7 @@ -use axum::extract::{Path, Query, State}; -use axum_jsonschema::Json; -use schemars::JsonSchema; +use axum::{ + Json, + extract::{Path, Query, State}, +}; use serde::Deserialize; use super::responses::FetchLatestDocumentsResponse; @@ -13,15 +14,13 @@ use crate::{ utils::normalize::normalize, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct FetchLatestDocumentsPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, } -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct QueryParams { since_update_id: Option, } diff --git a/backend/sync_server/src/server/ping.rs b/backend/sync_server/src/server/ping.rs index 96a8d82a..620ef0d4 100644 --- a/backend/sync_server/src/server/ping.rs +++ b/backend/sync_server/src/server/ping.rs @@ -6,7 +6,6 @@ use axum_extra::{ TypedHeader, headers::{Authorization, authorization::Bearer}, }; -use schemars::JsonSchema; use serde::Deserialize; use super::{auth::auth, responses::PingResponse}; @@ -16,8 +15,7 @@ use crate::{ utils::normalize::normalize, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct PingPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, diff --git a/backend/sync_server/src/server/requests.rs b/backend/sync_server/src/server/requests.rs index 89820dbe..9d1e478b 100644 --- a/backend/sync_server/src/server/requests.rs +++ b/backend/sync_server/src/server/requests.rs @@ -1,13 +1,12 @@ -use aide_axum_typed_multipart::FieldData; use axum::body::Bytes; -use axum_typed_multipart::TryFromMultipart; -use schemars::JsonSchema; +use axum_typed_multipart::{FieldData, TryFromMultipart}; use serde::{self, Deserialize}; +use ts_rs::TS; use crate::app_state::database::models::{DocumentId, VaultUpdateId}; -#[derive(Debug, Deserialize, JsonSchema)] -#[serde(rename_all = "camelCase")] +#[derive(TS, Debug, TryFromMultipart)] +#[ts(export)] pub struct CreateDocumentVersion { /// The client can decide the document id (if it wishes to) in order /// to help with syncing. If the client does not provide a document id, @@ -15,36 +14,26 @@ pub struct CreateDocumentVersion { /// it must not already exist in the database. pub document_id: Option, pub relative_path: String, - pub content_base64: String, -} -#[derive(Debug, TryFromMultipart, JsonSchema)] -pub struct CreateDocumentVersionMultipart { - pub document_id: Option, - pub relative_path: String, + #[ts(as = "Vec")] #[form_data(limit = "unlimited")] pub content: FieldData, } -#[derive(Debug, Deserialize, JsonSchema)] -#[serde(rename_all = "camelCase")] +#[derive(TS, Debug, TryFromMultipart)] +#[ts(export)] pub struct UpdateDocumentVersion { pub parent_version_id: VaultUpdateId, pub relative_path: String, - pub content_base64: String, -} -#[derive(Debug, TryFromMultipart, JsonSchema)] -#[serde(rename_all = "camelCase")] -pub struct UpdateDocumentVersionMultipart { - pub parent_version_id: VaultUpdateId, - pub relative_path: String, + #[ts(as = "Vec")] #[form_data(limit = "unlimited")] pub content: FieldData, } -#[derive(Debug, Deserialize, JsonSchema)] +#[derive(TS, Debug, Deserialize)] #[serde(rename_all = "camelCase")] +#[ts(export)] pub struct DeleteDocumentVersion { pub relative_path: String, } diff --git a/backend/sync_server/src/server/responses.rs b/backend/sync_server/src/server/responses.rs index 993bc7e7..5cfaa5d5 100644 --- a/backend/sync_server/src/server/responses.rs +++ b/backend/sync_server/src/server/responses.rs @@ -1,13 +1,14 @@ -use schemars::JsonSchema; use serde::{self, Serialize}; +use ts_rs::TS; use crate::app_state::database::models::{ DocumentVersion, DocumentVersionWithoutContent, VaultUpdateId, }; /// Response to a ping request. -#[derive(Debug, Clone, Serialize, JsonSchema)] +#[derive(TS, Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] +#[ts(export)] pub struct PingResponse { /// Semantic version of the server. pub server_version: String, @@ -18,8 +19,9 @@ pub struct PingResponse { } /// Response to a fetch latest documents request. -#[derive(Debug, Clone, Serialize, JsonSchema)] +#[derive(TS, Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] +#[ts(export)] pub struct FetchLatestDocumentsResponse { pub latest_documents: Vec, @@ -28,8 +30,9 @@ pub struct FetchLatestDocumentsResponse { } /// Response to an update document request. -#[derive(Debug, Clone, Serialize, JsonSchema)] +#[derive(TS, Debug, Clone, Serialize)] #[serde(tag = "type")] +#[ts(export)] pub enum DocumentUpdateResponse { /// Returned when the created/updated document's content is the same as was /// sent in the create/update request and thus the response doesn't contain diff --git a/backend/sync_server/src/server/update_document.rs b/backend/sync_server/src/server/update_document.rs index a784dad4..a3ab25e1 100644 --- a/backend/sync_server/src/server/update_document.rs +++ b/backend/sync_server/src/server/update_document.rs @@ -1,33 +1,29 @@ -use aide_axum_typed_multipart::TypedMultipart; use anyhow::{Context as _, anyhow}; use axum::{ - Extension, + Extension, Json, extract::{Path, State}, }; use axum_extra::TypedHeader; -use axum_jsonschema::Json; +use axum_typed_multipart::TypedMultipart; use log::info; -use schemars::JsonSchema; use serde::Deserialize; -use sync_lib::{base64_to_bytes, is_file_type_mergable, merge}; +use sync_lib::{is_file_type_mergable, merge}; use super::{ - device_id_header::DeviceIdHeader, - requests::{UpdateDocumentVersion, UpdateDocumentVersionMultipart}, + device_id_header::DeviceIdHeader, requests::UpdateDocumentVersion, responses::DocumentUpdateResponse, }; use crate::{ app_state::{ AppState, - database::models::{DocumentId, StoredDocumentVersion, VaultId, VaultUpdateId}, + database::models::{DocumentId, StoredDocumentVersion, VaultId}, }, config::user_config::User, - errors::{SyncServerError, client_error, not_found_error, server_error}, + errors::{SyncServerError, not_found_error, server_error}, utils::{dedup_paths::dedup_paths, normalize::normalize, sanitize_path::sanitize_path}, }; -// This is required for aide to infer the path parameter types and names -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize)] pub struct UpdateDocumentPathParams { #[serde(deserialize_with = "normalize")] vault_id: VaultId, @@ -36,7 +32,8 @@ pub struct UpdateDocumentPathParams { } #[axum::debug_handler] -pub async fn update_document_multipart( +#[allow(clippy::too_many_lines)] +pub async fn update_document( Path(UpdateDocumentPathParams { vault_id, document_id, @@ -44,79 +41,25 @@ pub async fn update_document_multipart( Extension(user): Extension, TypedHeader(device_id): TypedHeader, State(state): State, - TypedMultipart(axum_typed_multipart::TypedMultipart(request)): TypedMultipart< - UpdateDocumentVersionMultipart, - >, -) -> Result, SyncServerError> { - internal_update_document( - user, - device_id, - state, - vault_id, - document_id, - request.parent_version_id, - request.relative_path, - request.content.contents.to_vec(), - ) - .await -} - -#[axum::debug_handler] -pub async fn update_document_json( - Path(UpdateDocumentPathParams { - vault_id, - document_id, - }): Path, - Extension(user): Extension, - TypedHeader(device_id): TypedHeader, - State(state): State, - Json(request): Json, -) -> Result, SyncServerError> { - let content_bytes = base64_to_bytes(&request.content_base64) - .context("Failed to decode base64 content in request") - .map_err(client_error)?; - - internal_update_document( - user, - device_id, - state, - vault_id, - document_id, - request.parent_version_id, - request.relative_path, - content_bytes, - ) - .await -} - -#[allow(clippy::too_many_arguments, clippy::too_many_lines)] -async fn internal_update_document( - user: User, - device_id: DeviceIdHeader, - state: AppState, - vault_id: VaultId, - document_id: DocumentId, - parent_version_id: VaultUpdateId, - relative_path: String, - content: Vec, + TypedMultipart(request): TypedMultipart, ) -> Result, SyncServerError> { // No need for a transaction as document versions are immutable let parent_document = state .database - .get_document_version(&vault_id, parent_version_id, None) + .get_document_version(&vault_id, request.parent_version_id, None) .await .map_err(server_error)? .map_or_else( || { Err(not_found_error(anyhow!( "Parent version with id `{}` not found", - parent_version_id + request.parent_version_id ))) }, Ok, )?; - let sanitized_relative_path = sanitize_path(&relative_path); + let sanitized_relative_path = sanitize_path(&request.relative_path); let mut transaction = state .database @@ -156,6 +99,8 @@ async fn internal_update_document( ))); } + let content = request.content.contents.to_vec(); + // Return the latest version if the content and path are the same as the latest // version if content == latest_version.content && sanitized_relative_path == latest_version.relative_path diff --git a/scripts/update-api-types.sh b/scripts/update-api-types.sh index 3c8f788b..aea8a890 100755 --- a/scripts/update-api-types.sh +++ b/scripts/update-api-types.sh @@ -3,13 +3,9 @@ set -e rm -rf backend/sync_server/bindings + cd backend cargo test export_bindings cd - cp -r backend/sync_server/bindings/* frontend/sync-client/src/services/types/ - -./scripts/utils/wait-for-server.sh - -npm install -g openapi-typescript -openapi-typescript http://localhost:3000/api.json --output frontend/sync-client/src/services/types/http-api.ts