routa_server/api/
workspaces.rs1use axum::{
2 extract::{Query, State},
3 routing::{get, post},
4 Json, Router,
5};
6use serde::Deserialize;
7use std::collections::HashMap;
8
9use crate::error::ServerError;
10use crate::models::workspace::{Workspace, WorkspaceStatus};
11use crate::state::AppState;
12
13pub fn router() -> Router<AppState> {
14 Router::new()
15 .route("/", get(list_workspaces).post(create_workspace))
16 .route(
17 "/{id}",
18 get(get_workspace)
19 .delete(delete_workspace)
20 .patch(update_workspace),
21 )
22 .route("/{id}/archive", post(archive_workspace))
23}
24
25#[derive(Debug, Deserialize)]
26struct ListWorkspacesQuery {
27 status: Option<WorkspaceStatus>,
28}
29
30async fn list_workspaces(
31 State(state): State<AppState>,
32 Query(query): Query<ListWorkspacesQuery>,
33) -> Result<Json<serde_json::Value>, ServerError> {
34 let workspaces = if let Some(status) = query.status {
35 state.workspace_store.list_by_status(status).await?
36 } else {
37 state.workspace_store.list().await?
38 };
39 Ok(Json(serde_json::json!({ "workspaces": workspaces })))
40}
41
42async fn get_workspace(
43 State(state): State<AppState>,
44 axum::extract::Path(id): axum::extract::Path<String>,
45) -> Result<Json<serde_json::Value>, ServerError> {
46 let workspace = state
47 .workspace_store
48 .get(&id)
49 .await?
50 .ok_or_else(|| ServerError::NotFound(format!("Workspace {id} not found")))?;
51 let codebases = state
52 .codebase_store
53 .list_by_workspace(&id)
54 .await
55 .unwrap_or_default();
56 Ok(Json(
57 serde_json::json!({ "workspace": workspace, "codebases": codebases }),
58 ))
59}
60
61#[derive(Debug, Deserialize)]
62#[serde(rename_all = "camelCase")]
63struct CreateWorkspaceRequest {
64 title: Option<String>,
65 metadata: Option<HashMap<String, String>>,
66}
67
68async fn create_workspace(
69 State(state): State<AppState>,
70 Json(body): Json<CreateWorkspaceRequest>,
71) -> Result<Json<serde_json::Value>, ServerError> {
72 let title = body
73 .title
74 .map(|value| value.trim().to_string())
75 .filter(|value| !value.is_empty())
76 .ok_or_else(|| ServerError::BadRequest("title is required".to_string()))?;
77 let ws = Workspace::new(uuid::Uuid::new_v4().to_string(), title, body.metadata);
78
79 state.workspace_store.save(&ws).await?;
80 Ok(Json(serde_json::json!({ "workspace": ws })))
81}
82
83#[derive(Debug, Deserialize)]
84#[serde(rename_all = "camelCase")]
85struct UpdateWorkspaceRequest {
86 title: Option<String>,
87 metadata: Option<HashMap<String, String>>,
88}
89
90async fn update_workspace(
91 State(state): State<AppState>,
92 axum::extract::Path(id): axum::extract::Path<String>,
93 Json(body): Json<UpdateWorkspaceRequest>,
94) -> Result<Json<serde_json::Value>, ServerError> {
95 let mut ws = state
96 .workspace_store
97 .get(&id)
98 .await?
99 .ok_or_else(|| ServerError::NotFound(format!("Workspace {id} not found")))?;
100
101 if let Some(title) = &body.title {
102 state.workspace_store.update_title(&id, title).await?;
103 }
104
105 if let Some(metadata) = body.metadata {
106 ws.metadata.extend(metadata);
107 state.workspace_store.save(&ws).await?;
108 }
109
110 let ws = state
111 .workspace_store
112 .get(&id)
113 .await?
114 .ok_or_else(|| ServerError::NotFound(format!("Workspace {id} not found")))?;
115
116 Ok(Json(serde_json::json!({ "workspace": ws })))
117}
118
119async fn archive_workspace(
120 State(state): State<AppState>,
121 axum::extract::Path(id): axum::extract::Path<String>,
122) -> Result<Json<serde_json::Value>, ServerError> {
123 state
125 .workspace_store
126 .get(&id)
127 .await?
128 .ok_or_else(|| ServerError::NotFound(format!("Workspace {id} not found")))?;
129
130 state.workspace_store.update_status(&id, "archived").await?;
131
132 let ws = state
133 .workspace_store
134 .get(&id)
135 .await?
136 .ok_or_else(|| ServerError::NotFound(format!("Workspace {id} not found")))?;
137
138 Ok(Json(serde_json::json!({ "workspace": ws })))
139}
140
141async fn delete_workspace(
142 State(state): State<AppState>,
143 axum::extract::Path(id): axum::extract::Path<String>,
144) -> Result<Json<serde_json::Value>, ServerError> {
145 state.workspace_store.delete(&id).await?;
146 Ok(Json(serde_json::json!({ "deleted": true })))
147}