use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use tiny_http::{Request, Response, Server as HttpServer};
use tracing::{info, warn};
use std::time::Instant;
use std::io::Read;
#[derive(Debug, Clone)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
pub max_connections: usize,
pub request_timeout_ms: u64,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: "127.0.0.1".to_string(),
port: 3000,
max_connections: 1000,
request_timeout_ms: 30000,
}
}
}
pub struct Server {
config: ServerConfig,
runtime: Arc<Mutex<Runtime>>,
}
pub mod websocket;
impl Server {
pub fn new(runtime: Runtime) -> Self {
Self {
config: ServerConfig::default(),
runtime: Arc::new(Mutex::new(runtime)))
}
}
pub fn host(mut self, host: &str) -> Self {
self.config.host = host.to_string();
self
}
pub fn port(mut self, port: u16) -> Self {
self.config.port = port;
self
}
pub fn max_connections(mut self, max: usize) -> Self {
self.config.max_connections = max;
self
}
pub fn timeout(mut self, timeout_ms: u64) -> Self {
self.config.request_timeout_ms = timeout_ms;
self
}
pub async fn run(self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let server_url: _ = format!("{}:{}, self.config.host", self.config.port));
let server: _ = HttpServer::http(&server_url).map_err(|e| format!("Failed to bind to {}: {}", server_url, e))?;
info!("🚀 Amber Server started on http://{}", server_url);
info!("📊 POST /api/v1/eval - Execute JavaScript code");
info!("📊 GET /api/v1/stats - Get server statistics");
info!("❤️ GET /health - Health check");
info!("🔌 WebSocket available on ws://{}:{}/ws", self.config.host, self.config.port + 1);
info!("⚠️ Note: Running in single-threaded mode for V8 compatibility");
for request in server.incoming_requests() {
let runtime_clone: _ = self.runtime.clone();
if let Err(e) = self.handle_request(runtime_clone, request).await {
warn!("Request error: {}", e);
}
}
Ok(())
}
async fn handle_request(
&self,
runtime: Arc<Mutex<Runtime>>,
request: Request,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let url: _ = request.url().to_string();
match url.as_str() {
"/api/v1/eval" if request.method() == &tiny_http::Method::Post => {
self.handle_eval(runtime, request).await
}
"/api/v1/stats" if request.method() == &tiny_http::Method::Get => {
self.handle_stats(runtime, request).await
}
"/health" if request.method() == &tiny_http::Method::Get => {
self.handle_health(request).await
}
_ => {
let response: _ = Response::from_string("Not Found").with_status_code(404);
request.respond(response)?;
Ok(())
}
}
}
async fn handle_eval(
&self,
runtime: Arc<Mutex<Runtime>>,
mut request: Request,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let start_time: _ = std::time::Instant::now();
let mut body = String::new();
request.as_reader().read_to_string(&mut body)?;
let eval_request: EvalRequest = serde_json::from_str(&body)
.map_err(|e| format!("Invalid JSON: {}", e))?;
info!("Processing eval request ({} bytes of code)", eval_request.code.len());
let result: _ = {
let runtime_guard: _ = runtime.lock().map_err(|e| format!("Runtime lock error: {}", e))?;
runtime_guard.execute_code(&eval_request.code)
};
let execution_time_ms: _ = start_time.elapsed().as_millis() as u64;
let response: _ = match result {
Ok(output) => {
let eval_response: _ = EvalResponse {
result: output,
execution_time_ms,
cached: false,
error: None,
};
Response::from_string(serde_json::to_string(&eval_response)?)
.with_status_code(200)
.with_header(
tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap()
)
}
Err(e) => {
let eval_response: _ = EvalResponse {
result: String::new(),
execution_time_ms,
cached: false,
error: Some(e.to_string()),
};
Response::from_string(serde_json::to_string(&eval_response)?)
.with_status_code(500)
.with_header(
tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap()
)
}
};
request.respond(response)?;
Ok(())
}
async fn handle_stats(
&self,
_runtime: Arc<Mutex<Runtime>>,
request: Request,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let stats: _ = ServerStats::default();
let response: _ = Response::from_string(serde_json::to_string(&stats)?)
.with_status_code(200)
.with_header(
tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap()
);
request.respond(response)?;
Ok(())
}
async fn handle_health(
&self,
request: Request,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let response: _ = Response::from_string("OK")
.with_status_code(200)
.with_header(
tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"text/plain"[..]).unwrap()
);
request.respond(response)?;
Ok(())
}
}
#[derive(Debug, Deserialize)]
pub struct EvalRequest {
pub code: String,
pub timeout: Option<u64>,
pub optimize: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EvalResponse {
pub result: String,
pub execution_time_ms: u64,
pub cached: bool,
pub error: Option<String>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ServerStats {
pub total_executions: u64,
pub successful_executions: u64,
pub failed_executions: u64,
pub avg_execution_time_ms: f64,
pub cache_hit_rate: f64,
pub active_sessions: usize,
}
pub async fn start_server(config: ServerConfig, runtime: Runtime) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let server: _ = Server::new(runtime)
.host(&config.host)
.port(config.port)
.max_connections(config.max_connections)
.timeout(config.request_timeout_ms);
server.run().await
}
#[cfg(test)]
mod tests {
#[test]
fn test_server_config_default() {
let config: _ = ServerConfig::default();
assert_eq!(config.port, 3000);
assert_eq!(config.host, "127.0.0.1");
}
#[test]
fn test_eval_request_deserialize() {
let json: _ = r#"{"code": "1+1", "timeout": 5000, "optimize": "speed"}"#;
let request: EvalRequest = serde_json::from_str(json).unwrap();
assert_eq!(request.code, "1+1");
assert_eq!(request.timeout, Some(5000));
}
#[test]
fn test_eval_response_serialize() {
let response: _ = EvalResponse {
result: "2".to_string(),
execution_time_ms: 5,
cached: false,
error: None,
};
let json: _ = serde_json::to_string(&response).unwrap();
let parsed: EvalResponse = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.result, "2");
}
}