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 tracing::error;
11
12#[derive(Deserialize)]
13pub struct GetLatestCommitParams {
14 pub did: String,
15}
16
17#[derive(Serialize)]
18pub struct GetLatestCommitOutput {
19 pub cid: String,
20 pub rev: String,
21}
22
23pub async fn get_latest_commit(
24 State(state): State<AppState>,
25 Query(params): Query<GetLatestCommitParams>,
26) -> Response {
27 let did = params.did.trim();
28
29 if did.is_empty() {
30 return (
31 StatusCode::BAD_REQUEST,
32 Json(json!({"error": "InvalidRequest", "message": "did is required"})),
33 )
34 .into_response();
35 }
36
37 let result = sqlx::query!(
38 r#"
39 SELECT r.repo_root_cid
40 FROM repos r
41 JOIN users u ON r.user_id = u.id
42 WHERE u.did = $1
43 "#,
44 did
45 )
46 .fetch_optional(&state.db)
47 .await;
48
49 match result {
50 Ok(Some(row)) => {
51 (
52 StatusCode::OK,
53 Json(GetLatestCommitOutput {
54 cid: row.repo_root_cid,
55 rev: chrono::Utc::now().timestamp_millis().to_string(),
56 }),
57 )
58 .into_response()
59 }
60 Ok(None) => (
61 StatusCode::NOT_FOUND,
62 Json(json!({"error": "RepoNotFound", "message": "Could not find repo for DID"})),
63 )
64 .into_response(),
65 Err(e) => {
66 error!("DB error in get_latest_commit: {:?}", e);
67 (
68 StatusCode::INTERNAL_SERVER_ERROR,
69 Json(json!({"error": "InternalError"})),
70 )
71 .into_response()
72 }
73 }
74}
75
76#[derive(Deserialize)]
77pub struct ListReposParams {
78 pub limit: Option<i64>,
79 pub cursor: Option<String>,
80}
81
82#[derive(Serialize)]
83#[serde(rename_all = "camelCase")]
84pub struct RepoInfo {
85 pub did: String,
86 pub head: String,
87 pub rev: String,
88 pub active: bool,
89}
90
91#[derive(Serialize)]
92pub struct ListReposOutput {
93 pub cursor: Option<String>,
94 pub repos: Vec<RepoInfo>,
95}
96
97pub async fn list_repos(
98 State(state): State<AppState>,
99 Query(params): Query<ListReposParams>,
100) -> Response {
101 let limit = params.limit.unwrap_or(50).min(1000);
102 let cursor_did = params.cursor.as_deref().unwrap_or("");
103
104 let result = sqlx::query!(
105 r#"
106 SELECT u.did, r.repo_root_cid
107 FROM repos r
108 JOIN users u ON r.user_id = u.id
109 WHERE u.did > $1
110 ORDER BY u.did ASC
111 LIMIT $2
112 "#,
113 cursor_did,
114 limit + 1
115 )
116 .fetch_all(&state.db)
117 .await;
118
119 match result {
120 Ok(rows) => {
121 let has_more = rows.len() as i64 > limit;
122 let repos: Vec<RepoInfo> = rows
123 .iter()
124 .take(limit as usize)
125 .map(|row| {
126 RepoInfo {
127 did: row.did.clone(),
128 head: row.repo_root_cid.clone(),
129 rev: chrono::Utc::now().timestamp_millis().to_string(),
130 active: true,
131 }
132 })
133 .collect();
134
135 let next_cursor = if has_more {
136 repos.last().map(|r| r.did.clone())
137 } else {
138 None
139 };
140
141 (
142 StatusCode::OK,
143 Json(ListReposOutput {
144 cursor: next_cursor,
145 repos,
146 }),
147 )
148 .into_response()
149 }
150 Err(e) => {
151 error!("DB error in list_repos: {:?}", e);
152 (
153 StatusCode::INTERNAL_SERVER_ERROR,
154 Json(json!({"error": "InternalError"})),
155 )
156 .into_response()
157 }
158 }
159}
160
161#[derive(Deserialize)]
162pub struct GetRepoStatusParams {
163 pub did: String,
164}
165
166#[derive(Serialize)]
167pub struct GetRepoStatusOutput {
168 pub did: String,
169 pub active: bool,
170 pub rev: Option<String>,
171}
172
173pub async fn get_repo_status(
174 State(state): State<AppState>,
175 Query(params): Query<GetRepoStatusParams>,
176) -> Response {
177 let did = params.did.trim();
178
179 if did.is_empty() {
180 return (
181 StatusCode::BAD_REQUEST,
182 Json(json!({"error": "InvalidRequest", "message": "did is required"})),
183 )
184 .into_response();
185 }
186
187 let result = sqlx::query!(
188 r#"
189 SELECT u.did, r.repo_root_cid
190 FROM users u
191 LEFT JOIN repos r ON u.id = r.user_id
192 WHERE u.did = $1
193 "#,
194 did
195 )
196 .fetch_optional(&state.db)
197 .await;
198
199 match result {
200 Ok(Some(row)) => {
201 let rev = Some(chrono::Utc::now().timestamp_millis().to_string());
202
203 (
204 StatusCode::OK,
205 Json(GetRepoStatusOutput {
206 did: row.did,
207 active: true,
208 rev,
209 }),
210 )
211 .into_response()
212 }
213 Ok(None) => (
214 StatusCode::NOT_FOUND,
215 Json(json!({"error": "RepoNotFound", "message": "Could not find repo for DID"})),
216 )
217 .into_response(),
218 Err(e) => {
219 error!("DB error in get_repo_status: {:?}", e);
220 (
221 StatusCode::INTERNAL_SERVER_ERROR,
222 Json(json!({"error": "InternalError"})),
223 )
224 .into_response()
225 }
226 }
227}