postrust_proxy/saas/handlers/
mod.rs1pub mod auth;
4pub mod domains;
5pub mod routes;
6pub mod upstreams;
7pub mod wellknown;
8
9use crate::admin::api::ApiResponse;
10use crate::error::{ProxyError, ProxyResult};
11use crate::saas::api_keys::ApiKeyService;
12use crate::saas::auth::SaasAuthLayer;
13use crate::saas::manager::DomainManager;
14use crate::saas::verification::DomainVerificationService;
15use crate::tls::CertificateStore;
16use axum::{
17 middleware,
18 routing::{delete, get, post, put},
19 Router,
20};
21use sqlx::PgPool;
22use std::sync::Arc;
23
24#[derive(Clone)]
26pub struct SaasState {
27 pub domain_manager: Arc<DomainManager>,
29 pub api_key_service: Arc<ApiKeyService>,
31 pub auth_layer: Arc<SaasAuthLayer>,
35 pub pool: PgPool,
42
43 pub cert_store: Arc<CertificateStore>,
45}
46
47impl SaasState {
48 pub async fn new(
55 pool: PgPool,
56 jwt_secret: Option<String>,
57 cert_dir: impl AsRef<std::path::Path>,
58 ) -> ProxyResult<Self> {
59 let verification_service = Arc::new(DomainVerificationService::new());
60 let domain_manager = Arc::new(DomainManager::new(pool.clone(), verification_service));
61 let api_key_service = Arc::new(ApiKeyService::new(pool.clone()));
62 let pool_for_handlers = pool.clone();
63 let auth_layer = Arc::new(SaasAuthLayer::new(pool, jwt_secret));
64
65 let cert_store =
66 Arc::new(CertificateStore::new(pool_for_handlers.clone(), cert_dir).await?);
67
68 Ok(Self {
69 domain_manager,
70 api_key_service,
71 auth_layer,
72 pool: pool_for_handlers,
73 cert_store,
74 })
75 }
76}
77
78pub fn saas_router(state: SaasState) -> Router {
80 let auth_layer = state.auth_layer.clone();
81
82 let public_routes = Router::new().route(
84 "/.well-known/postrust-verification/{token}",
85 get(wellknown::handle_verification_challenge),
86 );
87
88 #[cfg(feature = "acme")]
92 let public_routes = public_routes.route(
93 "/.well-known/acme-challenge/{token}",
94 get(wellknown::handle_acme_challenge),
95 );
96
97 let protected_routes = Router::new();
99
100 #[cfg(feature = "acme")]
105 let protected_routes =
106 protected_routes.route("/domains/{id}/ssl/provision", post(domains::provision_ssl));
107
108 let protected_routes = protected_routes
109 .route("/auth/api-keys", post(auth::create_api_key))
111 .route("/auth/api-keys", get(auth::list_api_keys))
112 .route("/auth/api-keys/{id}", delete(auth::revoke_api_key))
113 .route("/tenant/me", get(auth::get_current_tenant))
115 .route("/tenant/usage", get(auth::get_tenant_usage))
116 .route("/domains", get(domains::list_domains))
118 .route("/domains", post(domains::create_domain))
119 .route("/domains/{id}", get(domains::get_domain))
120 .route("/domains/{id}", delete(domains::delete_domain))
121 .route("/domains/{id}/verify", post(domains::verify_domain))
122 .route("/domains/{id}/enable", post(domains::enable_domain))
123 .route("/domains/{id}", put(domains::update_domain))
124 .route("/domains/{id}/disable", post(domains::disable_domain))
125 .route(
126 "/domains/{id}/ssl/upload",
127 post(domains::upload_certificate),
128 )
129 .route("/domains/{domain_id}/routes", get(routes::list_routes))
131 .route("/domains/{domain_id}/routes", post(routes::create_route))
132 .route("/domains/{domain_id}/routes/{id}", get(routes::get_route))
133 .route(
134 "/domains/{domain_id}/routes/{id}",
135 put(routes::update_route),
136 )
137 .route(
138 "/domains/{domain_id}/routes/{id}",
139 delete(routes::delete_route),
140 )
141 .route("/upstreams", get(upstreams::list_upstreams))
143 .route("/upstreams", post(upstreams::create_upstream))
144 .route("/upstreams/{id}", get(upstreams::get_upstream))
145 .route("/upstreams/{id}", put(upstreams::update_upstream))
146 .route("/upstreams/{id}", delete(upstreams::delete_upstream))
147 .route("/upstreams/{id}/backends", post(upstreams::add_backend))
148 .route(
149 "/upstreams/{id}/backends/{backend_id}",
150 delete(upstreams::remove_backend),
151 )
152 .layer(middleware::from_fn_with_state(
153 auth_layer,
154 crate::saas::auth::auth_middleware,
155 ))
156 .with_state(state.clone());
157
158 Router::new()
160 .merge(public_routes.with_state(state.clone()))
161 .nest("/api/v1", protected_routes)
162}
163
164pub fn error_response(error: ProxyError) -> (axum::http::StatusCode, axum::Json<ApiResponse<()>>) {
166 let status = error.status_code();
167 let response = ApiResponse::<()>::error(error.to_string());
168 (status, axum::Json(response))
169}