use autoincrement for IDs
This commit is contained in:
+60
-58
@@ -463,12 +463,12 @@ async fn create_list(
|
||||
async fn list_page(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path(list_id): Path<String>,
|
||||
Path(list_id): Path<i64>,
|
||||
) -> Result<Response, AppError> {
|
||||
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;
|
||||
let access = require_access(&state, list_id).await?;
|
||||
let items = state.db.items(list_id).await?;
|
||||
let categories = state.db.categories(list_id).await?;
|
||||
let presence = state.hub.presence(list_id).await;
|
||||
Ok(html_response(views::list_page(
|
||||
&user.session.user,
|
||||
&access,
|
||||
@@ -482,15 +482,15 @@ async fn list_page(
|
||||
async fn add_item(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path(list_id): Path<String>,
|
||||
Path(list_id): Path<i64>,
|
||||
LoggedForm(form): LoggedForm<ItemForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &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();
|
||||
let category_id = normalize_category_id(form.category_id);
|
||||
let category_id = parse_category_id(form.category_id);
|
||||
if name.is_empty() || name.chars().count() > 120 {
|
||||
return Err(AppError::BadRequest(
|
||||
"Item names must be between 1 and 120 characters.".into(),
|
||||
@@ -498,23 +498,23 @@ async fn add_item(
|
||||
}
|
||||
let revision = state
|
||||
.db
|
||||
.add_item(list_id.clone(), name, quantity, note, category_id)
|
||||
.add_item(list_id, name, quantity, note, category_id)
|
||||
.await?;
|
||||
state
|
||||
.hub
|
||||
.publish_list_changed(list_id.clone(), revision)
|
||||
.publish_list_changed(list_id, revision)
|
||||
.await;
|
||||
list_fragment_response(&state, &user, &list_id).await
|
||||
list_fragment_response(&state, &user, list_id).await
|
||||
}
|
||||
|
||||
async fn check_item(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path((list_id, item_id)): Path<(String, String)>,
|
||||
Path((list_id, item_id)): Path<(i64, i64)>,
|
||||
LoggedForm(form): LoggedForm<CheckForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &list_id).await?;
|
||||
require_access(&state, list_id).await?;
|
||||
let checked = match form.checked.as_str() {
|
||||
"1" | "true" => true,
|
||||
"0" | "false" => false,
|
||||
@@ -522,23 +522,23 @@ async fn check_item(
|
||||
};
|
||||
let revision = state
|
||||
.db
|
||||
.set_item_checked(list_id.clone(), item_id, checked)
|
||||
.set_item_checked(list_id, item_id, checked)
|
||||
.await?;
|
||||
state
|
||||
.hub
|
||||
.publish_list_changed(list_id.clone(), revision)
|
||||
.publish_list_changed(list_id, revision)
|
||||
.await;
|
||||
list_fragment_response(&state, &user, &list_id).await
|
||||
list_fragment_response(&state, &user, list_id).await
|
||||
}
|
||||
|
||||
async fn edit_item(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path((list_id, item_id)): Path<(String, String)>,
|
||||
Path((list_id, item_id)): Path<(i64, i64)>,
|
||||
LoggedForm(form): LoggedForm<ItemForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &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(
|
||||
@@ -548,59 +548,59 @@ async fn edit_item(
|
||||
let revision = state
|
||||
.db
|
||||
.update_item(
|
||||
list_id.clone(),
|
||||
list_id,
|
||||
item_id,
|
||||
name,
|
||||
form.quantity.trim().to_owned(),
|
||||
form.note.trim().to_owned(),
|
||||
normalize_category_id(form.category_id),
|
||||
parse_category_id(form.category_id),
|
||||
)
|
||||
.await?;
|
||||
state
|
||||
.hub
|
||||
.publish_list_changed(list_id.clone(), revision)
|
||||
.publish_list_changed(list_id, revision)
|
||||
.await;
|
||||
list_fragment_response(&state, &user, &list_id).await
|
||||
list_fragment_response(&state, &user, list_id).await
|
||||
}
|
||||
|
||||
async fn delete_item(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path((list_id, item_id)): Path<(String, String)>,
|
||||
Path((list_id, item_id)): Path<(i64, i64)>,
|
||||
LoggedForm(form): LoggedForm<CsrfForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &list_id).await?;
|
||||
let revision = state.db.delete_item(list_id.clone(), item_id).await?;
|
||||
require_access(&state, list_id).await?;
|
||||
let revision = state.db.delete_item(list_id, item_id).await?;
|
||||
state
|
||||
.hub
|
||||
.publish_list_changed(list_id.clone(), revision)
|
||||
.publish_list_changed(list_id, revision)
|
||||
.await;
|
||||
list_fragment_response(&state, &user, &list_id).await
|
||||
list_fragment_response(&state, &user, list_id).await
|
||||
}
|
||||
|
||||
async fn create_category(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path(list_id): Path<String>,
|
||||
Path(list_id): Path<i64>,
|
||||
LoggedForm(form): LoggedForm<CategoryForm>,
|
||||
) -> Result<Response, AppError> {
|
||||
verify_csrf(&user, &form.csrf)?;
|
||||
require_access(&state, &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(
|
||||
"Category names must be between 1 and 60 characters.".into(),
|
||||
));
|
||||
}
|
||||
let revision = state.db.create_category(list_id.clone(), name).await?;
|
||||
let revision = state.db.create_category(list_id, name).await?;
|
||||
state
|
||||
.hub
|
||||
.publish_list_changed(list_id.clone(), revision)
|
||||
.publish_list_changed(list_id, revision)
|
||||
.await;
|
||||
|
||||
let access = require_access(&state, &list_id).await?;
|
||||
let items = state.db.items(list_id.clone()).await?;
|
||||
let access = require_access(&state, list_id).await?;
|
||||
let items = state.db.items(list_id).await?;
|
||||
let categories = state.db.categories(list_id).await?;
|
||||
Ok(html_response(views::category_created(
|
||||
&access,
|
||||
@@ -667,10 +667,10 @@ async fn accept_invitation(
|
||||
async fn list_stream(
|
||||
State(state): State<AppState>,
|
||||
user: CurrentUser,
|
||||
Path(list_id): Path<String>,
|
||||
Path(list_id): Path<i64>,
|
||||
websocket: WebSocketUpgrade,
|
||||
) -> Result<Response, AppError> {
|
||||
require_access(&state, &list_id).await?;
|
||||
require_access(&state, list_id).await?;
|
||||
let state_for_socket = state.clone();
|
||||
let user_for_socket = user.clone();
|
||||
Ok(websocket
|
||||
@@ -678,12 +678,12 @@ async fn list_stream(
|
||||
.into_response())
|
||||
}
|
||||
|
||||
async fn handle_socket(state: AppState, user: CurrentUser, list_id: String, socket: WebSocket) {
|
||||
async fn handle_socket(state: AppState, user: CurrentUser, list_id: i64, socket: WebSocket) {
|
||||
let subscription = state
|
||||
.hub
|
||||
.join(
|
||||
list_id.clone(),
|
||||
user.session.user.id.clone(),
|
||||
list_id,
|
||||
user.session.user.id,
|
||||
user.session.user.display_name.clone(),
|
||||
)
|
||||
.await;
|
||||
@@ -692,16 +692,16 @@ async fn handle_socket(state: AppState, user: CurrentUser, list_id: String, sock
|
||||
let mut heartbeat = tokio::time::interval(Duration::from_secs(30));
|
||||
heartbeat.tick().await;
|
||||
|
||||
match websocket_snapshot(&state, &user, &list_id, &subscription.presence).await {
|
||||
match websocket_snapshot(&state, &user, list_id, &subscription.presence).await {
|
||||
Ok(snapshot) => {
|
||||
if sender.send(Message::Text(snapshot.into())).await.is_err() {
|
||||
state.hub.leave(&list_id, &connection_id).await;
|
||||
state.hub.leave(list_id, &connection_id).await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
error!(%error, "could not render websocket snapshot");
|
||||
state.hub.leave(&list_id, &connection_id).await;
|
||||
state.hub.leave(list_id, &connection_id).await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -713,7 +713,7 @@ async fn handle_socket(state: AppState, user: CurrentUser, list_id: String, sock
|
||||
match event {
|
||||
Ok(HubEvent::ListChanged { list_id: event_list_id, revision }) if event_list_id == list_id => {
|
||||
tracing::debug!(%list_id, revision, "list changed on websocket");
|
||||
match websocket_list_update(&state, &user, &list_id).await {
|
||||
match websocket_list_update(&state, &user, list_id).await {
|
||||
Ok(update) => {
|
||||
if sender.send(Message::Text(update.into())).await.is_err() {
|
||||
break;
|
||||
@@ -726,14 +726,14 @@ async fn handle_socket(state: AppState, user: CurrentUser, list_id: String, sock
|
||||
}
|
||||
}
|
||||
Ok(HubEvent::PresenceChanged { list_id: event_list_id }) if event_list_id == list_id => {
|
||||
let presence = state.hub.presence(&list_id).await;
|
||||
let presence = state.hub.presence(list_id).await;
|
||||
let update = views::presence_panel(&presence, true).into_string();
|
||||
if sender.send(Message::Text(update.into())).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => {
|
||||
match websocket_snapshot(&state, &user, &list_id, &state.hub.presence(&list_id).await).await {
|
||||
match websocket_snapshot(&state, &user, list_id, &state.hub.presence(list_id).await).await {
|
||||
Ok(snapshot) => {
|
||||
if sender.send(Message::Text(snapshot.into())).await.is_err() {
|
||||
break;
|
||||
@@ -769,18 +769,18 @@ async fn handle_socket(state: AppState, user: CurrentUser, list_id: String, sock
|
||||
}
|
||||
}
|
||||
|
||||
state.hub.leave(&list_id, &connection_id).await;
|
||||
state.hub.leave(list_id, &connection_id).await;
|
||||
}
|
||||
|
||||
async fn websocket_snapshot(
|
||||
state: &AppState,
|
||||
user: &CurrentUser,
|
||||
list_id: &str,
|
||||
list_id: i64,
|
||||
presence: &[hub::PresenceUser],
|
||||
) -> Result<String, AppError> {
|
||||
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?;
|
||||
let items = state.db.items(list_id).await?;
|
||||
let categories = state.db.categories(list_id).await?;
|
||||
Ok(
|
||||
views::live_list_fragments(&access, &items, &categories, &user.session.csrf_token)
|
||||
.into_string()
|
||||
@@ -791,11 +791,11 @@ async fn websocket_snapshot(
|
||||
async fn websocket_list_update(
|
||||
state: &AppState,
|
||||
user: &CurrentUser,
|
||||
list_id: &str,
|
||||
list_id: i64,
|
||||
) -> Result<String, AppError> {
|
||||
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?;
|
||||
let items = state.db.items(list_id).await?;
|
||||
let categories = state.db.categories(list_id).await?;
|
||||
Ok(
|
||||
views::live_list_fragments(&access, &items, &categories, &user.session.csrf_token)
|
||||
.into_string(),
|
||||
@@ -805,11 +805,11 @@ async fn websocket_list_update(
|
||||
async fn list_fragment_response(
|
||||
state: &AppState,
|
||||
user: &CurrentUser,
|
||||
list_id: &str,
|
||||
list_id: i64,
|
||||
) -> Result<Response, AppError> {
|
||||
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?;
|
||||
let items = state.db.items(list_id).await?;
|
||||
let categories = state.db.categories(list_id).await?;
|
||||
Ok(html_response(views::list_items_fragment(
|
||||
&access,
|
||||
&items,
|
||||
@@ -821,11 +821,11 @@ async fn list_fragment_response(
|
||||
|
||||
async fn require_access(
|
||||
state: &AppState,
|
||||
list_id: &str,
|
||||
list_id: i64,
|
||||
) -> Result<db::GroceryList, AppError> {
|
||||
state
|
||||
.db
|
||||
.list_access(list_id.to_owned())
|
||||
.list_access(list_id)
|
||||
.await?
|
||||
.ok_or(AppError::NotFound)
|
||||
}
|
||||
@@ -856,8 +856,10 @@ fn verify_csrf(user: &CurrentUser, token: &str) -> Result<(), AppError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn normalize_category_id(category_id: Option<String>) -> Option<String> {
|
||||
category_id.filter(|category_id| !category_id.trim().is_empty())
|
||||
fn parse_category_id(category_id: Option<String>) -> Option<i64> {
|
||||
category_id
|
||||
.filter(|category_id| !category_id.trim().is_empty())
|
||||
.and_then(|category_id| category_id.trim().parse().ok())
|
||||
}
|
||||
|
||||
async fn can_register(state: &AppState, invite: Option<&str>) -> Result<bool, AppError> {
|
||||
|
||||
Reference in New Issue
Block a user