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) {
// Log and attach validation errors to the context.
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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -97,8 +97,7 @@ func CreateMarker(router *gin.RouterGroup) {
// Initialize form.
if err := c.BindJSON(&frm); err != nil {
log.Errorf("faces: %s (bind marker form)", err)
AbortBadRequest(c)
AbortBadRequest(c, err)
return
}
@@ -113,8 +112,7 @@ func CreateMarker(router *gin.RouterGroup) {
// Validate form values.
if err = frm.Validate(); err != nil {
log.Errorf("faces: %s (validate new marker)", err)
AbortBadRequest(c)
AbortBadRequest(c, err)
return
} else if frm.W <= 0 || frm.H <= 0 {
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.
if err = marker.Create(); err != nil {
log.Errorf("faces: %s (create marker)", err)
AbortBadRequest(c)
AbortBadRequest(c, err)
return
}
@@ -210,14 +207,14 @@ func UpdateMarker(router *gin.RouterGroup) {
return
} else if err = c.BindJSON(&frm); err != nil {
log.Errorf("faces: %s (bind marker form)", err)
AbortBadRequest(c)
AbortBadRequest(c, err)
return
}
// Validate form values.
if err = frm.Validate(); err != nil {
log.Errorf("faces: %s (validate updated marker)", err)
AbortBadRequest(c)
AbortBadRequest(c, err)
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.
if err = c.ShouldBind(&frm); err != nil && authToken == "" {
event.AuditWarn([]string{clientIp, "oauth2", actor, action, "%s"}, err)
AbortBadRequest(c)
AbortBadRequest(c, err)
return
} else if frm.Empty() {
frm.Token = authToken

View File

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

View File

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

View File

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

View File

@@ -49,7 +49,7 @@ func SearchGeo(router *gin.RouterGroup) {
// Abort if request params are invalid.
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)
AbortBadRequest(c)
AbortBadRequest(c, err)
return
}
@@ -74,7 +74,7 @@ func SearchGeo(router *gin.RouterGroup) {
// Ok?
if err != nil {
event.AuditWarn([]string{ClientIP(c), "session %s", string(acl.ResourcePlaces), "search", "%s"}, s.RefID, err)
AbortBadRequest(c)
AbortBadRequest(c, err)
return
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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