feat: complete Epic 1 production foundation

This commit is contained in:
2026-08-25 01:24:11 +03:00
parent 767428436d
commit 182bde8ac0
298 changed files with 35719 additions and 5299 deletions
+81 -2
View File
@@ -1,13 +1,18 @@
use axum::{
Json,
extract::{Path, Query, State},
http::{
HeaderValue, StatusCode,
header::{CONTENT_DISPOSITION, CONTENT_TYPE},
},
response::{IntoResponse, Response},
};
use serde_json::{Value, json};
use crate::{
error::ApiError,
routes::access::WorkspacePath,
service::{ApprovalsQuery, LogsQuery, UsageRequestQuery},
service::{ApprovalDecisionPayload, ApprovalsQuery, LogsQuery, UsageRequestQuery},
state::AppState,
};
@@ -44,7 +49,49 @@ pub async fn list_logs(
.service
.list_logs(&path.workspace_id.as_str().into(), query)
.await?;
Ok(Json(json!({ "items": items })))
Ok(Json(json!(items)))
}
pub async fn export_logs_csv(
Path(path): Path<WorkspacePath>,
Query(query): Query<LogsQuery>,
State(state): State<AppState>,
) -> Result<Response, ApiError> {
let csv = state
.service
.export_logs_csv(&path.workspace_id.as_str().into(), query)
.await?;
let mut response = (StatusCode::OK, csv).into_response();
response.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("text/csv; charset=utf-8"),
);
response.headers_mut().insert(
CONTENT_DISPOSITION,
HeaderValue::from_static("attachment; filename=\"crank-invocation-history.csv\""),
);
Ok(response)
}
pub async fn export_usage_csv(
Path(path): Path<WorkspacePath>,
Query(query): Query<UsageRequestQuery>,
State(state): State<AppState>,
) -> Result<Response, ApiError> {
let csv = state
.service
.export_usage_csv(&path.workspace_id.as_str().into(), query)
.await?;
let mut response = (StatusCode::OK, csv).into_response();
response.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("text/csv; charset=utf-8"),
);
response.headers_mut().insert(
CONTENT_DISPOSITION,
HeaderValue::from_static("attachment; filename=\"crank-usage.csv\""),
);
Ok(response)
}
pub async fn get_log(
@@ -87,6 +134,38 @@ pub async fn get_approval(
Ok(Json(json!(item)))
}
pub async fn approve_approval(
Path(path): Path<WorkspaceApprovalPath>,
State(state): State<AppState>,
Json(payload): Json<ApprovalDecisionPayload>,
) -> Result<Json<Value>, ApiError> {
let item = state
.service
.approve_approval(
&path.workspace_id.as_str().into(),
&path.approval_id.as_str().into(),
payload,
)
.await?;
Ok(Json(json!(item)))
}
pub async fn deny_approval(
Path(path): Path<WorkspaceApprovalPath>,
State(state): State<AppState>,
Json(payload): Json<ApprovalDecisionPayload>,
) -> Result<Json<Value>, ApiError> {
let item = state
.service
.deny_approval(
&path.workspace_id.as_str().into(),
&path.approval_id.as_str().into(),
payload,
)
.await?;
Ok(Json(json!(item)))
}
pub async fn get_usage(
Path(path): Path<WorkspacePath>,
Query(query): Query<UsageRequestQuery>,