sweet_server/server/
mod.rs1pub mod address;
2pub use self::address::*;
3pub mod tls;
4pub use self::tls::*;
5use anyhow::Result;
6use axum::Router;
7use axum::extract::Request;
8use axum::http::Method;
9use axum::response::Response;
10use axum::routing::get;
11use clap::Parser;
12use std::path::PathBuf;
13use std::thread::JoinHandle;
14use std::time::Duration;
15use sweet_fs::prelude::*;
16use sweet_utils::prelude::*;
17use tower_http::cors::CorsLayer;
18use tower_http::services::ServeDir;
19use tower_http::services::ServeFile;
20use tower_livereload::LiveReloadLayer;
21
22#[derive(Debug, Clone, Parser)]
24#[command(name = "serve")]
25pub struct Server {
26 #[arg(default_value = ".")]
28 pub dir: PathBuf,
29 #[arg(long, default_value = "3000")]
31 pub port: String,
32 #[arg(long, default_value = "0.0.0.0")]
34 pub host: String,
35 #[arg(long)]
37 pub secure: bool,
38 #[arg(long)]
41 pub no_clear: bool,
42 #[arg(long)]
43 pub quiet: bool,
44 #[arg(long)]
46 pub fallback: Option<String>,
47 #[arg(long)]
49 pub any_origin: bool,
50 #[command(flatten)]
54 pub filter: GlobFilter,
55 #[arg(short,long="debounce-millis",value_parser = parse_duration,default_value="50")]
57 pub debounce: Duration,
58}
59
60impl Default for Server {
61 fn default() -> Self { Self::parse_from(&[""]) }
62}
63
64impl Server {
65 pub fn with_dir(mut self, dir: impl Into<PathBuf>) -> Self {
66 self.dir = dir.into();
67 self
68 }
69 pub fn quietly(mut self) -> Self {
70 self.quiet = true;
71 self
72 }
73 pub async fn run(self) -> Result<()> {
74 self.serve_with_default_reload().await
75 }
76
77 pub async fn serve_with_default_reload(self) -> Result<()> {
78 let (livereload, _handle) = self.default_reload();
79 self.serve_with_reload(livereload).await
80 }
81
82 pub async fn serve_with_reload(
83 self,
84 livereload: LiveReloadLayer,
85 ) -> Result<()> {
86 self.serve_with_options(Some(livereload)).await
87 }
88
89 pub async fn serve_with_options(
90 self,
91 livereload: Option<LiveReloadLayer>,
92 ) -> Result<()> {
93 self.print_start();
94
95 let mut router = Router::new().route_service("/__ping__", get(ping));
96
97 if let Some(fallback) = &self.fallback {
98 router = router.fallback_service(
99 ServeDir::new(&self.dir)
100 .append_index_html_on_directories(true)
101 .fallback(ServeFile::new(fallback)),
102 );
103 } else {
104 router = router.fallback_service(
105 ServeDir::new(&self.dir).append_index_html_on_directories(true),
106 );
107 }
108 if let Some(livereload) = livereload {
109 router = router.layer(livereload);
110 }
111 if self.any_origin {
112 let cors = CorsLayer::new()
113 .allow_methods([Method::GET, Method::POST])
114 .allow_origin(tower_http::cors::Any);
115 router = router.layer(cors);
116 }
117
118 if self.secure {
119 self.serve_secure(router).await
120 } else {
121 self.serve_insecure(router).await
122 }
123 }
124
125 fn default_reload(&self) -> (LiveReloadLayer, JoinHandle<Result<()>>) {
126 let livereload = LiveReloadLayer::new();
127 let reload = livereload.reloader();
128 let this = self.clone();
129 let reload_handle = std::thread::spawn(move || -> Result<()> {
130 let this2 = this.clone();
131
132 FsWatcher {
133 infallible: false,
134 cwd: this.dir.clone(),
135 filter: this.filter.clone(),
136 debounce: this.debounce.clone(),
137 }
138 .watch_blocking(move |e| {
139 if let Some(events) = e.mutated_pretty() {
140 reload.reload();
141 println!("{}", events);
142 this2.print_start();
143 }
144 Ok(())
145 })
146 });
147 (livereload, reload_handle)
148 }
149
150 fn print_start(&self) {
151 if self.quiet {
152 return;
153 }
154 if self.no_clear == false {
155 terminal::clear().ok();
157 }
158 let any_origin = if self.any_origin {
159 "\nany-origin: true"
160 } else {
161 ""
162 };
163 println!(
164 "serving '{}' at {}{any_origin}",
165 self.dir.display(),
166 self.address().unwrap(),
167 );
168 }
169 pub fn address(&self) -> Result<Address> {
170 Ok(Address {
171 host: Address::host_from_str(&self.host)?,
172 port: self.port.parse::<u16>()?,
173 secure: self.secure,
174 ..Default::default()
175 })
176 }
177}
178
179async fn ping(req: Request) -> Response<String> {
180 let body = format!("request was {:?}", req);
181 Response::new(body)
182}