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}