1use anyhow::Result;
2use log::info;
3use std::sync::Arc;
4use tokio::sync::RwLock;
5use warp::Filter;
6use serde::{Serialize, Deserialize};
7
8use crate::context::ContextManager;
9use crate::rcon_client::RconClient;
10
11#[derive(Debug, Serialize, Deserialize)]
12pub struct StatusResponse {
13 pub status: String,
14 pub uptime: u64,
15 pub context_messages: usize,
16}
17
18#[derive(Debug, Serialize, Deserialize)]
19pub struct CommandRequest {
20 pub command: String,
21}
22
23#[derive(Debug, Serialize, Deserialize)]
24pub struct CommandResponse {
25 pub success: bool,
26 pub result: String,
27}
28
29pub struct HttpApi {
30 port: u16,
31 context: Arc<ContextManager>,
32 rcon: Arc<RwLock<Option<RconClient>>>,
33 start_time: std::time::Instant,
34}
35
36impl HttpApi {
37 pub fn new(
38 port: u16,
39 context: Arc<ContextManager>,
40 rcon: Arc<RwLock<Option<RconClient>>>,
41 ) -> Self {
42 Self {
43 port,
44 context,
45 rcon,
46 start_time: std::time::Instant::now(),
47 }
48 }
49
50 pub async fn start(self: Arc<Self>) -> Result<()> {
51 let context = self.context.clone();
52 let rcon = self.rcon.clone();
53 let start_time = self.start_time;
54 let port = self.port;
55
56 let status_route = warp::path("status")
57 .and(warp::get())
58 .map(move || {
59 let response = StatusResponse {
60 status: "running".to_string(),
61 uptime: start_time.elapsed().as_secs(),
62 context_messages: context.len(),
63 };
64 warp::reply::json(&response)
65 });
66
67 let context = self.context.clone();
68 let history_route = warp::path("history")
69 .and(warp::get())
70 .map(move || {
71 let messages = context.get_messages();
72 warp::reply::json(&messages)
73 });
74
75 let rcon_clone = self.rcon.clone();
76 let command_route = warp::path("command")
77 .and(warp::post())
78 .and(warp::body::json())
79 .and_then(move |req: CommandRequest| {
80 let rcon = rcon_clone.clone();
81 async move {
82 let mut rcon_guard = rcon.write().await;
83 if let Some(ref mut rcon_client) = *rcon_guard {
84 match rcon_client.execute(&req.command) {
85 Ok(result) => {
86 let response = CommandResponse {
87 success: true,
88 result,
89 };
90 Ok::<_, warp::Rejection>(warp::reply::json(&response))
91 }
92 Err(e) => {
93 let response = CommandResponse {
94 success: false,
95 result: e.to_string(),
96 };
97 Ok(warp::reply::json(&response))
98 }
99 }
100 } else {
101 let response = CommandResponse {
102 success: false,
103 result: "RCON not connected".to_string(),
104 };
105 Ok(warp::reply::json(&response))
106 }
107 }
108 });
109
110 let routes = status_route
111 .or(history_route)
112 .or(command_route)
113 .with(warp::cors().allow_any_origin());
114
115 info!("Starting HTTP API server on port {}", port);
116
117 warp::serve(routes)
118 .run(([0, 0, 0, 0], port))
119 .await;
120
121 Ok(())
122 }
123}