clean shutdown
This commit is contained in:
+40
-41
@@ -282,10 +282,39 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let listener = TcpListener::bind(&bind_address).await?;
|
let listener = TcpListener::bind(&bind_address).await?;
|
||||||
info!(address = %bind_address, "sustenance listening");
|
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(())
|
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 {
|
async fn home() -> Redirect {
|
||||||
Redirect::to("/lists")
|
Redirect::to("/lists")
|
||||||
}
|
}
|
||||||
@@ -431,10 +460,7 @@ async fn lists_page(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
user: CurrentUser,
|
user: CurrentUser,
|
||||||
) -> Result<Response, AppError> {
|
) -> Result<Response, AppError> {
|
||||||
let lists = state
|
let lists = state.db.list_summaries().await?;
|
||||||
.db
|
|
||||||
.list_summaries()
|
|
||||||
.await?;
|
|
||||||
Ok(html_response(views::lists_page(
|
Ok(html_response(views::lists_page(
|
||||||
&user.session.user,
|
&user.session.user,
|
||||||
&lists,
|
&lists,
|
||||||
@@ -498,10 +524,7 @@ async fn add_item(
|
|||||||
.db
|
.db
|
||||||
.add_item(list_id, name, quantity, note, category_id)
|
.add_item(list_id, name, quantity, note, category_id)
|
||||||
.await?;
|
.await?;
|
||||||
state
|
state.hub.publish_list_changed(list_id, revision).await;
|
||||||
.hub
|
|
||||||
.publish_list_changed(list_id, revision)
|
|
||||||
.await;
|
|
||||||
list_fragment_response(&state, &user, list_id).await
|
list_fragment_response(&state, &user, list_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,14 +541,8 @@ async fn check_item(
|
|||||||
"0" | "false" => false,
|
"0" | "false" => false,
|
||||||
_ => return Err(AppError::BadRequest("Invalid checked value.".into())),
|
_ => return Err(AppError::BadRequest("Invalid checked value.".into())),
|
||||||
};
|
};
|
||||||
let revision = state
|
let revision = state.db.set_item_checked(list_id, item_id, checked).await?;
|
||||||
.db
|
state.hub.publish_list_changed(list_id, revision).await;
|
||||||
.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
|
list_fragment_response(&state, &user, list_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,10 +571,7 @@ async fn edit_item(
|
|||||||
parse_category_id(form.category_id),
|
parse_category_id(form.category_id),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
state
|
state.hub.publish_list_changed(list_id, revision).await;
|
||||||
.hub
|
|
||||||
.publish_list_changed(list_id, revision)
|
|
||||||
.await;
|
|
||||||
list_fragment_response(&state, &user, list_id).await
|
list_fragment_response(&state, &user, list_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,10 +584,7 @@ async fn delete_item(
|
|||||||
verify_csrf(&user, &form.csrf)?;
|
verify_csrf(&user, &form.csrf)?;
|
||||||
require_access(&state, list_id).await?;
|
require_access(&state, list_id).await?;
|
||||||
let revision = state.db.delete_item(list_id, item_id).await?;
|
let revision = state.db.delete_item(list_id, item_id).await?;
|
||||||
state
|
state.hub.publish_list_changed(list_id, revision).await;
|
||||||
.hub
|
|
||||||
.publish_list_changed(list_id, revision)
|
|
||||||
.await;
|
|
||||||
list_fragment_response(&state, &user, list_id).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?;
|
let revision = state.db.create_category(list_id, name).await?;
|
||||||
state
|
state.hub.publish_list_changed(list_id, revision).await;
|
||||||
.hub
|
|
||||||
.publish_list_changed(list_id, revision)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let access = require_access(&state, list_id).await?;
|
let access = require_access(&state, list_id).await?;
|
||||||
let items = state.db.items(list_id).await?;
|
let items = state.db.items(list_id).await?;
|
||||||
@@ -631,10 +639,7 @@ async fn invitation_page(
|
|||||||
Path(token): Path<String>,
|
Path(token): Path<String>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> Result<Response, AppError> {
|
) -> Result<Response, AppError> {
|
||||||
let info = state
|
let info = state.db.invitation(token.clone()).await?;
|
||||||
.db
|
|
||||||
.invitation(token.clone())
|
|
||||||
.await?;
|
|
||||||
if !info {
|
if !info {
|
||||||
return Err(AppError::NotFound);
|
return Err(AppError::NotFound);
|
||||||
}
|
}
|
||||||
@@ -655,10 +660,7 @@ async fn accept_invitation(
|
|||||||
LoggedForm(form): LoggedForm<CsrfForm>,
|
LoggedForm(form): LoggedForm<CsrfForm>,
|
||||||
) -> Result<Response, AppError> {
|
) -> Result<Response, AppError> {
|
||||||
verify_csrf(&user, &form.csrf)?;
|
verify_csrf(&user, &form.csrf)?;
|
||||||
state
|
state.db.accept_invitation(token).await?;
|
||||||
.db
|
|
||||||
.accept_invitation(token)
|
|
||||||
.await?;
|
|
||||||
Ok(Redirect::to("/lists").into_response())
|
Ok(Redirect::to("/lists").into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,10 +819,7 @@ async fn list_fragment_response(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn require_access(
|
async fn require_access(state: &AppState, list_id: i64) -> Result<db::GroceryList, AppError> {
|
||||||
state: &AppState,
|
|
||||||
list_id: i64,
|
|
||||||
) -> Result<db::GroceryList, AppError> {
|
|
||||||
state
|
state
|
||||||
.db
|
.db
|
||||||
.list_access(list_id)
|
.list_access(list_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user