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::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 start_time = self.start_time;
53 let port = self.port;
54
55 let status_route = warp::path("status")
56 .and(warp::get())
57 .map(move || {
58 let response = StatusResponse {
59 status: "running".to_string(),
60 uptime: start_time.elapsed().as_secs(),
61 context_messages: context.len(),
62 };
63 warp::reply::json(&response)
64 });
65
66 let context = self.context.clone();
67 let history_route = warp::path("history")
68 .and(warp::get())
69 .map(move || {
70 let messages = context.get_messages();
71 warp::reply::json(&messages)
72 });
73
74 let rcon_clone = self.rcon.clone();
75 let command_route = warp::path("command")
76 .and(warp::post())
77 .and(warp::body::json())
78 .and_then(move |req: CommandRequest| {
79 let rcon = rcon_clone.clone();
80 async move {
81 let mut rcon_guard = rcon.write().await;
82 if let Some(ref mut rcon_client) = *rcon_guard {
83 match rcon_client.execute(&req.command) {
84 Ok(result) => {
85 let response = CommandResponse {
86 success: true,
87 result,
88 };
89 Ok::<_, warp::Rejection>(warp::reply::json(&response))
90 }
91 Err(e) => {
92 let response = CommandResponse {
93 success: false,
94 result: e.to_string(),
95 };
96 Ok(warp::reply::json(&response))
97 }
98 }
99 } else {
100 let response = CommandResponse {
101 success: false,
102 result: "RCON not connected".to_string(),
103 };
104 Ok(warp::reply::json(&response))
105 }
106 }
107 });
108
109 let routes = status_route
110 .or(history_route)
111 .or(command_route)
112 .with(warp::cors().allow_any_origin());
113
114 info!("Starting HTTP API server on port {}", port);
115
116 warp::serve(routes)
117 .run(([0, 0, 0, 0], port))
118 .await;
119
120 Ok(())
121 }
122}