tiny_proxy/api/
endpoints.rs1use anyhow::Result;
4use bytes::Bytes;
5use http_body::Body;
6use http_body_util::{BodyExt, Full};
7use hyper::{Request, Response};
8use std::sync::Arc;
9use tracing::info;
10
11use crate::config::Config;
12
13pub async fn handle_get_config<B>(
15 _req: Request<B>,
16 config: Arc<tokio::sync::RwLock<Config>>,
17) -> Result<Response<Full<Bytes>>>
18where
19 B: Body,
20{
21 let config = config.read().await;
22
23 let json = serde_json::to_string_pretty(&*config)
24 .unwrap_or_else(|_| r#"{"error": "Failed to serialize config"}"#.to_string());
25
26 info!("GET /config - Returning configuration");
27
28 let response = Response::builder()
29 .status(200)
30 .header("Content-Type", "application/json")
31 .body(Full::new(Bytes::from(json)))
32 .unwrap();
33
34 Ok(response)
35}
36
37pub async fn handle_post_config<B>(
39 req: Request<B>,
40 _config: Arc<tokio::sync::RwLock<Config>>,
41) -> Result<Response<Full<Bytes>>>
42where
43 B: Body,
44 B::Error: std::fmt::Display,
45{
46 let body_bytes = match BodyExt::collect(req.into_body()).await {
47 Ok(collected) => collected.to_bytes(),
48 Err(e) => {
49 let response = Response::builder()
50 .status(400)
51 .body(Full::new(Bytes::from(format!(
52 "Failed to read request body: {}",
53 e
54 ))))
55 .unwrap();
56 return Ok(response);
57 }
58 };
59
60 let body_str = match std::str::from_utf8(&body_bytes) {
61 Ok(s) => s,
62 Err(_) => {
63 let response = Response::builder()
64 .status(400)
65 .body(Full::new(Bytes::from(
66 "Invalid UTF-8 in request body".to_string(),
67 )))
68 .unwrap();
69 return Ok(response);
70 }
71 };
72
73 info!("POST /config - Updating configuration");
74 info!("New config: {}", body_str);
75
76 let response = Response::builder()
77 .status(200)
78 .header("Content-Type", "application/json")
79 .body(Full::new(Bytes::from(
80 r#"{"status": "success", "message": "Configuration updated"}"#.to_string(),
81 )))
82 .unwrap();
83
84 Ok(response)
85}
86
87pub async fn handle_health_check<B>(_req: Request<B>) -> Result<Response<Full<Bytes>>>
89where
90 B: Body,
91{
92 info!("GET /health - Health check");
93
94 let health = serde_json::json!({
95 "status": "healthy",
96 "service": "tiny-proxy",
97 "version": env!("CARGO_PKG_VERSION")
98 });
99
100 let response = Response::builder()
101 .status(200)
102 .header("Content-Type", "application/json")
103 .body(Full::new(Bytes::from(
104 serde_json::to_string(&health).unwrap(),
105 )))
106 .unwrap();
107
108 Ok(response)
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use http_body_util::Empty;
115 use std::collections::HashMap;
116 use std::sync::Arc;
117
118 #[tokio::test]
119 async fn test_handle_health_check() {
120 let req: Request<Empty<Bytes>> = Request::builder().body(Empty::new()).unwrap();
121
122 let response = handle_health_check(req).await.unwrap();
123 assert_eq!(response.status(), 200);
124 }
125
126 #[tokio::test]
127 async fn test_handle_get_config() {
128 let config = Arc::new(tokio::sync::RwLock::new(Config {
129 sites: HashMap::new(),
130 }));
131
132 let req: Request<Empty<Bytes>> = Request::builder().body(Empty::new()).unwrap();
133
134 let response = handle_get_config(req, config).await.unwrap();
135 assert_eq!(response.status(), 200);
136 }
137
138 #[tokio::test]
139 async fn test_handle_post_config() {
140 let config = Arc::new(tokio::sync::RwLock::new(Config {
141 sites: HashMap::new(),
142 }));
143
144 let req: Request<Empty<Bytes>> = Request::builder().body(Empty::new()).unwrap();
145
146 let response = handle_post_config(req, config).await.unwrap();
147 assert_eq!(response.status(), 200);
148 }
149}