this repo has no description
1use crate::state::AppState; 2use axum::{ 3 Json, 4 extract::{Query, State}, 5 http::StatusCode, 6 response::{IntoResponse, Response}, 7}; 8use serde::{Deserialize, Serialize}; 9use serde_json::json; 10use sqlx::Row; 11use tracing::error; 12 13#[derive(Deserialize)] 14pub struct GetLatestCommitParams { 15 pub did: String, 16} 17 18#[derive(Serialize)] 19pub struct GetLatestCommitOutput { 20 pub cid: String, 21 pub rev: String, 22} 23 24pub async fn get_latest_commit( 25 State(state): State<AppState>, 26 Query(params): Query<GetLatestCommitParams>, 27) -> Response { 28 let did = params.did.trim(); 29 30 if did.is_empty() { 31 return ( 32 StatusCode::BAD_REQUEST, 33 Json(json!({"error": "InvalidRequest", "message": "did is required"})), 34 ) 35 .into_response(); 36 } 37 38 let result = sqlx::query( 39 r#" 40 SELECT r.repo_root_cid 41 FROM repos r 42 JOIN users u ON r.user_id = u.id 43 WHERE u.did = $1 44 "#, 45 ) 46 .bind(did) 47 .fetch_optional(&state.db) 48 .await; 49 50 match result { 51 Ok(Some(row)) => { 52 let cid: String = row.get("repo_root_cid"); 53 ( 54 StatusCode::OK, 55 Json(GetLatestCommitOutput { 56 cid, 57 rev: chrono::Utc::now().timestamp_millis().to_string(), 58 }), 59 ) 60 .into_response() 61 } 62 Ok(None) => ( 63 StatusCode::NOT_FOUND, 64 Json(json!({"error": "RepoNotFound", "message": "Could not find repo for DID"})), 65 ) 66 .into_response(), 67 Err(e) => { 68 error!("DB error in get_latest_commit: {:?}", e); 69 ( 70 StatusCode::INTERNAL_SERVER_ERROR, 71 Json(json!({"error": "InternalError"})), 72 ) 73 .into_response() 74 } 75 } 76} 77 78#[derive(Deserialize)] 79pub struct ListReposParams { 80 pub limit: Option<i64>, 81 pub cursor: Option<String>, 82} 83 84#[derive(Serialize)] 85#[serde(rename_all = "camelCase")] 86pub struct RepoInfo { 87 pub did: String, 88 pub head: String, 89 pub rev: String, 90 pub active: bool, 91} 92 93#[derive(Serialize)] 94pub struct ListReposOutput { 95 pub cursor: Option<String>, 96 pub repos: Vec<RepoInfo>, 97} 98 99pub async fn list_repos( 100 State(state): State<AppState>, 101 Query(params): Query<ListReposParams>, 102) -> Response { 103 let limit = params.limit.unwrap_or(50).min(1000); 104 let cursor_did = params.cursor.as_deref().unwrap_or(""); 105 106 let result = sqlx::query( 107 r#" 108 SELECT u.did, r.repo_root_cid 109 FROM repos r 110 JOIN users u ON r.user_id = u.id 111 WHERE u.did > $1 112 ORDER BY u.did ASC 113 LIMIT $2 114 "#, 115 ) 116 .bind(cursor_did) 117 .bind(limit + 1) 118 .fetch_all(&state.db) 119 .await; 120 121 match result { 122 Ok(rows) => { 123 let has_more = rows.len() as i64 > limit; 124 let repos: Vec<RepoInfo> = rows 125 .iter() 126 .take(limit as usize) 127 .map(|row| { 128 let did: String = row.get("did"); 129 let head: String = row.get("repo_root_cid"); 130 RepoInfo { 131 did, 132 head, 133 rev: chrono::Utc::now().timestamp_millis().to_string(), 134 active: true, 135 } 136 }) 137 .collect(); 138 139 let next_cursor = if has_more { 140 repos.last().map(|r| r.did.clone()) 141 } else { 142 None 143 }; 144 145 ( 146 StatusCode::OK, 147 Json(ListReposOutput { 148 cursor: next_cursor, 149 repos, 150 }), 151 ) 152 .into_response() 153 } 154 Err(e) => { 155 error!("DB error in list_repos: {:?}", e); 156 ( 157 StatusCode::INTERNAL_SERVER_ERROR, 158 Json(json!({"error": "InternalError"})), 159 ) 160 .into_response() 161 } 162 } 163}