Clean up diff

This commit is contained in:
Andras Schmelczer 2026-05-09 15:16:16 +01:00
parent 3a20a7c2f8
commit 6d40097bcd
11 changed files with 52 additions and 164 deletions

View file

@ -59,6 +59,26 @@ export class DeterministicAgent extends debugging.InMemoryFileSystem {
this.data.settings = { ...initialSettings };
}
private static isCreateDocumentRequest(
input: RequestInfo | URL,
init: RequestInit | undefined
): boolean {
const method =
init?.method ??
(typeof Request !== "undefined" && input instanceof Request
? input.method
: "GET");
if (method.toUpperCase() !== "POST") {
return false;
}
const url =
input instanceof URL
? input
: new URL(typeof input === "string" ? input : input.url);
return /\/documents\/?$/.test(url.pathname);
}
public async init(
fetchImplementation: typeof globalThis.fetch
): Promise<void> {
@ -118,7 +138,7 @@ export class DeterministicAgent extends debugging.InMemoryFileSystem {
this.nextCreateResponseDrop === undefined,
`Client ${this.clientId} already has a create response drop armed`
);
let resolveDropped!: () => void;
let resolveDropped: () => void = () => {};
const dropped = new Promise<void>((resolve) => {
resolveDropped = resolve;
});
@ -461,23 +481,4 @@ export class DeterministicAgent extends debugging.InMemoryFileSystem {
};
}
private static isCreateDocumentRequest(
input: RequestInfo | URL,
init: RequestInit | undefined
): boolean {
const method =
init?.method ??
(typeof Request !== "undefined" && input instanceof Request
? input.method
: "GET");
if (method.toUpperCase() !== "POST") {
return false;
}
const url =
input instanceof URL
? input
: new URL(typeof input === "string" ? input : input.url);
return /\/documents\/?$/.test(url.pathname);
}
}

View file

