tags, folder rename, other stuff

This commit is contained in:
2025-10-15 21:32:17 +02:00
parent b5119a8e6f
commit 068f96880b
13 changed files with 525 additions and 130 deletions
+2 -1
View File
@@ -167,7 +167,8 @@ pub async fn update_correspondent(
}
if new_name.is_none() && new_metadata.is_none() {
return Err(AppError::bad_request("no changes supplied"));
let usage = load_usage_for_correspondent(&mut conn, correspondent_id)?;
return Ok(Json(build_summary(existing.clone(), usage)));
}
let mut changeset = CorrespondentChangeset::default();
+46 -24
View File
@@ -39,7 +39,9 @@ pub struct EnsureFolderPathRequest {
#[derive(Deserialize)]
pub struct UpdateFolderRequest {
pub parent_id: Option<Uuid>,
#[serde(default)]
pub parent_id: Option<Option<Uuid>>,
pub name: Option<String>,
}
#[derive(Serialize)]
@@ -597,50 +599,69 @@ pub async fn delete_folder(
Ok(StatusCode::NO_CONTENT)
}
pub async fn update_folder_parent(
pub async fn update_folder(
State(state): State<AppState>,
Path(folder_id): Path<Uuid>,
Json(payload): Json<UpdateFolderRequest>,
) -> AppResult<StatusCode> {
if payload.parent_id == Some(folder_id) {
return Err(AppError::bad_request("folder cannot be its own parent"));
}
let mut conn = state.db()?;
conn.transaction::<(), AppError, _>(|conn| {
let folder: Folder = folders::table.find(folder_id).first(conn)?;
let current_parent = folder.parent_id;
let next_parent = payload.parent_id;
if current_parent == next_parent {
return Ok(());
}
let mut next_parent = folder.parent_id;
let mut parent_changed = false;
if let Some(parent_id) = next_parent {
let _parent: Folder = folders::table.find(parent_id).first(conn)?;
}
if let Some(parent_id) = next_parent {
let descendant_ids = gather_descendant_folder_ids(conn, folder_id)?;
if descendant_ids.contains(&parent_id) {
return Err(AppError::bad_request(
"cannot move folder into itself or a descendant",
));
if let Some(parent_request) = payload.parent_id {
if parent_request == Some(folder_id) {
return Err(AppError::bad_request("folder cannot be its own parent"));
}
if let Some(parent_id) = parent_request {
let _parent: Folder = folders::table.find(parent_id).first(conn)?;
let descendant_ids = gather_descendant_folder_ids(conn, folder_id)?;
if descendant_ids.contains(&parent_id) {
return Err(AppError::bad_request(
"cannot move folder into itself or a descendant",
));
}
}
parent_changed = parent_request != folder.parent_id;
next_parent = parent_request;
}
let mut new_name = folder.name.clone();
let mut name_changed = false;
if let Some(name) = payload.name {
let trimmed = name.trim();
if trimmed.is_empty() {
return Err(AppError::bad_request("name must not be empty"));
}
if trimmed != folder.name {
new_name = trimmed.to_string();
name_changed = true;
}
}
if !parent_changed && !name_changed {
return Ok(());
}
let conflict = if let Some(parent_id) = next_parent {
folders::table
.filter(folders::parent_id.eq(Some(parent_id)))
.filter(folders::name.eq(&folder.name))
.filter(folders::name.eq(&new_name))
.filter(folders::id.ne(folder_id))
.first::<Folder>(conn)
.optional()?
} else {
folders::table
.filter(folders::parent_id.is_null())
.filter(folders::name.eq(&folder.name))
.filter(folders::name.eq(&new_name))
.filter(folders::id.ne(folder_id))
.first::<Folder>(conn)
.optional()?
@@ -652,11 +673,12 @@ pub async fn update_folder_parent(
));
}
let new_path = build_path_cache(conn, next_parent, &folder.name)?;
let new_path = build_path_cache(conn, next_parent, &new_name)?;
diesel::update(folders::table.find(folder_id))
.set((
folders::parent_id.eq(next_parent),
folders::name.eq(&new_name),
folders::path_cache.eq(new_path),
))
.execute(conn)?;
+1 -1
View File
@@ -95,7 +95,7 @@ pub fn create_router(state: AppState) -> Router<()> {
.route("/path", post(folders::ensure_folder_path))
.route(
"/:id",
delete(folders::delete_folder).patch(folders::update_folder_parent),
delete(folders::delete_folder).patch(folders::update_folder),
)
.route("/:id/contents", get(folders::list_folder_contents))
.route("/:id/documents", get(folders::search_documents));
+20 -2
View File
@@ -112,7 +112,16 @@ pub async fn update_tag(
if matches!(label_class, NullableValue::Omitted)
&& matches!(color_class, NullableValue::Omitted)
{
return Err(AppError::bad_request("no changes supplied"));
let usage_count: i64 = document_tags::table
.filter(document_tags::tag_id.eq(tag_id))
.select(count_star())
.first(&mut conn)?;
return Ok(Json(TagCatalogEntry {
id: existing.id,
label: existing.label.clone(),
color: existing.color.clone(),
usage_count,
}));
}
let mut new_label: Option<String> = None;
@@ -163,7 +172,16 @@ pub async fn update_tag(
}
if !label_changed && !color_changed {
return Err(AppError::bad_request("no changes supplied"));
let usage_count: i64 = document_tags::table
.filter(document_tags::tag_id.eq(tag_id))
.select(count_star())
.first(&mut conn)?;
return Ok(Json(TagCatalogEntry {
id: existing.id,
label: existing.label.clone(),
color: existing.color.clone(),
usage_count,
}));
}
let changeset = UpdateTagChangeset {