Skip to main content

routa_server/api/
workspaces.rs

1use 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 {} not found", id)))?;
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 {} not found", id)))?;
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 {} not found", id)))?;
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    // Verify workspace exists
124    state
125        .workspace_store
126        .get(&id)
127        .await?
128        .ok_or_else(|| ServerError::NotFound(format!("Workspace {} not found", id)))?;
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 {} not found", id)))?;
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}