1mod api;
6
7use std::sync::Arc;
8use tokio::sync::Mutex;
9
10use axum::routing::{get, post};
11use axum::Router;
12use stint_core::service::StintService;
13use stint_core::storage::sqlite::SqliteStorage;
14use tokio::net::TcpListener;
15
16pub fn run_server(port: u16) -> Result<(), Box<dyn std::error::Error>> {
20 let rt = tokio::runtime::Runtime::new()?;
21 rt.block_on(async { serve(port).await })
22}
23
24async fn serve(port: u16) -> Result<(), Box<dyn std::error::Error>> {
26 let path = SqliteStorage::default_path();
27 let storage = SqliteStorage::open(&path)?;
28 let service = StintService::new(storage);
29 let state = Arc::new(Mutex::new(service));
30
31 let app = Router::new()
32 .route("/api/health", get(api::health))
33 .route("/api/status", get(api::status))
34 .route("/api/entries", get(api::entries))
35 .route("/api/projects", get(api::projects))
36 .route("/api/start", post(api::start))
37 .route("/api/stop", post(api::stop))
38 .with_state(state);
39
40 let addr = format!("127.0.0.1:{port}");
41 let listener = TcpListener::bind(&addr).await?;
42 println!("Stint API listening on http://{addr}");
43
44 axum::serve(listener, app)
45 .with_graceful_shutdown(shutdown_signal())
46 .await?;
47
48 println!("Server stopped.");
49 Ok(())
50}
51
52async fn shutdown_signal() {
54 let ctrl_c = tokio::signal::ctrl_c();
55 #[cfg(unix)]
56 {
57 match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {
58 Ok(mut sigterm) => {
59 tokio::select! {
60 _ = ctrl_c => {},
61 _ = sigterm.recv() => {},
62 }
63 }
64 Err(_) => {
65 ctrl_c.await.ok();
67 }
68 }
69 }
70 #[cfg(not(unix))]
71 {
72 ctrl_c.await.ok();
73 }
74}