Skip to main content

postrust_proxy/saas/handlers/
upstreams.rs

1//! Upstream API handlers.
2
3use crate::admin::api::ApiResponse;
4use crate::saas::auth::Auth;
5use crate::saas::handlers::{error_response, SaasState};
6use crate::saas::types::{CreateBackendRequest, CreateUpstreamRequest, UpdateUpstreamRequest};
7use axum::{
8    extract::{Path, State},
9    http::StatusCode,
10    response::IntoResponse,
11    Json,
12};
13use uuid::Uuid;
14
15/// List upstreams for the authenticated tenant.
16pub async fn list_upstreams(State(state): State<SaasState>, Auth(auth): Auth) -> impl IntoResponse {
17    match state.domain_manager.list_upstreams(auth.tenant_id).await {
18        Ok(upstreams) => Json(ApiResponse::success(upstreams)).into_response(),
19        Err(e) => error_response(e).into_response(),
20    }
21}
22
23/// Create a new upstream.
24pub async fn create_upstream(
25    State(state): State<SaasState>,
26    Auth(auth): Auth,
27    Json(req): Json<CreateUpstreamRequest>,
28) -> impl IntoResponse {
29    if !auth.can_write("upstreams") {
30        return (
31            StatusCode::FORBIDDEN,
32            Json(ApiResponse::<()>::error("Insufficient permissions")),
33        )
34            .into_response();
35    }
36
37    match state
38        .domain_manager
39        .create_upstream(auth.tenant_id, req)
40        .await
41    {
42        Ok(upstream) => (StatusCode::CREATED, Json(ApiResponse::success(upstream))).into_response(),
43        Err(e) => error_response(e).into_response(),
44    }
45}
46
47/// Get an upstream by ID.
48pub async fn get_upstream(
49    State(state): State<SaasState>,
50    Auth(auth): Auth,
51    Path(id): Path<Uuid>,
52) -> impl IntoResponse {
53    match state.domain_manager.get_upstream(id, auth.tenant_id).await {
54        Ok(Some(upstream)) => Json(ApiResponse::success(upstream)).into_response(),
55        Ok(None) => (
56            StatusCode::NOT_FOUND,
57            Json(ApiResponse::<()>::error("Upstream not found")),
58        )
59            .into_response(),
60        Err(e) => error_response(e).into_response(),
61    }
62}
63
64/// Update an upstream.
65pub async fn update_upstream(
66    State(state): State<SaasState>,
67    Auth(auth): Auth,
68    Path(id): Path<Uuid>,
69    Json(req): Json<UpdateUpstreamRequest>,
70) -> impl IntoResponse {
71    if !auth.can_write("upstreams") {
72        return (
73            StatusCode::FORBIDDEN,
74            Json(ApiResponse::<()>::error("Insufficient permissions")),
75        )
76            .into_response();
77    }
78
79    match state
80        .domain_manager
81        .update_upstream(id, auth.tenant_id, req)
82        .await
83    {
84        Ok(Some(upstream)) => Json(ApiResponse::success(upstream)).into_response(),
85        Ok(None) => (
86            StatusCode::NOT_FOUND,
87            Json(ApiResponse::<()>::error("Upstream not found")),
88        )
89            .into_response(),
90        Err(e) => error_response(e).into_response(),
91    }
92}
93
94/// Delete an upstream.
95pub async fn delete_upstream(
96    State(state): State<SaasState>,
97    Auth(auth): Auth,
98    Path(id): Path<Uuid>,
99) -> impl IntoResponse {
100    if !auth.can_write("upstreams") {
101        return (
102            StatusCode::FORBIDDEN,
103            Json(ApiResponse::<()>::error("Insufficient permissions")),
104        )
105            .into_response();
106    }
107
108    match state
109        .domain_manager
110        .delete_upstream(id, auth.tenant_id)
111        .await
112    {
113        Ok(true) => Json(ApiResponse::success(())).into_response(),
114        Ok(false) => (
115            StatusCode::NOT_FOUND,
116            Json(ApiResponse::<()>::error("Upstream not found")),
117        )
118            .into_response(),
119        Err(e) => error_response(e).into_response(),
120    }
121}
122
123/// Add a backend to an upstream.
124pub async fn add_backend(
125    State(state): State<SaasState>,
126    Auth(auth): Auth,
127    Path(upstream_id): Path<Uuid>,
128    Json(req): Json<CreateBackendRequest>,
129) -> impl IntoResponse {
130    if !auth.can_write("upstreams") {
131        return (
132            StatusCode::FORBIDDEN,
133            Json(ApiResponse::<()>::error("Insufficient permissions")),
134        )
135            .into_response();
136    }
137
138    match state
139        .domain_manager
140        .add_backend(upstream_id, auth.tenant_id, req)
141        .await
142    {
143        Ok(backend) => (StatusCode::CREATED, Json(ApiResponse::success(backend))).into_response(),
144        Err(e) => error_response(e).into_response(),
145    }
146}
147
148/// Remove a backend from an upstream.
149pub async fn remove_backend(
150    State(state): State<SaasState>,
151    Auth(auth): Auth,
152    Path((upstream_id, backend_id)): Path<(Uuid, Uuid)>,
153) -> impl IntoResponse {
154    if !auth.can_write("upstreams") {
155        return (
156            StatusCode::FORBIDDEN,
157            Json(ApiResponse::<()>::error("Insufficient permissions")),
158        )
159            .into_response();
160    }
161
162    match state
163        .domain_manager
164        .remove_backend(backend_id, upstream_id, auth.tenant_id)
165        .await
166    {
167        Ok(true) => Json(ApiResponse::success(())).into_response(),
168        Ok(false) => (
169            StatusCode::NOT_FOUND,
170            Json(ApiResponse::<()>::error("Backend not found")),
171        )
172            .into_response(),
173        Err(e) => error_response(e).into_response(),
174    }
175}