From 55497e1428c4eb8443f368df86eb79f3f117cbdb Mon Sep 17 00:00:00 2001 From: dawn <90008@gaze.systems> Date: Fri, 27 Mar 2026 04:28:43 +0300 Subject: [PATCH] [api] implement getLatestCommit --- README.md | 1 + src/api/xrpc/get_latest_commit.rs | 58 +++++++++++++++++++++++++++++++ src/api/xrpc/mod.rs | 3 ++ 3 files changed, 62 insertions(+) create mode 100644 src/api/xrpc/get_latest_commit.rs diff --git a/README.md b/README.md index 9846523..17fd550 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,7 @@ the following are implemented currently: - `com.atproto.sync.listHosts` - `com.atproto.sync.getRepoStatus` - `com.atproto.sync.listRepos` +- `com.atproto.sync.getLatestCommit` ### systems.gaze.hydrant.* diff --git a/src/api/xrpc/get_latest_commit.rs b/src/api/xrpc/get_latest_commit.rs new file mode 100644 index 0000000..7e6f10e --- /dev/null +++ b/src/api/xrpc/get_latest_commit.rs @@ -0,0 +1,58 @@ +use jacquard_api::com_atproto::sync::get_latest_commit::{ + GetLatestCommitError, GetLatestCommitOutput, GetLatestCommitRequest, GetLatestCommitResponse, +}; +use jacquard_common::{CowStr, types::cid::Cid}; + +use crate::types::RepoStatus; + +use super::*; + +pub async fn handle( + State(hydrant): State, + ExtractXrpc(req): ExtractXrpc, +) -> XrpcResult>, GetLatestCommitError<'static>> { + let nsid = GetLatestCommitResponse::NSID; + + let Some(state) = hydrant + .repos + .get(&req.did) + .state() + .await + .map_err(|e| internal_error(nsid, e))? + else { + return Err(XrpcErrorResponse { + status: StatusCode::NOT_FOUND, + error: XrpcError::Xrpc(GetLatestCommitError::RepoNotFound(None)), + }); + }; + + // return specific errors for inactive account states + let xrpc_err = match &state.status { + RepoStatus::Takendown => Some(GetLatestCommitError::RepoTakendown(None)), + RepoStatus::Suspended => Some(GetLatestCommitError::RepoSuspended(None)), + RepoStatus::Deactivated => Some(GetLatestCommitError::RepoDeactivated(None)), + _ => None, + }; + if let Some(err) = xrpc_err { + return Err(XrpcErrorResponse { + status: StatusCode::FORBIDDEN, + error: XrpcError::Xrpc(err), + }); + } + + // return whatever we last recorded; if we haven't synced at all yet, we have nothing to give + let (Some(data), Some(rev_db)) = (state.data, state.rev) else { + return Err(XrpcErrorResponse { + status: StatusCode::NOT_FOUND, + error: XrpcError::Xrpc(GetLatestCommitError::RepoNotFound(Some(CowStr::Borrowed( + "repo still backfilling", + )))), + }); + }; + + Ok(Json(GetLatestCommitOutput { + cid: Cid::from(data), + rev: rev_db.to_tid(), + extra_data: None, + })) +} diff --git a/src/api/xrpc/mod.rs b/src/api/xrpc/mod.rs index 2b2a4a0..38e517c 100644 --- a/src/api/xrpc/mod.rs +++ b/src/api/xrpc/mod.rs @@ -10,6 +10,7 @@ use jacquard_api::com_atproto::repo::{ list_records::{ListRecordsOutput, ListRecordsRequest, Record as RepoRecord}, }; use jacquard_api::com_atproto::sync::get_host_status::GetHostStatusRequest; +use jacquard_api::com_atproto::sync::get_latest_commit::GetLatestCommitRequest; use jacquard_api::com_atproto::sync::get_repo_status::GetRepoStatusRequest; use jacquard_api::com_atproto::sync::list_hosts::ListHostsRequest; use jacquard_api::com_atproto::sync::list_repos::ListReposRequest; @@ -28,6 +29,7 @@ use std::fmt::Display; mod count_records; mod describe_repo; mod get_host_status; +mod get_latest_commit; mod get_record; mod get_repo_status; mod list_hosts; @@ -42,6 +44,7 @@ pub fn router() -> Router { .route(DescribeRepo::PATH, get(describe_repo::handle)) .route(GetHostStatusRequest::PATH, get(get_host_status::handle)) .route(ListHostsRequest::PATH, get(list_hosts::handle)) + .route(GetLatestCommitRequest::PATH, get(get_latest_commit::handle)) .route(GetRepoStatusRequest::PATH, get(get_repo_status::handle)) .route(ListReposRequest::PATH, get(list_repos::handle)) } -- 2.51.2