✨feature: Add docker support
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// Hide console window on Windows in release builds (production mode)
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{routing::get, Router};
|
||||
@@ -20,22 +20,58 @@ mod websocket;
|
||||
/// Find an available port and return the bound listener.
|
||||
/// Avoids TOCTOU race by keeping the listener alive.
|
||||
async fn find_available_port() -> (TcpListener, u16) {
|
||||
let host = std::env::var("IRONPAD_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||||
let bind_ip = match host.parse::<IpAddr>() {
|
||||
Ok(ip) => ip,
|
||||
Err(_) => {
|
||||
warn!("Invalid IRONPAD_HOST '{}', falling back to 127.0.0.1", host);
|
||||
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(port_str) = std::env::var("IRONPAD_PORT") {
|
||||
match port_str.parse::<u16>() {
|
||||
Ok(port) => {
|
||||
let addr = SocketAddr::new(bind_ip, port);
|
||||
let listener = TcpListener::bind(addr)
|
||||
.await
|
||||
.unwrap_or_else(|e| panic!("Failed to bind to {addr}: {e}"));
|
||||
return (listener, port);
|
||||
}
|
||||
Err(_) => {
|
||||
warn!("Invalid IRONPAD_PORT '{}', falling back to 3000-3010", port_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for port in 3000..=3010 {
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], port));
|
||||
let addr = SocketAddr::new(bind_ip, port);
|
||||
if let Ok(listener) = TcpListener::bind(addr).await {
|
||||
return (listener, port);
|
||||
}
|
||||
}
|
||||
panic!("No available ports in range 3000–3010");
|
||||
panic!("No available ports in range 3000-3010");
|
||||
}
|
||||
|
||||
fn env_flag(name: &str) -> bool {
|
||||
std::env::var(name)
|
||||
.map(|v| matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tracing_subscriber::fmt().init();
|
||||
config::init_data_dir();
|
||||
|
||||
if config::is_production() {
|
||||
let production = config::is_production();
|
||||
let disable_tray = env_flag("IRONPAD_DISABLE_TRAY");
|
||||
|
||||
if production && !disable_tray {
|
||||
run_with_tray();
|
||||
} else {
|
||||
if production && disable_tray {
|
||||
info!("Production static mode detected; running headless (IRONPAD_DISABLE_TRAY=1)");
|
||||
}
|
||||
// Development mode: normal tokio runtime, no tray
|
||||
let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
|
||||
rt.block_on(run_server(None));
|
||||
@@ -130,7 +166,11 @@ async fn run_server(port_tx: Option<std::sync::mpsc::Sender<u16>>) {
|
||||
}
|
||||
|
||||
// Start server
|
||||
info!("Ironpad running on http://localhost:{port}");
|
||||
let bound_addr = listener
|
||||
.local_addr()
|
||||
.map(|a| a.to_string())
|
||||
.unwrap_or_else(|_| format!("127.0.0.1:{port}"));
|
||||
info!("Ironpad running on http://{bound_addr}");
|
||||
|
||||
axum::serve(listener, app).await.expect("Server failed");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user