use crate::{api, middleware};
use actix_web::dev::ServerHandle;
use actix_web::middleware::{NormalizePath, TrailingSlash};
use actix_web::{App, HttpServer, web};
use puniyu_common::app::app_name;
use puniyu_runtime::ServerRuntime;
use crate::logger::info;
use std::io;
use std::net::IpAddr;
use std::sync::{LazyLock, Mutex};
struct ServerControl {
handle: ServerHandle,
}
static SERVER_CONTROL: LazyLock<Mutex<Option<ServerControl>>> = LazyLock::new(|| Mutex::new(None));
pub fn start_server(host: IpAddr, port: u16) -> io::Result<ServerRuntime> {
{
let guard = SERVER_CONTROL.lock().map_err(|e| io::Error::other(e.to_string()))?;
if guard.is_some() {
return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Server already running"));
}
}
info!("Server running on {}:{}", host, port);
let server = HttpServer::new(|| {
let app = App::new()
.wrap(middleware::AccessLog)
.wrap(NormalizePath::new(TrailingSlash::Trim))
.service(web::resource("/").to(|| async { format!("welcome {}", app_name()) }))
.configure(|cfg| {
cfg.service(web::scope("/api/v1").configure(api::register_routes));
});
#[cfg(feature = "registry")]
let app = {
use crate::registry::ServerRegistry;
ServerRegistry::all().into_iter().fold(app, |app, cfg| {
let builder = cfg.builder.clone();
app.configure(move |sc| builder.call(sc))
})
};
app
})
.bind((host, port))?;
let running_server = server.run();
let handle = running_server.handle();
let control = ServerControl { handle: handle.clone() };
SERVER_CONTROL.lock().map_err(|e| io::Error::other(e.to_string()))?.replace(control);
let join_handle = tokio::spawn(running_server);
Ok(ServerRuntime::new(handle, join_handle))
}
pub async fn run_server(host: IpAddr, port: u16) -> io::Result<()> {
start_server(host, port)?.wait().await
}
pub async fn stop_server() -> io::Result<()> {
let control = SERVER_CONTROL
.lock()
.map_err(|e| io::Error::other(e.to_string()))?
.take()
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Server not running"))?;
control.handle.stop(true).await;
Ok(())
}
pub async fn restart_server(host: IpAddr, port: u16) -> io::Result<ServerRuntime> {
stop_server().await?;
start_server(host, port)
}