package api import ( "errors" "fmt" "log/slog" "net/http" "git.fabledsword.com/bvandeusen/minstrel/internal/apierror" "git.fabledsword.com/bvandeusen/minstrel/internal/library" ) // fileRemoveAPIError answers a delete that could not reach the track's file // (#3918). Both delete endpoints use it, so the operator gets the same // explanation from the admin remove-track action and from quarantine's Delete // file. // // The unwritable case is a 409 rather than a 500 because nothing is broken: the // request conflicts with how the library is mounted, and the fix is the // operator's. The message names the directory — removal writes to the parent, // not the file — and the uid/gid the process runs as, which is the half of a // permission problem invisible from the host. Every case says nothing was // deleted, because that is exactly what the operator will be worried about. func fileRemoveAPIError(err error) (*apierror.Error, bool) { var fre *library.FileRemoveError if !errors.As(err, &fre) { return nil, false } if fre.NotWritable() { return &apierror.Error{ Status: http.StatusConflict, Code: "library_not_writable", Message: fmt.Sprintf( "Minstrel runs as uid %d, gid %d and cannot delete from %s (%s). "+ "The library mount must be writable by that user. Nothing was deleted.", fre.UID, fre.GID, fre.Dir(), fre.Reason()), Cause: err, }, true } return &apierror.Error{ Status: http.StatusInternalServerError, Code: "file_delete_failed", Message: fmt.Sprintf("Could not delete %s (%s). Nothing was deleted.", fre.Path, fre.Reason()), Cause: err, }, true } // logFileRemoveFailure records a delete that could not reach its file. An // unwritable library is an environment fact the operator can fix, so it is a // Warn; anything else is a real fault. func logFileRemoveFailure(logger *slog.Logger, apiErr *apierror.Error, attrs ...any) { attrs = append(attrs, "code", apiErr.Code, "err", apiErr.Cause) if apiErr.Status == http.StatusConflict { logger.Warn("api: track file could not be deleted", attrs...) return } logger.Error("api: track file could not be deleted", attrs...) }