use std::path::Path;
use anyhow::Result;
use tiny_http::{Server, Response, Header};
use std::fs;
use serde_json::Value;
pub fn execute(port: u16, project_root: &Path) -> Result<()> {
let addr = format!("127.0.0.1:{}", port);
let server = Server::http(&addr).map_err(|e| anyhow::anyhow!("Failed to start server: {}", e))?;
println!("Dashboard backend running at http://{}", addr);
println!("Press Ctrl+C to stop.");
for request in server.incoming_requests() {
let response = match (request.method(), request.url()) {
(&tiny_http::Method::Get, "/api/sessions") => {
handle_list_dir(project_root.join(".morph-cli/sessions"))
}
(&tiny_http::Method::Get, "/api/reports") => {
handle_list_dir(project_root.join(".morph-cli/reports"))
}
(&tiny_http::Method::Get, "/api/validation") => {
handle_list_dir(project_root.join(".morph-cli/validation"))
}
(&tiny_http::Method::Get, url) if url.starts_with("/api/sessions/") => {
let id = &url[14..];
handle_read_file(project_root.join(".morph-cli/sessions").join(format!("{}.json", id)))
}
(&tiny_http::Method::Get, url) if url.starts_with("/api/reports/") => {
let id = &url[13..];
handle_read_file(project_root.join(".morph-cli/reports").join(format!("{}.json", id)))
}
_ => Response::from_string("Not Found").with_status_code(404),
};
let response = response.with_header(Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap())
.with_header(Header::from_bytes(&b"Access-Control-Allow-Origin"[..], &b"*"[..]).unwrap());
let _ = request.respond(response);
}
Ok(())
}
fn handle_list_dir(path: std::path::PathBuf) -> Response<std::io::Cursor<Vec<u8>>> {
if !path.exists() {
return Response::from_string("[]").with_status_code(200);
}
let mut entries = Vec::new();
if let Ok(dir) = fs::read_dir(path) {
for entry in dir.filter_map(|e| e.ok()) {
if entry.path().extension().and_then(|s| s.to_str()) == Some("json") {
if let Ok(content) = fs::read_to_string(entry.path()) {
if let Ok(json) = serde_json::from_str::<Value>(&content) {
entries.push(json);
}
}
}
}
}
let json = serde_json::to_string(&entries).unwrap_or_else(|_| "[]".to_string());
Response::from_string(json)
}
fn handle_read_file(path: std::path::PathBuf) -> Response<std::io::Cursor<Vec<u8>>> {
if !path.exists() {
return Response::from_string("Not Found").with_status_code(404);
}
match fs::read_to_string(path) {
Ok(content) => Response::from_string(content),
Err(_) => Response::from_string("Error reading file").with_status_code(500),
}
}