simplify list ownership & invitations
This commit is contained in:
+26
-33
@@ -267,7 +267,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.route("/lists/{list_id}/items/{item_id}/edit", post(edit_item))
|
||||
.route("/lists/{list_id}/items/{item_id}/delete", post(delete_item))
|
||||
.route("/lists/{list_id}/categories", post(create_category))
|
||||
.route("/lists/{list_id}/invitations", post(create_invitation))
|
||||
.route("/invitations", post(create_invitation))
|
||||
.route("/lists/{list_id}/stream", get(list_stream))
|
||||
.route("/invite/{token}", get(invitation_page))
|
||||
.route("/invite/{token}/accept", post(accept_invitation))
|
||||
@@ -435,7 +435,7 @@ async fn lists_page(
|
||||
) -> Result<Response, AppError> {
|
||||
let lists = state
|
||||
.db
|
||||
.list_summaries(user.session.user.id.clone())
|
||||
.list_summaries()
|
||||
.await?;
|
||||
Ok(html_response(views::lists_page(
|
||||
&user.session.user,
|
||||
@@ -456,7 +456,7 @@ async fn create_list(
|
||||
"List names must be between 1 and 80 characters.".into(),
|
||||
));
|
||||
}
|
||||
let list = state.db.create_list(user.session.user.id, name).await?;
|
||||
let list = state.db.create_list(name).await?;
|
||||
Ok(Redirect::to(&format!("/lists/{}", list.id)).into_response())
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ async fn list_page(
|
||||
user: CurrentUser,
|
||||
Path(list_id): Path<String>,
|
||||
) -> Result<Response, AppError> {
|
||||
let access = require_access(&state, &user, &list_id).await?;
|
||||
let access = require_access(&state, &list_id).await?;
|
||||
let items = state.db.items(list_id.clone()).await?;
|
||||
let categories = state.db.categories(list_id.clone()).await?;
|
||||
let presence = state.hub.presence(&list_id).await;
|
||||
@@ -486,7 +486,7 @@ async fn add_item(
|
||||
LoggedForm(form): LoggedForm<ItemForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &user, &list_id).await?;
|
||||
require_access(&state, &list_id).await?;
|
||||
let name = form.name.trim().to_owned();
|
||||
let quantity = form.quantity.trim().to_owned();
|
||||
let note = form.note.trim().to_owned();
|
||||
@@ -514,7 +514,7 @@ async fn check_item(
|
||||
LoggedForm(form): LoggedForm<CheckForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &user, &list_id).await?;
|
||||
require_access(&state, &list_id).await?;
|
||||
let checked = match form.checked.as_str() {
|
||||
"1" | "true" => true,
|
||||
"0" | "false" => false,
|
||||
@@ -538,7 +538,7 @@ async fn edit_item(
|
||||
LoggedForm(form): LoggedForm<ItemForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &user, &list_id).await?;
|
||||
require_access(&state, &list_id).await?;
|
||||
let name = form.name.trim().to_owned();
|
||||
if name.is_empty() || name.chars().count() > 120 {
|
||||
return Err(AppError::BadRequest(
|
||||
@@ -570,7 +570,7 @@ async fn delete_item(
|
||||
LoggedForm(form): LoggedForm<CsrfForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &user, &list_id).await?;
|
||||
require_access(&state, &list_id).await?;
|
||||
let revision = state.db.delete_item(list_id.clone(), item_id).await?;
|
||||
state
|
||||
.hub
|
||||
@@ -586,7 +586,7 @@ async fn create_category(
|
||||
LoggedForm(form): LoggedForm<CategoryForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &user, &list_id).await?;
|
||||
require_access(&state, &list_id).await?;
|
||||
let name = form.name.trim().to_owned();
|
||||
if name.is_empty() || name.chars().count() > 60 {
|
||||
return Err(AppError::BadRequest(
|
||||
@@ -599,7 +599,7 @@ async fn create_category(
|
||||
.publish_list_changed(list_id.clone(), revision)
|
||||
.await;
|
||||
|
||||
let access = require_access(&state, &user, &list_id).await?;
|
||||
let access = require_access(&state, &list_id).await?;
|
||||
let items = state.db.items(list_id.clone()).await?;
|
||||
let categories = state.db.categories(list_id).await?;
|
||||
Ok(html_response(views::category_created(
|
||||
@@ -613,20 +613,13 @@ async fn create_category(
|
||||
async fn create_invitation(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path(list_id): Path<String>,
|
||||
LoggedForm(form): LoggedForm<CsrfForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
let access = require_access(&state, &user, &list_id).await?;
|
||||
if access.role != "owner" {
|
||||
return Err(AppError::BadRequest(
|
||||
"Only the list owner can create invitations.".into(),
|
||||
));
|
||||
}
|
||||
let token = db::new_secret();
|
||||
state
|
||||
.db
|
||||
.create_invitation(list_id, user.session.user.id, token.clone())
|
||||
.create_invitation(user.session.user.id, token.clone())
|
||||
.await?;
|
||||
let url = format!(
|
||||
"{}/invite/{token}",
|
||||
@@ -643,11 +636,12 @@ async fn invitation_page(
|
||||
let info = state
|
||||
.db
|
||||
.invitation(token.clone())
|
||||
.await?
|
||||
.ok_or(AppError::NotFound)?;
|
||||
.await?;
|
||||
if !info {
|
||||
return Err(AppError::NotFound);
|
||||
}
|
||||
let user = optional_user(&state, &headers).await?;
|
||||
Ok(html_response(views::invite_page(
|
||||
&info,
|
||||
user.as_ref().map(|current| ¤t.session.user),
|
||||
&token,
|
||||
None,
|
||||
@@ -663,11 +657,11 @@ async fn accept_invitation(
|
||||
LoggedForm(form): LoggedForm<CsrfForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
let list_id = state
|
||||
state
|
||||
.db
|
||||
.accept_invitation(token, user.session.user.id)
|
||||
.accept_invitation(token)
|
||||
.await?;
|
||||
Ok(Redirect::to(&format!("/lists/{list_id}")).into_response())
|
||||
Ok(Redirect::to("/lists").into_response())
|
||||
}
|
||||
|
||||
async fn list_stream(
|
||||
@@ -676,7 +670,7 @@ async fn list_stream(
|
||||
Path(list_id): Path<String>,
|
||||
websocket: WebSocketUpgrade,
|
||||
) -> Result<Response, AppError> {
|
||||
require_access(&state, &user, &list_id).await?;
|
||||
require_access(&state, &list_id).await?;
|
||||
let state_for_socket = state.clone();
|
||||
let user_for_socket = user.clone();
|
||||
Ok(websocket
|
||||
@@ -784,7 +778,7 @@ async fn websocket_snapshot(
|
||||
list_id: &str,
|
||||
presence: &[hub::PresenceUser],
|
||||
) -> Result<String, AppError> {
|
||||
let access = require_access(state, user, list_id).await?;
|
||||
let access = require_access(state, list_id).await?;
|
||||
let items = state.db.items(list_id.to_owned()).await?;
|
||||
let categories = state.db.categories(list_id.to_owned()).await?;
|
||||
Ok(
|
||||
@@ -799,7 +793,7 @@ async fn websocket_list_update(
|
||||
user: &CurrentUser,
|
||||
list_id: &str,
|
||||
) -> Result<String, AppError> {
|
||||
let access = require_access(state, user, list_id).await?;
|
||||
let access = require_access(state, list_id).await?;
|
||||
let items = state.db.items(list_id.to_owned()).await?;
|
||||
let categories = state.db.categories(list_id.to_owned()).await?;
|
||||
Ok(
|
||||
@@ -813,11 +807,11 @@ async fn list_fragment_response(
|
||||
user: &CurrentUser,
|
||||
list_id: &str,
|
||||
) -> Result<Response, AppError> {
|
||||
let access = require_access(state, user, list_id).await?;
|
||||
let access = require_access(state, list_id).await?;
|
||||
let items = state.db.items(list_id.to_owned()).await?;
|
||||
let categories = state.db.categories(list_id.to_owned()).await?;
|
||||
Ok(html_response(views::list_items_fragment(
|
||||
&access.list,
|
||||
&access,
|
||||
&items,
|
||||
&categories,
|
||||
&user.session.csrf_token,
|
||||
@@ -827,12 +821,11 @@ async fn list_fragment_response(
|
||||
|
||||
async fn require_access(
|
||||
state: &AppState,
|
||||
user: &CurrentUser,
|
||||
list_id: &str,
|
||||
) -> Result<db::ListAccess, AppError> {
|
||||
) -> Result<db::GroceryList, AppError> {
|
||||
state
|
||||
.db
|
||||
.list_access(list_id.to_owned(), user.session.user.id.clone())
|
||||
.list_access(list_id.to_owned())
|
||||
.await?
|
||||
.ok_or(AppError::NotFound)
|
||||
}
|
||||
@@ -877,7 +870,7 @@ async fn can_register(state: &AppState, invite: Option<&str>) -> Result<bool, Ap
|
||||
let Some(invite) = invite.filter(|invite| !invite.is_empty()) else {
|
||||
return Ok(false);
|
||||
};
|
||||
Ok(state.db.invitation(invite.to_owned()).await?.is_some())
|
||||
Ok(state.db.invitation(invite.to_owned()).await?)
|
||||
}
|
||||
|
||||
fn hash_password(password: &str) -> Result<String, String> {
|
||||
|
||||
Reference in New Issue
Block a user