One error response format: { error, code, details? } (step 1)

- error::ApiError (ResponseError): status + stable snake_case code +
  display-safe message + optional details. From<anyhow>/From<diesel> and
  into_http_internal_err become a 500 "internal" whose cause is logged,
  never sent.
- JSON body, query and path parse failures, the Claims 401 and unknown
  routes now answer in the format instead of actix's plain text.
- tags handlers return ApiResult with codes (invalid_tag_name,
  tag_not_found, tag_name_taken, no_files, too_many_files,
  nothing_to_change, tag_added_and_removed). The rename 409 keeps
  existing_tag at the top level for older clients.
- Enhance's error_response builds through ApiError; its codes are
  unchanged, and an internal error no longer sends its message.
- duplicates: eight 500s sent format!("{:?}", e) to the client.

Refs #202

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cameron CordesandClaude Opus 5.5 committed 2026-11-01 13:03:06 -05:00
1 parent 96a2858a50
commit 13c244765f
7 files changed
+470 -146

No files matched your search

+5 -2
View File
@@ -6,7 +6,6 @@ use anyhow::{Context, anyhow};
use chrono::{DateTime, Utc};
use log::error;
use actix_web::error::ErrorUnauthorized;
use actix_web::{Error, FromRequest, HttpRequest, dev, http::header};
use futures::future::{Ready, err, ok};
use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};
@@ -90,7 +89,11 @@ impl FromRequest for Claims {
.map_or_else(
|e| {
error!("{}", e);
err(ErrorUnauthorized("Bad token"))
err(crate::error::ApiError::unauthorized(
"unauthorized",
"Your session has expired. Sign in again.",
)
.into())
},
ok,
)