use std::{collections::HashMap, net::SocketAddr}; use futures::Future; use include_dir::{include_dir, Dir}; use once_cell::sync::Lazy; use warp::{ http::HeaderValue, hyper::HeaderMap, reply::{json, Response}, Filter, Rejection, Reply, }; use crate::themes::{read_theme_from_dir, Theme}; static THEMES: Lazy> = Lazy::new(|| { let mut themes = HashMap::new(); for entry in std::fs::read_dir("styles").unwrap() { if entry.is_err() { continue; } let path = entry.unwrap().path(); if path.is_dir() { if let Some(theme) = read_theme_from_dir(path) { themes.insert(theme.slug.clone(), theme); } } } themes }); fn injector(theme_name: String, host: String) -> String { if let Some(theme) = THEMES.get(&theme_name) { // let current_addr = CURRENT_ADDRESS.get().expect("Couldn't get current address"); crate::injector::render_injector(host, theme) } else { "// Unknown theme, sorry!".to_string() } } fn themes() -> impl Reply { let themes: &HashMap<_, _> = &THEMES; json(themes) } const PANEL_DIR: Dir = include_dir!("./src/panel"); async fn serve_panel_file(path: warp::path::Tail) -> Result { let mut file_name = path.as_str().to_string(); if ("/".to_string() + &file_name).ends_with("/") { file_name.push_str("index.html"); } if let Some(file) = PANEL_DIR.get_file(&file_name) { let (mut head, body) = Response::new(file.contents().into()).into_parts(); let extension = file_name.rfind('.').map(|idx| &file_name[idx..]); let content_type = match extension { Some(".html") => "text/html; charset=utf-8", Some(".css") => "text/css; charset=utf-8", Some(".js") => "application/javascript; charset=utf-8", Some(".txt") => "text/plain; charset=utf-8", _ => "application/octet-stream", }; head.headers .insert("Content-Type", HeaderValue::from_str(content_type).unwrap()); return Ok(Response::from_parts(head, body)); } Err(warp::reject::not_found()) } pub fn serve_panel() -> (SocketAddr, impl Future) { let styles_route = { let mut static_headers = HeaderMap::new(); for (h, v) in [ ("Access-Control-Allow-Origin", "*"), ("Pragma", "no-cache"), ("Cache-Control", "no-cache"), ] { static_headers.insert(h, HeaderValue::from_static(v)); } warp::path("styles") .and(warp::fs::dir("styles")) .with(warp::reply::with::headers(static_headers)) }; let themes_list_route = warp::path("themes.json").map(themes); let injector_route = warp::path!("theme" / String / "injector.js") .and(warp::header("Host")) .map(injector); let panel_route = warp::get() .and(warp::path::tail()) .and_then(serve_panel_file); let routes = styles_route .or(injector_route) .or(themes_list_route) .or(panel_route); warp::serve(routes).bind_ephemeral(([127, 0, 0, 1], 0)) }