API: Apply "golangci-lint" recommendations #5330

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-11-22 09:25:01 +01:00
parent 264bc78d51
commit 4eac10c9d1
35 changed files with 146 additions and 146 deletions

View File

@@ -565,13 +565,11 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
// Find album by UID.
album, err := query.AlbumByUID(uid)
if err != nil {
switch {
case err != nil, !album.HasID():
AbortAlbumNotFound(c)
return
} else if !album.HasID() {
AbortAlbumNotFound(c)
return
} else if frm.Empty() {
case frm.Empty():
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
return
}

View File

@@ -25,11 +25,12 @@ Additional information can be found in our Developer Guide:
package api
import (
_ "net/http"
// Blank imports register HTTP handlers, models, translations, and form bindings via init side effects.
_ "net/http" // register net/http handlers referenced by swagger
_ "github.com/gin-gonic/gin"
_ "github.com/gin-gonic/gin" // register gin metadata used by swaggo
_ "github.com/photoprism/photoprism/internal/auth/acl"
_ "github.com/photoprism/photoprism/internal/auth/acl" // embed ACL docs
_ "github.com/photoprism/photoprism/internal/entity"
_ "github.com/photoprism/photoprism/internal/entity/query"
_ "github.com/photoprism/photoprism/internal/event"

View File

@@ -35,14 +35,11 @@ func AuthAny(c *gin.Context, resource acl.Resource, perms acl.Permissions) (s *e
c.Header(header.CacheControl, header.CacheControlNoStore)
// Allow requests based on an access token for specific resources.
switch resource {
case acl.ResourceVision:
if perms.Contains(acl.ActionUse) && vision.ServiceApi && vision.ServiceKey != "" && vision.ServiceKey == authToken {
if resource == acl.ResourceVision && perms.Contains(acl.ActionUse) && vision.ServiceApi && vision.ServiceKey != "" && vision.ServiceKey == authToken {
s = entity.NewSessionFromToken(c, authToken, acl.ResourceVision.String(), "service-key")
event.AuditInfo([]string{clientIp, "%s", "%s %s as %s", status.Granted}, s.RefID, perms.First(), string(resource), s.GetClientRole().String())
return s
}
}
// Find active session to perform authorization check or deny if no session was found.
if s = Session(clientIp, authToken); s == nil {

View File

@@ -58,10 +58,6 @@ func (r *CloseableResponseRecorder) CloseNotify() <-chan bool {
return r.closeCh
}
func (r *CloseableResponseRecorder) closeClient() {
r.closeCh <- true
}
// NewApiTest returns new API test helper.
func NewApiTest() (app *gin.Engine, router *gin.RouterGroup, conf *config.Config) {
gin.SetMode(gin.TestMode)

View File

@@ -329,25 +329,27 @@ func BatchPhotosDelete(router *gin.RouterGroup) {
}
// Get selection or all archived photos if f.All is true.
if len(frm.Photos) == 0 && !frm.All {
switch {
case len(frm.Photos) == 0 && !frm.All:
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
return
} else if frm.All {
case frm.All:
photos, err = query.ArchivedPhotos(1000000, 0)
} else {
default:
photos, err = query.SelectedPhotos(frm)
}
// Abort if the query failed or no photos were found.
if err != nil {
switch {
case err != nil:
log.Errorf("archive: %s", err)
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
return
} else if len(photos) > 0 {
log.Infof("archive: deleting %s", english.Plural(len(photos), "photo", "photos"))
} else {
case len(photos) == 0:
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
return
default:
log.Infof("archive: deleting %s", english.Plural(len(photos), "photo", "photos"))
}
var deleted entity.Photos

View File

@@ -73,7 +73,7 @@ func BatchPhotosEdit(router *gin.RouterGroup) {
return
}
preloadedPhotos := map[string]*entity.Photo{}
var preloadedPhotos map[string]*entity.Photo
if hydrated, err := query.PhotoPreloadByUIDs(photos.UIDs()); err != nil {
log.Errorf("batch: failed to preload photo selection: %s", err)
@@ -107,7 +107,7 @@ func BatchPhotosEdit(router *gin.RouterGroup) {
// Refresh selected photos from database?
if !savedAny {
// Don't refresh.
} else if photos, count, err = search.BatchPhotos(frm.Photos, s); err != nil {
} else if photos, _, err = search.BatchPhotos(frm.Photos, s); err != nil {
log.Errorf("batch: %s (refresh selection)", clean.Error(err))
}

View File

@@ -111,7 +111,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
}
for i := 0; i < len(name); i++ {
b := name[i]
if !(b == '-' || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9')) {
if b != '-' && (b < 'a' || b > 'z') && (b < '0' || b > '9') {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid name chars", status.Failed})
AbortBadRequest(c)
return

View File

@@ -72,7 +72,7 @@ func SaveConfigOptions(router *gin.RouterGroup) {
v := make(entity.Values)
if fs.FileExists(fileName) {
yamlData, err := os.ReadFile(fileName)
yamlData, err := os.ReadFile(fileName) // #nosec G304 file path validated above
if err != nil {
log.Errorf("config: failed loading values from %s (%s)", clean.Log(fileName), err)

View File

@@ -130,7 +130,7 @@ func DownloadAlbum(router *gin.RouterGroup) {
alias = file.DownloadName(dlName, seq)
}
aliases[key] += 1
aliases[key]++
if fs.FileExists(fileName) {
if zipErr := fs.ZipFile(zipWriter, fileName, alias, false); zipErr != nil {

View File

@@ -133,26 +133,27 @@ func OAuthRevoke(router *gin.RouterGroup) {
}
// Check revocation request and abort if invalid.
if err != nil {
switch {
case err != nil:
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Error(err)}, clean.Log(sess.RefID), role.String())
AbortInvalidCredentials(c)
return
} else if sess == nil {
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String())
case sess == nil:
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, "", role.String())
AbortInvalidCredentials(c)
return
} else if sess.Abort(c) {
case sess.Abort(c):
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String())
return
} else if !sess.IsClient() {
case !sess.IsClient():
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String())
c.AbortWithStatusJSON(http.StatusForbidden, i18n.NewResponse(http.StatusForbidden, i18n.ErrForbidden))
return
} else if sUserUID != "" && sess.UserUID != sUserUID {
case sUserUID != "" && sess.UserUID != sUserUID:
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", authn.ErrUnauthorized.Error()}, clean.Log(sess.RefID), role.String())
AbortInvalidCredentials(c)
return
} else {
default:
event.AuditInfo([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Granted}, clean.Log(sess.RefID), role.String())
}

View File

@@ -18,7 +18,7 @@ import (
)
func TestOAuthRevoke(t *testing.T) {
const tokenPath = "/api/v1/oauth/token"
const tokenPath = "/api/v1/oauth/token" // #nosec G101 test constant, not a credential
const revokePath = "/api/v1/oauth/revoke"
t.Run("ClientSuccessToken", func(t *testing.T) {

View File

@@ -86,11 +86,12 @@ func OAuthToken(router *gin.RouterGroup) {
return
}
if frm.ClientID != "" {
switch {
case frm.ClientID != "":
actor = fmt.Sprintf("client %s", clean.Log(frm.ClientID))
} else if frm.Username != "" {
case frm.Username != "":
actor = fmt.Sprintf("user %s", clean.Log(frm.Username))
} else if frm.GrantType == authn.GrantPassword {
case frm.GrantType == authn.GrantPassword:
actor = "unknown user"
}
@@ -150,17 +151,18 @@ func OAuthToken(router *gin.RouterGroup) {
authUser, authProvider, authMethod, authErr := entity.Auth(loginForm, nil, c)
if authProvider.IsClient() {
switch {
case authProvider.IsClient():
event.AuditErr([]string{clientIp, "oauth2", actor, action, status.Denied})
AbortInvalidCredentials(c)
return
} else if authMethod.Is(authn.Method2FA) && errors.Is(authErr, authn.ErrPasscodeRequired) {
case authMethod.Is(authn.Method2FA) && errors.Is(authErr, authn.ErrPasscodeRequired):
// Ok.
} else if authErr != nil {
case authErr != nil:
event.AuditErr([]string{clientIp, "oauth2", actor, action, status.Error(authErr)})
AbortInvalidCredentials(c)
return
} else if !authUser.Equal(s.GetUser()) {
case !authUser.Equal(s.GetUser()):
event.AuditErr([]string{clientIp, "oauth2", actor, action, authn.ErrUserDoesNotMatch.Error()})
AbortInvalidCredentials(c)
return

View File

@@ -51,8 +51,7 @@ func OIDCLogin(router *gin.RouterGroup) {
}
// Check request rate limit.
var r *limiter.Request
r = limiter.Login.Request(clientIp)
r := limiter.Login.Request(clientIp)
// Abort if failure rate limit is exceeded.
if r.Reject() || limiter.Auth.Reject(clientIp) {

View File

@@ -66,8 +66,7 @@ func OIDCRedirect(router *gin.RouterGroup) {
}
// Check request rate limit.
var r *limiter.Request
r = limiter.Login.Request(clientIp)
r := limiter.Login.Request(clientIp)
// Abort if failure rate limit is exceeded.
if r.Reject() || limiter.Auth.Reject(clientIp) {
@@ -145,17 +144,18 @@ func OIDCRedirect(router *gin.RouterGroup) {
event.AuditInfo([]string{clientIp, "create session", "oidc", "found user", userName})
// Check if the account is enabled and the OIDC Subject ID matches.
if !user.CanLogIn() {
switch {
case !user.CanLogIn():
event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrAccountDisabled.Error()})
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrAccountDisabled.Error())
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
} else if authn.ProviderOIDC.NotEqual(user.AuthProvider) {
case authn.ProviderOIDC.NotEqual(user.AuthProvider):
event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrAuthProviderIsNotOIDC.Error()})
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrAuthProviderIsNotOIDC.Error())
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
} else if user.AuthID == "" || oidcUser.AuthID == "" || user.AuthID != oidcUser.AuthID {
case user.AuthID == "" || oidcUser.AuthID == "" || user.AuthID != oidcUser.AuthID:
event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrInvalidAuthID.Error()})
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrInvalidAuthID.Error())
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
@@ -228,12 +228,12 @@ func OIDCRedirect(router *gin.RouterGroup) {
}
// Set user avatar image?
if avatarUrl := userInfo.Picture; avatarUrl == "" || user.HasAvatar() {
// Do nothing.
} else if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil {
if avatarUrl := userInfo.Picture; avatarUrl != "" && !user.HasAvatar() {
if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil {
event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()})
}
} else if conf.UsersQuotaReached(conf.OIDCRole()) {
}
} else if conf.UsersQuotaReached(conf.OIDCRole()) { //nolint:gocritic
userName = oidcUser.Username()
event.AuditWarn([]string{clientIp, "create session", "oidc", "create user", userName, authn.ErrUsersQuotaExceeded.Error()})
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrUsersQuotaExceeded.Error())
@@ -247,7 +247,7 @@ func OIDCRedirect(router *gin.RouterGroup) {
// Resolve potential naming conflict by adding a random number to the username.
if found := entity.FindUserByName(userName); found != nil {
userName = userName + rnd.Base10(6)
userName += rnd.Base10(6)
}
event.AuditInfo([]string{clientIp, "create session", "oidc", "create user", userName})

View File

@@ -1,6 +1,7 @@
package api
import (
"errors"
"net/http"
"strconv"
@@ -136,6 +137,11 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
return
}
if labelId < 0 {
AbortBadRequest(c, errors.New("invalid label id"))
return
}
label, err := query.PhotoLabel(m.ID, uint(labelId))
if err != nil {
@@ -143,13 +149,14 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
return
}
if (label.LabelSrc == classify.SrcManual || label.LabelSrc == entity.SrcBatch) && label.Uncertainty < 100 {
switch {
case (label.LabelSrc == classify.SrcManual || label.LabelSrc == entity.SrcBatch) && label.Uncertainty < 100:
logErr("label", entity.Db().Delete(&label).Error)
} else if label.LabelSrc != classify.SrcManual && label.LabelSrc != entity.SrcBatch {
case label.LabelSrc != classify.SrcManual && label.LabelSrc != entity.SrcBatch:
label.Uncertainty = 100
label.LabelSrc = entity.SrcManual
logErr("label", entity.Db().Save(&label).Error)
} else {
default:
logErr("label", entity.Db().Save(&label).Error)
}
@@ -212,6 +219,11 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
return
}
if labelId < 0 {
AbortBadRequest(c, errors.New("invalid label id"))
return
}
label, err := query.PhotoLabel(m.ID, uint(labelId))
if err != nil {

View File

@@ -47,15 +47,16 @@ func PhotoUnstack(router *gin.RouterGroup) {
return
}
if file.FilePrimary {
switch {
case file.FilePrimary:
log.Errorf("photo: cannot unstack primary file")
AbortBadRequest(c)
return
} else if file.FileSidecar {
case file.FileSidecar:
log.Errorf("photo: cannot unstack sidecar files")
AbortBadRequest(c)
return
} else if file.FileRoot != entity.RootOriginals {
case file.FileRoot != entity.RootOriginals:
log.Errorf("photo: only originals can be unstacked")
AbortBadRequest(c)
return
@@ -91,15 +92,16 @@ func PhotoUnstack(router *gin.RouterGroup) {
related, err := unstackFile.RelatedFiles(false)
if err != nil {
switch {
case err != nil:
log.Errorf("photo: %s (unstack %s)", err, clean.Log(baseName))
AbortEntityNotFound(c)
return
} else if related.Len() == 0 {
case related.Len() == 0:
log.Errorf("photo: found no files for %s (unstack)", clean.Log(baseName))
AbortEntityNotFound(c)
return
} else if related.Main == nil {
case related.Main == nil:
log.Errorf("photo: found no main media file for %s (unstack)", clean.Log(baseName))
AbortEntityNotFound(c)
return

View File

@@ -6,7 +6,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/photoprism/get"
"github.com/photoprism/photoprism/internal/server/process"
)
@@ -32,8 +31,7 @@ func StopServer(router *gin.RouterGroup) {
return
}
var options *config.Options
options = conf.Options()
options := conf.Options()
// Trigger restart.
//

View File

@@ -74,7 +74,7 @@ func UploadToService(router *gin.RouterGroup) {
alias = file.ShareBase(seq)
}
aliases[key] += 1
aliases[key]++
entity.FirstOrCreateFileShare(entity.NewFileShare(file.ID, m.ID, alias))
}

View File

@@ -100,13 +100,14 @@ func CreateSession(router *gin.RouterGroup) {
// Check authentication credentials.
if err = sess.LogIn(frm, c); err != nil {
if sess.GetMethod().IsNot(authn.Method2FA) {
switch {
case sess.GetMethod().IsNot(authn.Method2FA):
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
} else if errors.Is(err, authn.ErrPasscodeRequired) {
case errors.Is(err, authn.ErrPasscodeRequired):
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "code": 32, "message": i18n.Msg(i18n.ErrPasscodeRequired)})
// Return the reserved request rate limit tokens if password is correct, even if the verification code is missing.
r.Success()
} else {
default:
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "code": http.StatusUnauthorized, "message": i18n.Msg(i18n.ErrInvalidPasscode)})
}
return
@@ -119,17 +120,20 @@ func CreateSession(router *gin.RouterGroup) {
}
// Save session after successful authentication.
if sess, err = get.Session().Save(sess); err != nil {
event.AuditErr([]string{clientIp, status.Error(err)})
switch saved, saveErr := get.Session().Save(sess); {
case saveErr != nil:
event.AuditErr([]string{clientIp, status.Error(saveErr)})
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
return
} else if sess == nil {
case saved == nil:
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrUnexpected)})
return
} else if isNew {
event.AuditInfo([]string{clientIp, "session %s", "created"}, sess.RefID)
} else {
event.AuditInfo([]string{clientIp, "session %s", "updated"}, sess.RefID)
case isNew:
event.AuditInfo([]string{clientIp, "session %s", "created"}, saved.RefID)
sess = saved
default:
event.AuditInfo([]string{clientIp, "session %s", "updated"}, saved.RefID)
sess = saved
}
// Return the reserved request rate limit tokens after successful authentication.

View File

@@ -373,7 +373,7 @@ func TestDeleteSession(t *testing.T) {
defer conf.SetAuthMode(config.AuthModePublic)
DeleteSession(router)
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1"
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" // #nosec G101 test token
r := PerformRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(bobToken))
assert.Equal(t, http.StatusUnauthorized, r.Code)
@@ -399,7 +399,7 @@ func TestDeleteSession(t *testing.T) {
DeleteSession(router)
bobToken := AuthenticateUser(app, router, "bob", "Bobbob123!")
aliceToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac0"
aliceToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac0" // #nosec G101 test token
r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(aliceToken), bobToken)
assert.Equal(t, http.StatusForbidden, r.Code)
@@ -411,7 +411,7 @@ func TestDeleteSession(t *testing.T) {
DeleteSession(router)
aliceToken := AuthenticateUser(app, router, "alice", "Alice123!")
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1"
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" // #nosec G101 test token
r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(bobToken), aliceToken)
assert.Equal(t, http.StatusForbidden, r.Code)
@@ -423,7 +423,7 @@ func TestDeleteSession(t *testing.T) {
DeleteSession(router)
authToken := AuthenticateUser(app, router, "alice", "Alice123!")
deleteToken := "638bffc9b86a8fda0d908ebee84a43930cb8d1e3507f4aa0"
deleteToken := "638bffc9b86a8fda0d908ebee84a43930cb8d1e3507f4aa0" // #nosec G101 test token
r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(deleteToken), authToken)
assert.Equal(t, http.StatusForbidden, r.Code)

View File

@@ -36,7 +36,7 @@ func ShareToken(router *gin.RouterGroup) {
}
clientConfig := conf.ClientShare()
clientConfig.SiteUrl = clientConfig.SiteUrl + path.Join("s", token)
clientConfig.SiteUrl += path.Join("s", token)
uri := conf.LibraryUri("/albums")
c.HTML(http.StatusOK, "share.gohtml", gin.H{"shared": gin.H{"token": token, "uri": uri}, "config": clientConfig})
@@ -71,7 +71,7 @@ func ShareTokenShared(router *gin.RouterGroup) {
uid := links[0].ShareUID
clientConfig := conf.ClientShare()
clientConfig.SiteUrl = clientConfig.SiteUrl + path.Join("s", token, uid)
clientConfig.SiteUrl += path.Join("s", token, uid)
clientConfig.SitePreview = clientConfig.SiteUrl + "/preview"
if a, err := query.AlbumByUID(uid); err == nil {

View File

@@ -114,7 +114,7 @@ func SharePreview(router *gin.RouterGroup) {
return
}
size, _ := thumb.Sizes[thumb.Tile500]
size := thumb.Sizes[thumb.Tile500]
images := make([]image.Image, 0, len(p))
@@ -147,6 +147,11 @@ func SharePreview(router *gin.RouterGroup) {
// Create album preview from thumbnail images.
preview, err := frame.Collage(frame.Polaroid, images)
if err != nil {
log.Warnf("preview collage: %v", err)
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
return
}
// Downsize from 1600x900 to 1200x675.
preview = imaging.Resize(preview, 1200, 0, imaging.Lanczos)

View File

@@ -336,20 +336,25 @@ func checkUserPasscodePassword(c *gin.Context, user *entity.User, password strin
}
// Check if user login credentials are valid.
if authUser, provider, method, authErr := entity.Auth(f, nil, c); method == authn.Method2FA && errors.Is(authErr, authn.ErrPasscodeRequired) {
authUser, provider, method, authErr := entity.Auth(f, nil, c)
switch {
case method == authn.Method2FA && errors.Is(authErr, authn.ErrPasscodeRequired):
return http.StatusOK, i18n.MsgVerified, nil
} else if authErr != nil {
case authErr != nil:
// Abort if authentication has failed otherwise.
return code, msg, authErr
} else if authUser == nil {
case authUser == nil:
// Abort if account was not found.
return code, msg, authn.ErrAccountNotFound
} else if !authUser.Equal(user) {
case !authUser.Equal(user):
// Abort if user accounts do not match.
return code, msg, authn.ErrUserDoesNotMatch
} else if !provider.SupportsPasscodeAuthentication() || method != authn.MethodDefault {
case !provider.SupportsPasscodeAuthentication() || method != authn.MethodDefault:
// Abort if e.g. an app password was provided.
return code, msg, authn.ErrInvalidCredentials
default:
return http.StatusOK, i18n.MsgVerified, nil
}
}

View File

@@ -117,13 +117,14 @@ func UploadUserFiles(router *gin.RouterGroup) {
fileType := fs.FileType(baseName)
// Reject unsupported files and files with extensions that aren't allowed.
if fileType == fs.TypeUnknown {
switch {
case fileType == fs.TypeUnknown:
log.Errorf("upload: rejected %s because it has an unsupported file extension", clean.Log(baseName))
continue
} else if allowedExt.Excludes(fileType.DefaultExt()) {
case allowedExt.Excludes(fileType.DefaultExt()):
log.Errorf("upload: rejected %s because its extension is not allowed", clean.Log(baseName))
continue
} else if fileSizeLimit > 0 && file.Size > fileSizeLimit {
case fileSizeLimit > 0 && file.Size > fileSizeLimit:
log.Errorf("upload: rejected %s because its size exceeds the file size limit", clean.Log(baseName))
continue
}
@@ -203,13 +204,14 @@ func UploadUserFiles(router *gin.RouterGroup) {
for _, filename := range uploads {
labels, nsfwErr := vision.DetectNSFW([]string{filename}, media.SrcLocal)
if nsfwErr != nil {
switch {
case nsfwErr != nil:
log.Debug(nsfwErr)
continue
} else if len(labels) < 1 {
case len(labels) < 1:
log.Errorf("nsfw: model returned no result")
continue
} else if labels[0].IsSafe() {
case labels[0].IsSafe():
continue
}

View File

@@ -74,18 +74,6 @@ func buildZipWithDirsAndFiles(dirs []string, files map[string][]byte) []byte {
return zbuf.Bytes()
}
func findUploadedFiles(t *testing.T, base string) []string {
t.Helper()
var out []string
_ = filepath.Walk(base, func(path string, info os.FileInfo, err error) error {
if err == nil && !info.IsDir() {
out = append(out, path)
}
return nil
})
return out
}
// findUploadedFilesForToken lists files only under upload subfolders whose name ends with token suffix.
func findUploadedFilesForToken(t *testing.T, base string, tokenSuffix string) []string {
t.Helper()

View File

@@ -94,10 +94,10 @@ func GetVideo(router *gin.RouterGroup) {
// Get video bitrate, codec, and file type.
videoFileType := f.Type()
videoCodec := f.FileCodec
videoBitrate := f.Bitrate()
videoFileName := photoprism.FileName(f.FileRoot, f.FileName)
videoContentType := f.ContentType()
var videoCodec string
// If the file has a hybrid photo/video format, try to find and send the embedded video data.
if f.MediaType == entity.MediaLive {
@@ -201,7 +201,5 @@ func GetVideo(router *gin.RouterGroup) {
} else {
c.File(videoFileName)
}
return
})
}

View File

@@ -55,15 +55,16 @@ func PostVisionCaption(router *gin.RouterGroup) {
// Run inference to generate a caption.
result, model, err := vision.GenerateCaption(request.Images, media.SrcRemote)
if err != nil {
switch {
case err != nil:
log.Errorf("vision: %s (caption)", err)
c.JSON(http.StatusBadRequest, vision.NewApiError(request.GetId(), http.StatusBadRequest))
return
} else if model == nil {
case model == nil:
log.Errorf("vision: no model specified (caption)")
c.JSON(http.StatusInternalServerError, vision.NewApiError(request.GetId(), http.StatusInternalServerError))
return
} else if result == nil {
case result == nil:
log.Errorf("vision: no result (caption)")
c.JSON(http.StatusInternalServerError, vision.NewApiError(request.GetId(), http.StatusInternalServerError))
return

View File

@@ -166,10 +166,6 @@ func TestPostVisionFace(t *testing.T) {
t.Fatal(apiErr)
}
if apiResponse == nil {
t.Fatal("api response expected")
}
// t.Logf("error: %s", apiResponse.Err())
assert.Error(t, apiResponse.Err())

View File

@@ -117,10 +117,6 @@ func TestPostVisionLabels(t *testing.T) {
t.Fatal(apiErr)
}
if apiResponse == nil {
t.Fatal("api response expected")
}
t.Logf("error: %s", apiResponse.Err())
assert.Error(t, apiResponse.Err())

View File

@@ -132,10 +132,6 @@ func TestPostVisionNsfw(t *testing.T) {
t.Fatal(apiErr)
}
if apiResponse == nil {
t.Fatal("api response expected")
}
// t.Logf("error: %s", apiResponse.Err())
assert.Error(t, apiResponse.Err())

View File

@@ -96,6 +96,7 @@ func ZipCreate(router *gin.RouterGroup) {
// Create new zip file.
var newZipFile *os.File
// #nosec G304 zip name derived from request
if newZipFile, err = os.Create(zipFileName); err != nil {
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
return
@@ -124,7 +125,7 @@ func ZipCreate(router *gin.RouterGroup) {
alias = file.DownloadName(dlName, seq)
}
aliases[key] += 1
aliases[key]++
if fs.FileExists(fileName) {
if zipErr := fs.ZipFile(zipWriter, fileName, alias, false); zipErr != nil {