1use crate::config::SiteConfig;
2use pingora::http::ResponseHeader;
3use pingora::prelude::*;
4
5pub struct ApiHandler {
6 }
8
9impl ApiHandler {
10 pub fn new() -> Self {
11 Self {}
12 }
13
14 pub async fn handle(&self, session: &mut Session, site: Option<&SiteConfig>) -> Result<()> {
15 let path = session.req_header().uri.path().to_string();
16 let method = session.req_header().method.as_str();
17
18 match (method, path.as_str()) {
19 ("GET", "/api/sites") => self.handle_sites_info(session, site).await,
20 ("GET", "/api/ssl/certificates") => self.handle_ssl_certificates(session, site).await,
21 ("POST", path) if path.starts_with("/api/ssl/certificates/") => {
22 self.handle_ssl_certificate_request(session, site, path)
23 .await
24 }
25 ("GET", "/api/ssl/status") => self.handle_ssl_status(session, site).await,
26 ("GET", "/api/config") => self.handle_config_info(session, site).await,
27 ("POST", "/api/config/reload") => self.handle_config_reload(session, site).await,
28 _ => self.handle_not_found(session, site).await,
29 }
30 }
31
32 async fn handle_sites_info(
33 &self,
34 session: &mut Session,
35 _site: Option<&SiteConfig>,
36 ) -> Result<()> {
37 let response = serde_json::json!({
40 "sites": [],
41 "total_sites": 0,
42 "message": "Sites information endpoint"
43 });
44
45 self.send_json_response(session, 200, &response).await
46 }
47
48 async fn handle_ssl_certificates(
49 &self,
50 session: &mut Session,
51 _site: Option<&SiteConfig>,
52 ) -> Result<()> {
53 let response = serde_json::json!({
56 "certificates": [],
57 "total_certificates": 0,
58 "message": "SSL certificates information endpoint"
59 });
60
61 self.send_json_response(session, 200, &response).await
62 }
63
64 async fn handle_ssl_certificate_request(
65 &self,
66 session: &mut Session,
67 _site: Option<&SiteConfig>,
68 path: &str,
69 ) -> Result<()> {
70 let domain = path
72 .strip_prefix("/api/ssl/certificates/")
73 .unwrap_or("unknown");
74
75 let response = serde_json::json!({
76 "message": format!("Certificate request for domain: {}", domain),
77 "domain": domain,
78 "status": "not_implemented"
79 });
80
81 self.send_json_response(session, 501, &response).await
82 }
83
84 async fn handle_ssl_status(
85 &self,
86 session: &mut Session,
87 _site: Option<&SiteConfig>,
88 ) -> Result<()> {
89 let response = serde_json::json!({
90 "ssl_enabled": false,
91 "auto_cert": false,
92 "certificates": [],
93 "renewal_status": "unknown",
94 "message": "SSL status endpoint"
95 });
96
97 self.send_json_response(session, 200, &response).await
98 }
99
100 async fn handle_config_info(
101 &self,
102 session: &mut Session,
103 _site: Option<&SiteConfig>,
104 ) -> Result<()> {
105 let response = serde_json::json!({
106 "server": {
107 "name": "bws-web-server",
108 "version": env!("CARGO_PKG_VERSION")
109 },
110 "message": "Configuration information endpoint"
111 });
112
113 self.send_json_response(session, 200, &response).await
114 }
115
116 async fn handle_config_reload(
117 &self,
118 session: &mut Session,
119 _site: Option<&SiteConfig>,
120 ) -> Result<()> {
121 let response = serde_json::json!({
122 "message": "Configuration reload endpoint",
123 "status": "not_implemented"
124 });
125
126 self.send_json_response(session, 501, &response).await
127 }
128
129 async fn handle_not_found(
130 &self,
131 session: &mut Session,
132 _site: Option<&SiteConfig>,
133 ) -> Result<()> {
134 let response = serde_json::json!({
135 "error": "API endpoint not found",
136 "message": "The requested API endpoint does not exist",
137 "available_endpoints": [
138 "GET /api/sites",
139 "GET /api/ssl/certificates",
140 "POST /api/ssl/certificates/{domain}",
141 "GET /api/ssl/status",
142 "GET /api/config",
143 "POST /api/config/reload"
144 ]
145 });
146
147 self.send_json_response(session, 404, &response).await
148 }
149
150 async fn send_json_response(
151 &self,
152 session: &mut Session,
153 status: u16,
154 data: &serde_json::Value,
155 ) -> Result<()> {
156 let response_body = data.to_string();
157 let response_bytes = response_body.into_bytes();
158
159 let mut header = ResponseHeader::build(status, Some(4))?;
160 header.insert_header("Content-Type", "application/json; charset=utf-8")?;
161 header.insert_header("Content-Length", response_bytes.len().to_string())?;
162 header.insert_header("Cache-Control", "no-cache, no-store, must-revalidate")?;
163 header.insert_header("Pragma", "no-cache")?;
164
165 session
166 .write_response_header(Box::new(header), false)
167 .await?;
168 session
169 .write_response_body(Some(response_bytes.into()), true)
170 .await?;
171
172 Ok(())
173 }
174}
175
176impl Default for ApiHandler {
177 fn default() -> Self {
178 Self::new()
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_api_handler_creation() {
188 let handler = ApiHandler::new();
189 assert_eq!(
191 std::mem::size_of_val(&handler),
192 std::mem::size_of::<ApiHandler>()
193 );
194 }
195}