@ -8,11 +8,9 @@ cd sync-server
cargo test export_bindings
cd -
# Both target directories contain only generated bindings — wipe and copy
# Wipe and copy generated bindings into the consuming workspace
rm -f frontend/sync-client/src/services/types/*.ts
rm -f frontend/history-ui/src/lib/types/*.ts
cp -r sync-server/bindings/* frontend/sync-client/src/services/types/
cp -r sync-server/bindings/* frontend/history-ui/src/lib/types/
cd frontend
npm run lint

View file

@ -51,7 +51,8 @@ missing_debug_implementations = "warn"
[lints.clippy]
await_holding_lock = "warn"
dbg_macro = "warn"
empty_enum = "warn"
disallowed_macros = { level = "deny", priority = 1 }
empty_enums = "warn"
enum_glob_use = "warn"
exit = "warn"
filter_map_next = "warn"

4
sync-server/clippy.toml Normal file
View file

@ -0,0 +1,4 @@
disallowed-macros = [
{ path = "std::eprintln", reason = "use log::info! or log::warn! instead" },
{ path = "std::println", reason = "use log::info! or log::warn! instead" },
]

View file

@ -11,7 +11,7 @@ use crate::{
AppState,
database::models::{DocumentId, DocumentVersion, VaultId, VaultUpdateId},
},
errors::{SyncServerError, client_error, not_found_error, server_error},
errors::{SyncServerError, not_found_error, server_error},
utils::normalize::normalize,
};
@ -52,7 +52,7 @@ pub async fn fetch_document_version(
)?;
if result.document_id != document_id {
return Err(client_error(anyhow!(
return Err(not_found_error(anyhow!(
"Document with document id `{document_id}` does not have a version with id \
`{vault_update_id}`",
)));

View file

@ -11,7 +11,7 @@ use crate::{
AppState,
database::models::{DocumentId, VaultId, VaultUpdateId},
},
errors::{SyncServerError, client_error, not_found_error, server_error},
errors::{SyncServerError, not_found_error, server_error},
utils::normalize::normalize,
};
@ -52,7 +52,7 @@ pub async fn fetch_document_version_content(
)?;
if result.document_id != document_id {
return Err(client_error(anyhow!(
return Err(not_found_error(anyhow!(
"Document with document id `{document_id}` does not have a version with id \
`{vault_update_id}`",
)));

View file

@ -1,51 +0,0 @@
use anyhow::anyhow;
use axum::{
Json,
extract::{Path, State},
};
use log::debug;
use serde::Deserialize;
use crate::{
app_state::{
AppState,
database::models::{DocumentId, DocumentVersion, VaultId},
},
errors::{SyncServerError, not_found_error, server_error},
utils::normalize::normalize,
};
#[derive(Deserialize)]
pub struct FetchLatestDocumentVersionPathParams {
#[serde(deserialize_with = "normalize")]
vault_id: VaultId,
document_id: DocumentId,
}
#[axum::debug_handler]
pub async fn fetch_latest_document_version(
Path(FetchLatestDocumentVersionPathParams {
vault_id,
document_id,
}): Path<FetchLatestDocumentVersionPathParams>,
State(state): State<AppState>,
) -> Result<Json<DocumentVersion>, SyncServerError> {
debug!("Fetching latest document version for document `{document_id}` in vault `{vault_id}`");
let latest_version = state
.database
.get_latest_document(&vault_id, &document_id, None)
.await
.map_err(server_error)?
.map_or_else(
|| {
Err(not_found_error(anyhow!(
"Document with id `{document_id}` not found",
)))
},
Ok,
)?;
Ok(Json(latest_version.into()))
}

View file

@ -1,59 +0,0 @@
use axum::{
Json,
extract::{Path, Query, State},
};
use log::debug;
use serde::Deserialize;
use super::responses::FetchLatestDocumentsResponse;
use crate::{
app_state::{
AppState,
database::models::{VaultId, VaultUpdateId},
},
errors::{SyncServerError, server_error},
utils::normalize::normalize,
};
#[derive(Deserialize)]
pub struct FetchLatestDocumentsPathParams {
#[serde(deserialize_with = "normalize")]
vault_id: VaultId,
}
#[derive(Deserialize)]
pub struct QueryParams {
since_update_id: Option<VaultUpdateId>,
}
#[axum::debug_handler]
pub async fn fetch_latest_documents(
Path(FetchLatestDocumentsPathParams { vault_id }): Path<FetchLatestDocumentsPathParams>,
Query(QueryParams { since_update_id }): Query<QueryParams>,
State(state): State<AppState>,
) -> Result<Json<FetchLatestDocumentsResponse>, SyncServerError> {
debug!("Fetching latest documents in vault `{vault_id}` since update ID `{since_update_id:?}`");
let documents = if let Some(since_update_id) = since_update_id {
state
.database
.get_latest_documents_since(&vault_id, since_update_id, None, None)
.await
.map_err(server_error)
} else {
state
.database
.get_latest_documents(&vault_id, None, None)
.await
.map_err(server_error)
}?;
Ok(Json(FetchLatestDocumentsResponse {
last_update_id: documents
.iter()
.map(|doc| doc.vault_update_id)
.max()
.unwrap_or(since_update_id.unwrap_or(0)),
latest_documents: documents,
}))
}

View file

@ -9,7 +9,7 @@ use axum_extra::{
use log::debug;
use serde::Deserialize;
use super::{auth::auth, responses::PingResponse};
use super::{auth::authenticate_for_vault, responses::PingResponse};
use crate::{
app_state::{AppState, database::models::VaultId},
consts::SUPPORTED_API_VERSION,
@ -31,8 +31,9 @@ pub async fn ping(
) -> Result<Json<PingResponse>, SyncServerError> {
debug!("Pinging vault `{vault_id}`");
let is_authenticated = maybe_auth_header
.is_some_and(|auth_header| auth(&state, auth_header.token(), &vault_id).is_ok());
let is_authenticated = maybe_auth_header.is_some_and(|auth_header| {
authenticate_for_vault(&state, auth_header.token(), &vault_id).is_ok()
});
Ok(Json(PingResponse {
server_version: env!("CARGO_PKG_VERSION").to_owned(),

View file

@ -1,9 +1,7 @@
use serde::{self, Serialize};
use ts_rs::TS;
use crate::app_state::database::models::{
DocumentVersion, DocumentVersionWithoutContent, VaultUpdateId,
};
use crate::app_state::database::models::{DocumentVersion, DocumentVersionWithoutContent};
/// Response to a ping request.
#[derive(TS, Debug, Clone, Serialize)]
@ -25,17 +23,6 @@ pub struct PingResponse {
pub supported_api_version: u32,
}
/// Response to a fetch latest documents request.
#[derive(TS, Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
pub struct FetchLatestDocumentsResponse {
pub latest_documents: Vec<DocumentVersionWithoutContent>,
/// The update ID of the latest document in the response.
pub last_update_id: VaultUpdateId,
}
/// Response to a create/update document request.
#[derive(TS, Debug, Clone, Serialize)]
#[serde(tag = "type")]

View file

@ -2,11 +2,12 @@ use std::{
fs::{self, OpenOptions},
io::{self, Write},
path::{Path, PathBuf},
sync::{Arc, Mutex},
sync::{Arc, Mutex, MutexGuard},
time::{Duration, SystemTime, UNIX_EPOCH},
};
use chrono::NaiveDateTime;
use log::warn;
use tracing_subscriber::fmt::MakeWriter;
#[derive(Clone)]
@ -93,6 +94,17 @@ impl RotatingFileWriter {
SystemTime::now() >= inner.next_rotation_time
}
fn lock_inner(&self) -> MutexGuard<'_, RotatingFileWriterInner> {
match self.inner.lock() {
Ok(inner) => inner,
Err(poisoned) => {
warn!("RotatingFileWriter mutex was poisoned, recovering");
self.inner.clear_poison();
poisoned.into_inner()
}
}
}
fn open_or_create_log_file(inner: &mut RotatingFileWriterInner) -> io::Result<()> {
// If we haven't reached rotation time and there's an existing log file, reuse it
if !Self::should_rotate(inner)
@ -132,10 +144,7 @@ impl RotatingFileWriter {
impl Write for RotatingFileWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut inner = self.inner.lock().unwrap_or_else(|poisoned| {
eprintln!("RotatingFileWriter mutex was poisoned, recovering");
poisoned.into_inner()
});
let mut inner = self.lock_inner();
// Reset file handle after poison recovery so the next branch
// re-opens a valid file rather than writing to a potentially
@ -154,10 +163,7 @@ impl Write for RotatingFileWriter {
}
fn flush(&mut self) -> io::Result<()> {
let mut inner = self.inner.lock().unwrap_or_else(|poisoned| {
eprintln!("RotatingFileWriter mutex was poisoned, recovering");
poisoned.into_inner()
});
let mut inner = self.lock_inner();
if let Some(ref mut file) = inner.current_file {
file.flush()
} else {