API: Improve logging of bad request errors across all endpoints #271

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-07-10 09:38:36 +02:00
parent 6bfbad40af
commit 38da638f88
34 changed files with 64 additions and 79 deletions

View File

@@ -103,10 +103,18 @@ func AbortUnexpectedError(c *gin.Context) {
} }
func AbortBadRequest(c *gin.Context, errs ...error) { func AbortBadRequest(c *gin.Context, errs ...error) {
// Log and attach validation errors to the context.
for _, err := range errs { for _, err := range errs {
_ = c.Error(err) if err != nil {
// Add error message to the debug logs.
log.Debugf("api-v1: %s", err)
// Attach error to the current context
_ = c.Error(err)
}
} }
// Abort request with error 400.
Abort(c, http.StatusBadRequest, i18n.ErrBadRequest) Abort(c, http.StatusBadRequest, i18n.ErrBadRequest)
} }

View File

@@ -102,7 +102,7 @@ func CreateAlbum(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -194,8 +194,7 @@ func UpdateAlbum(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err = c.BindJSON(frm); err != nil { if err = c.BindJSON(frm); err != nil {
log.Error(err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }
@@ -439,7 +438,7 @@ func CloneAlbums(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err = c.BindJSON(&frm); err != nil { if err = c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -500,7 +499,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -531,8 +530,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
photos, err := query.SelectedPhotos(frm) photos, err := query.SelectedPhotos(frm)
if err != nil { if err != nil {
log.Errorf("album: %s", err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }
@@ -611,7 +609,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -42,7 +42,7 @@ func SearchAlbums(router *gin.RouterGroup) {
// Abort if request params are invalid. // Abort if request params are invalid.
if err = c.MustBindWith(&frm, binding.Form); err != nil { if err = c.MustBindWith(&frm, binding.Form); err != nil {
event.AuditWarn([]string{ClientIP(c), "session %s", "albums", "search", "form invalid", "%s"}, s.RefID, err) event.AuditWarn([]string{ClientIP(c), "session %s", "albums", "search", "form invalid", "%s"}, s.RefID, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -35,7 +35,7 @@ func BatchAlbumsDelete(router *gin.RouterGroup) {
var frm form.Selection var frm form.Selection
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -35,7 +35,7 @@ func BatchLabelsDelete(router *gin.RouterGroup) {
var frm form.Selection var frm form.Selection
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -45,7 +45,7 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -116,7 +116,7 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
var frm form.Selection var frm form.Selection
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -186,7 +186,7 @@ func BatchPhotosApprove(router *gin.RouterGroup) {
var frm form.Selection var frm form.Selection
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -246,7 +246,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
var frm form.Selection var frm form.Selection
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -313,7 +313,7 @@ func BatchPhotosDelete(router *gin.RouterGroup) {
var frm form.Selection var frm form.Selection
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -90,8 +90,7 @@ func SaveConfigOptions(router *gin.RouterGroup) {
} }
if err := c.BindJSON(&v); err != nil { if err := c.BindJSON(&v); err != nil {
log.Errorf("config: %s (bind json)", err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }

View File

@@ -79,7 +79,7 @@ func SaveSettings(router *gin.RouterGroup) {
// Set values from request. // Set values from request.
if err := c.BindJSON(settings); err != nil { if err := c.BindJSON(settings); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -114,7 +114,7 @@ func SaveSettings(router *gin.RouterGroup) {
// Set values from request. // Set values from request.
if err := c.BindJSON(settings); err != nil { if err := c.BindJSON(settings); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -70,7 +70,7 @@ func UpdateFace(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -40,7 +40,7 @@ func SearchFaces(router *gin.RouterGroup) {
err := c.MustBindWith(&frm, binding.Form) err := c.MustBindWith(&frm, binding.Form)
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -36,7 +36,7 @@ func SendFeedback(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -58,7 +58,7 @@ func SearchFolders(router *gin.RouterGroup, urlPath, rootName, rootPath string)
err := c.MustBindWith(&frm, binding.Form) err := c.MustBindWith(&frm, binding.Form)
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -68,7 +68,7 @@ func StartImport(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -52,7 +52,7 @@ func StartIndexing(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -55,7 +55,7 @@ func UpdateLabel(router *gin.RouterGroup) {
// Set form values from request. // Set form values from request.
if frmErr = c.BindJSON(frm); frmErr != nil { if frmErr = c.BindJSON(frm); frmErr != nil {
AbortBadRequest(c) AbortBadRequest(c, frmErr)
return return
} else if frmErr = frm.Validate(); frmErr != nil { } else if frmErr = frm.Validate(); frmErr != nil {
AbortInvalidName(c) AbortInvalidName(c)

View File

@@ -38,7 +38,7 @@ func SearchLabels(router *gin.RouterGroup) {
err := c.MustBindWith(&frm, binding.Form) err := c.MustBindWith(&frm, binding.Form)
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -29,8 +29,7 @@ func UpdateLink(c *gin.Context) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
log.Debugf("share: %s", err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }
@@ -109,8 +108,7 @@ func CreateLink(c *gin.Context) {
var frm form.Link var frm form.Link
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
log.Debugf("share: %s", err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }

View File

@@ -97,8 +97,7 @@ func CreateMarker(router *gin.RouterGroup) {
// Initialize form. // Initialize form.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
log.Errorf("faces: %s (bind marker form)", err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }
@@ -113,8 +112,7 @@ func CreateMarker(router *gin.RouterGroup) {
// Validate form values. // Validate form values.
if err = frm.Validate(); err != nil { if err = frm.Validate(); err != nil {
log.Errorf("faces: %s (validate new marker)", err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} else if frm.W <= 0 || frm.H <= 0 { } else if frm.W <= 0 || frm.H <= 0 {
log.Errorf("faces: width and height must be greater than zero") log.Errorf("faces: width and height must be greater than zero")
@@ -130,8 +128,7 @@ func CreateMarker(router *gin.RouterGroup) {
// Update marker from form values. // Update marker from form values.
if err = marker.Create(); err != nil { if err = marker.Create(); err != nil {
log.Errorf("faces: %s (create marker)", err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }
@@ -210,14 +207,14 @@ func UpdateMarker(router *gin.RouterGroup) {
return return
} else if err = c.BindJSON(&frm); err != nil { } else if err = c.BindJSON(&frm); err != nil {
log.Errorf("faces: %s (bind marker form)", err) log.Errorf("faces: %s (bind marker form)", err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
// Validate form values. // Validate form values.
if err = frm.Validate(); err != nil { if err = frm.Validate(); err != nil {
log.Errorf("faces: %s (validate updated marker)", err) log.Errorf("faces: %s (validate updated marker)", err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -75,7 +75,7 @@ func OAuthRevoke(router *gin.RouterGroup) {
// Get the auth token to be revoked from the submitted form values or the request header. // Get the auth token to be revoked from the submitted form values or the request header.
if err = c.ShouldBind(&frm); err != nil && authToken == "" { if err = c.ShouldBind(&frm); err != nil && authToken == "" {
event.AuditWarn([]string{clientIp, "oauth2", actor, action, "%s"}, err) event.AuditWarn([]string{clientIp, "oauth2", actor, action, "%s"}, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} else if frm.Empty() { } else if frm.Empty() {
frm.Token = authToken frm.Token = authToken

View File

@@ -59,7 +59,7 @@ func OAuthToken(router *gin.RouterGroup) {
frm.ClientSecret = clientSecret frm.ClientSecret = clientSecret
} else if err = c.ShouldBind(&frm); err != nil { } else if err = c.ShouldBind(&frm); err != nil {
event.AuditWarn([]string{clientIp, "oauth2", actor, action, "%s"}, err) event.AuditWarn([]string{clientIp, "oauth2", actor, action, "%s"}, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -47,7 +47,7 @@ func AddPhotoLabel(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err = c.BindJSON(frm); err != nil { if err = c.BindJSON(frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} else if err = frm.Validate(); err != nil { } else if err = frm.Validate(); err != nil {
AbortInvalidName(c) AbortInvalidName(c)
@@ -221,7 +221,7 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
} }
if err = c.BindJSON(label); err != nil { if err = c.BindJSON(label); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -49,7 +49,7 @@ func SearchPhotos(router *gin.RouterGroup) {
// Abort if request params are invalid. // Abort if request params are invalid.
if err = c.MustBindWith(&frm, binding.Form); err != nil { if err = c.MustBindWith(&frm, binding.Form); err != nil {
event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePhotos), "form invalid", "%s"}, s.RefID, err) event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePhotos), "form invalid", "%s"}, s.RefID, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return frm, s, err return frm, s, err
} }
@@ -85,7 +85,7 @@ func SearchPhotos(router *gin.RouterGroup) {
// Ok? // Ok?
if err != nil { if err != nil {
event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePhotos), "search", "%s"}, s.RefID, err) event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePhotos), "search", "%s"}, s.RefID, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -114,7 +114,7 @@ func SearchPhotos(router *gin.RouterGroup) {
if err != nil { if err != nil {
event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePhotos), "view", "%s"}, s.RefID, err) event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePhotos), "view", "%s"}, s.RefID, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -49,7 +49,7 @@ func SearchGeo(router *gin.RouterGroup) {
// Abort if request params are invalid. // Abort if request params are invalid.
if err = c.MustBindWith(&frm, binding.Form); err != nil { if err = c.MustBindWith(&frm, binding.Form); err != nil {
event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePlaces), "form invalid", "%s"}, s.RefID, err) event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePlaces), "form invalid", "%s"}, s.RefID, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -74,7 +74,7 @@ func SearchGeo(router *gin.RouterGroup) {
// Ok? // Ok?
if err != nil { if err != nil {
event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePlaces), "search", "%s"}, s.RefID, err) event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePlaces), "search", "%s"}, s.RefID, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -149,7 +149,7 @@ func AddService(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -162,8 +162,7 @@ func AddService(router *gin.RouterGroup) {
m, err := entity.AddService(frm) m, err := entity.AddService(frm)
if err != nil { if err != nil {
log.Error(err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }
@@ -218,8 +217,7 @@ func UpdateService(router *gin.RouterGroup) {
// 2) Update form with values from request // 2) Update form with values from request
if err = c.BindJSON(&frm); err != nil { if err = c.BindJSON(&frm); err != nil {
log.Error(err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }

View File

@@ -43,14 +43,14 @@ func SearchServices(router *gin.RouterGroup) {
err := c.MustBindWith(&frm, binding.Form) err := c.MustBindWith(&frm, binding.Form)
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
result, err := search.Accounts(frm) result, err := search.Accounts(frm)
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -49,7 +49,7 @@ func UploadToService(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err = c.BindJSON(&frm); err != nil { if err = c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -36,7 +36,7 @@ func CreateSession(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
event.AuditWarn([]string{clientIp, "create session", "invalid request", "%s"}, err) event.AuditWarn([]string{clientIp, "create session", "invalid request", "%s"}, err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -87,7 +87,7 @@ func UpdateSubject(router *gin.RouterGroup) {
return return
} else if err = c.BindJSON(frm); err != nil { } else if err = c.BindJSON(frm); err != nil {
log.Errorf("subject: %s (update form)", err) log.Errorf("subject: %s (update form)", err)
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -40,7 +40,7 @@ func SearchSubjects(router *gin.RouterGroup) {
err := c.MustBindWith(&frm, binding.Form) err := c.MustBindWith(&frm, binding.Form)
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -8180,16 +8180,10 @@
1000000000, 1000000000,
60000000000, 60000000000,
3600000000000, 3600000000000,
-9223372036854775808,
9223372036854775807,
1, 1,
1000, 1000,
1000000, 1000000,
1000000000, 1000000000,
60000000000,
3600000000000,
-9223372036854775808,
9223372036854775807,
1, 1,
1000, 1000,
1000000, 1000000,
@@ -8206,16 +8200,10 @@
"Second", "Second",
"Minute", "Minute",
"Hour", "Hour",
"minDuration",
"maxDuration",
"Nanosecond", "Nanosecond",
"Microsecond", "Microsecond",
"Millisecond", "Millisecond",
"Second", "Second",
"Minute",
"Hour",
"minDuration",
"maxDuration",
"Nanosecond", "Nanosecond",
"Microsecond", "Microsecond",
"Millisecond", "Millisecond",

View File

@@ -47,7 +47,7 @@ func FindUserSessions(router *gin.RouterGroup) {
// Abort if invalid. // Abort if invalid.
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }
@@ -61,7 +61,7 @@ func FindUserSessions(router *gin.RouterGroup) {
result, err := search.Sessions(frm) result, err := search.Sessions(frm)
if err != nil { if err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -58,8 +58,7 @@ func UpdateUser(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err = c.BindJSON(&f); err != nil { if err = c.BindJSON(&f); err != nil {
log.Error(err) AbortBadRequest(c, err)
AbortBadRequest(c)
return return
} }

View File

@@ -282,7 +282,7 @@ func ProcessUserUpload(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }

View File

@@ -47,7 +47,7 @@ func ZipCreate(router *gin.RouterGroup) {
// Assign and validate request form values. // Assign and validate request form values.
if err := c.BindJSON(&frm); err != nil { if err := c.BindJSON(&frm); err != nil {
AbortBadRequest(c) AbortBadRequest(c, err)
return return
} }