clean shutdown

This commit is contained in:
2026-08-01 16:29:31 -04:00
parent 5e57c61e4b
commit 6979cabe8b
+40 -41
View File
@@ -282,10 +282,39 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind(&bind_address).await?;
info!(address = %bind_address, "sustenance listening");
axum::serve(listener, app).await?;
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await?;
info!("shutdown complete; closing database");
Ok(())
}
async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
info!("signal received; starting graceful shutdown");
}
async fn home() -> Redirect {
Redirect::to("/lists")
}
@@ -431,10 +460,7 @@ async fn lists_page(
State(state): State<AppState>,
user: CurrentUser,
) -> Result<Response, AppError> {
let lists = state
.db
.list_summaries()
.await?;
let lists = state.db.list_summaries().await?;
Ok(html_response(views::lists_page(
&user.session.user,
&lists,
@@ -498,10 +524,7 @@ async fn add_item(
.db
.add_item(list_id, name, quantity, note, category_id)
.await?;
state
.hub
.publish_list_changed(list_id, revision)
.await;
state.hub.publish_list_changed(list_id, revision).await;
list_fragment_response(&state, &user, list_id).await
}
@@ -518,14 +541,8 @@ async fn check_item(
"0" | "false" => false,
_ => return Err(AppError::BadRequest("Invalid checked value.".into())),
};
let revision = state
.db
.set_item_checked(list_id, item_id, checked)
.await?;
state
.hub
.publish_list_changed(list_id, revision)
.await;
let revision = state.db.set_item_checked(list_id, item_id, checked).await?;
state.hub.publish_list_changed(list_id, revision).await;
list_fragment_response(&state, &user, list_id).await
}
@@ -554,10 +571,7 @@ async fn edit_item(
parse_category_id(form.category_id),
)
.await?;
state
.hub
.publish_list_changed(list_id, revision)
.await;
state.hub.publish_list_changed(list_id, revision).await;
list_fragment_response(&state, &user, list_id).await
}
@@ -570,10 +584,7 @@ async fn delete_item(
verify_csrf(&user, &form.csrf)?;
require_access(&state, list_id).await?;
let revision = state.db.delete_item(list_id, item_id).await?;
state
.hub
.publish_list_changed(list_id, revision)
.await;
state.hub.publish_list_changed(list_id, revision).await;
list_fragment_response(&state, &user, list_id).await
}
@@ -592,10 +603,7 @@ async fn create_category(
));
}
let revision = state.db.create_category(list_id, name).await?;
state
.hub
.publish_list_changed(list_id, revision)
.await;
state.hub.publish_list_changed(list_id, revision).await;
let access = require_access(&state, list_id).await?;
let items = state.db.items(list_id).await?;
@@ -631,10 +639,7 @@ async fn invitation_page(
Path(token): Path<String>,
headers: HeaderMap,
) -> Result<Response, AppError> {
let info = state
.db
.invitation(token.clone())
.await?;
let info = state.db.invitation(token.clone()).await?;
if !info {
return Err(AppError::NotFound);
}
@@ -655,10 +660,7 @@ async fn accept_invitation(
LoggedForm(form): LoggedForm<CsrfForm>,
) -> Result<Response, AppError> {
verify_csrf(&user, &form.csrf)?;
state
.db
.accept_invitation(token)
.await?;
state.db.accept_invitation(token).await?;
Ok(Redirect::to("/lists").into_response())
}
@@ -817,10 +819,7 @@ async fn list_fragment_response(
)))
}
async fn require_access(
state: &AppState,
list_id: i64,
) -> Result<db::GroceryList, AppError> {
async fn require_access(state: &AppState, list_id: i64) -> Result<db::GroceryList, AppError> {
state
.db
.list_access(list_id)