Backend: Add translations for API messages
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
44c0c4a58b
commit
68843a626d
41 changed files with 802 additions and 211 deletions
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/workers"
|
||||
|
@ -26,7 +27,7 @@ func GetAccounts(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAccounts, acl.ActionSearch)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -35,14 +36,14 @@ func GetAccounts(router *gin.RouterGroup) {
|
|||
err := c.MustBindWith(&f, binding.Form)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := query.AccountSearch(f)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -63,7 +64,7 @@ func GetAccount(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAccounts, acl.ActionRead)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -72,7 +73,7 @@ func GetAccount(router *gin.RouterGroup) {
|
|||
if m, err := query.AccountByID(id); err == nil {
|
||||
c.JSON(http.StatusOK, m)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAccountNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAccountNotFound)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -86,7 +87,7 @@ func GetAccountFolders(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAccounts, acl.ActionRead)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -110,7 +111,7 @@ func GetAccountFolders(router *gin.RouterGroup) {
|
|||
m, err := query.AccountByID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAccountNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAccountNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -118,7 +119,7 @@ func GetAccountFolders(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Errorf("account-folders: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrConnectionFailed)
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrConnectionFailed)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -140,7 +141,7 @@ func ShareWithAccount(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAccounts, acl.ActionUpload)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -149,14 +150,14 @@ func ShareWithAccount(router *gin.RouterGroup) {
|
|||
m, err := query.AccountByID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAccountNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAccountNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.AccountShare
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -164,7 +165,7 @@ func ShareWithAccount(router *gin.RouterGroup) {
|
|||
files, err := query.FilesByUID(f.Photos, 1000, 0)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(404, gin.H{"error": err.Error()})
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -187,20 +188,20 @@ func CreateAccount(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAccounts, acl.ActionCreate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Account
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := f.ServiceDiscovery(); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrConnectionFailed)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -210,11 +211,11 @@ func CreateAccount(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
event.Success("account created")
|
||||
event.SuccessMsg(i18n.MsgAccountCreated)
|
||||
|
||||
c.JSON(http.StatusOK, m)
|
||||
})
|
||||
|
@ -229,7 +230,7 @@ func UpdateAccount(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAccounts, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -238,7 +239,7 @@ func UpdateAccount(router *gin.RouterGroup) {
|
|||
m, err := query.AccountByID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAccountNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -247,30 +248,30 @@ func UpdateAccount(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
// 2) Update form with values from request
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormInvalid)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
// 3) Save model with values from form
|
||||
if err := m.SaveForm(f); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
event.Success("account saved")
|
||||
event.SuccessMsg(i18n.MsgAccountSaved)
|
||||
|
||||
m, err = query.AccountByID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAccountNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -287,7 +288,7 @@ func DeleteAccount(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAccounts, acl.ActionDelete)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -305,7 +306,7 @@ func DeleteAccount(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
event.Success("account deleted")
|
||||
event.SuccessMsg(i18n.MsgAccountDeleted)
|
||||
|
||||
c.JSON(http.StatusOK, m)
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/tidwall/gjson"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
@ -41,7 +42,7 @@ func TestGetAccount(t *testing.T) {
|
|||
GetAccount(router)
|
||||
r := PerformRequest(app, "GET", "/api/v1/accounts/999000")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Account not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrAccountNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
}
|
||||
|
@ -62,7 +63,7 @@ func TestGetAccountFolders(t *testing.T) {
|
|||
GetAccountFolders(router)
|
||||
r := PerformRequest(app, "GET", "/api/v1/accounts/999000/folders")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Account not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrAccountNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
}
|
||||
|
@ -73,7 +74,7 @@ func TestShareWithAccount(t *testing.T) {
|
|||
ShareWithAccount(router)
|
||||
r := PerformRequest(app, "POST", "/api/v1/accounts/1000000/share")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Invalid request", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrBadRequest), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("account not found", func(t *testing.T) {
|
||||
|
@ -81,7 +82,7 @@ func TestShareWithAccount(t *testing.T) {
|
|||
ShareWithAccount(router)
|
||||
r := PerformRequest(app, "POST", "/api/v1/accounts/999000/share")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Account not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrAccountNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
}
|
||||
|
@ -92,7 +93,7 @@ func TestCreateAccount(t *testing.T) {
|
|||
CreateAccount(router)
|
||||
r := PerformRequest(app, "POST", "/api/v1/accounts")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Invalid request", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrBadRequest), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("could not connect", func(t *testing.T) {
|
||||
|
@ -102,7 +103,7 @@ func TestCreateAccount(t *testing.T) {
|
|||
"AccKey": "123", "AccUser": "testuser", "AccPass": "testpasswd", "AccError": "", "AccShare": false, "AccSync": false, "RetryLimit": 3, "SharePath": "", "ShareSize": "", "ShareExpires": 0,
|
||||
"SyncPath": "", "SyncInterval": 3, "SyncUpload": false, "SyncDownload": false, "SyncFilenames": false, "SyncRaw": false}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Could not connect", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrConnectionFailed), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
t.Run("successful request", func(t *testing.T) {
|
||||
|
@ -150,7 +151,7 @@ func TestUpdateAccount(t *testing.T) {
|
|||
UpdateAccount(router)
|
||||
r := PerformRequestWithBody(app, "PUT", "/api/v1/accounts/xxx", `{"AccName": "CreateTestUpdated", "AccOwner": "TestUpdated123", "SyncInterval": 9}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Photo not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrAccountNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
|
||||
|
@ -159,7 +160,7 @@ func TestUpdateAccount(t *testing.T) {
|
|||
UpdateAccount(router)
|
||||
r := PerformRequestWithBody(app, "PUT", "/api/v1/accounts/"+id, `{"AccName": 6, "AccOwner": "TestUpdated123", "SyncInterval": 9, "AccUrl": "https:xxx.com"}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Changes could not be saved", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrBadRequest), val.String())
|
||||
assert.Equal(t, http.StatusBadRequest, r.Code)
|
||||
})
|
||||
}
|
||||
|
@ -183,7 +184,7 @@ func TestDeleteAccount(t *testing.T) {
|
|||
GetAccount(router)
|
||||
r2 := PerformRequest(app, "GET", "/api/v1/accounts/"+id)
|
||||
val2 := gjson.Get(r2.Body.String(), "error")
|
||||
assert.Equal(t, "Account not found", val2.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrAccountNotFound), val2.String())
|
||||
assert.Equal(t, http.StatusNotFound, r2.Code)
|
||||
})
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
@ -34,7 +35,7 @@ func GetAlbums(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionSearch)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -43,7 +44,7 @@ func GetAlbums(router *gin.RouterGroup) {
|
|||
err := c.MustBindWith(&f, binding.Form)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -73,7 +74,7 @@ func GetAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionRead)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -81,7 +82,7 @@ func GetAlbum(router *gin.RouterGroup) {
|
|||
m, err := query.AlbumByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -95,14 +96,14 @@ func CreateAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionCreate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Album
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -112,12 +113,11 @@ func CreateAlbum(router *gin.RouterGroup) {
|
|||
log.Debugf("album: creating %+v %+v", f, m)
|
||||
|
||||
if res := entity.Db().Create(m); res.Error != nil {
|
||||
log.Error(res.Error.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("%s already exists", txt.Quote(m.AlbumTitle))})
|
||||
AbortAlreadyExists(c, txt.Quote(m.AlbumTitle))
|
||||
return
|
||||
}
|
||||
|
||||
event.Success("album created")
|
||||
event.SuccessMsg(i18n.MsgAlbumCreated)
|
||||
|
||||
UpdateClientConfig()
|
||||
|
||||
|
@ -133,7 +133,7 @@ func UpdateAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ func UpdateAlbum(router *gin.RouterGroup) {
|
|||
m, err := query.AlbumByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -149,25 +149,25 @@ func UpdateAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormInvalid)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.SaveForm(f); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
UpdateClientConfig()
|
||||
|
||||
event.Success("album saved")
|
||||
event.SuccessMsg(i18n.MsgAlbumSaved)
|
||||
|
||||
PublishAlbumEvent(EntityUpdated, uid, c)
|
||||
|
||||
|
@ -181,7 +181,7 @@ func DeleteAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionDelete)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,7 @@ func DeleteAlbum(router *gin.RouterGroup) {
|
|||
m, err := query.AlbumByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -200,7 +200,8 @@ func DeleteAlbum(router *gin.RouterGroup) {
|
|||
conf.Db().Delete(&m)
|
||||
|
||||
UpdateClientConfig()
|
||||
event.Success(fmt.Sprintf("album %s deleted", txt.Quote(m.AlbumTitle)))
|
||||
|
||||
event.SuccessMsg(i18n.MsgAlbumDeleted, txt.Quote(m.AlbumTitle))
|
||||
|
||||
c.JSON(http.StatusOK, m)
|
||||
})
|
||||
|
@ -215,7 +216,7 @@ func LikeAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionLike)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,7 @@ func LikeAlbum(router *gin.RouterGroup) {
|
|||
album, err := query.AlbumByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -234,7 +235,7 @@ func LikeAlbum(router *gin.RouterGroup) {
|
|||
UpdateClientConfig()
|
||||
PublishAlbumEvent(EntityUpdated, id, c)
|
||||
|
||||
c.JSON(http.StatusOK, http.Response{})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgChangesSaved))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -247,7 +248,7 @@ func DislikeAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionLike)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -256,7 +257,7 @@ func DislikeAlbum(router *gin.RouterGroup) {
|
|||
album, err := query.AlbumByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -266,7 +267,7 @@ func DislikeAlbum(router *gin.RouterGroup) {
|
|||
UpdateClientConfig()
|
||||
PublishAlbumEvent(EntityUpdated, id, c)
|
||||
|
||||
c.JSON(http.StatusOK, http.Response{})
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgChangesSaved))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -276,21 +277,21 @@ func CloneAlbums(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
a, err := query.AlbumByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -315,12 +316,12 @@ func CloneAlbums(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if len(added) > 0 {
|
||||
event.Success(fmt.Sprintf("selection added to %s", txt.Quote(a.Title())))
|
||||
event.SuccessMsg(i18n.MsgSelectionAddedTo, txt.Quote(a.Title()))
|
||||
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "album contents cloned", "album": a, "added": added})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.Msg(i18n.MsgAlbumCloned), "album": a, "added": added})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -330,14 +331,14 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -345,7 +346,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
a, err := query.AlbumByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -353,7 +354,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Errorf("album: %s", err)
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -361,15 +362,15 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if len(added) > 0 {
|
||||
if len(added) == 1 {
|
||||
event.Success(fmt.Sprintf("one entry added to %s", txt.Quote(a.Title())))
|
||||
event.SuccessMsg(i18n.MsgEntryAddedTo, txt.Quote(a.Title()))
|
||||
} else {
|
||||
event.Success(fmt.Sprintf("%d entries added to %s", len(added), txt.Quote(a.Title())))
|
||||
event.SuccessMsg(i18n.MsgEntriesAddedTo, len(added), txt.Quote(a.Title()))
|
||||
}
|
||||
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "photos added to album", "album": a, "photos": photos.UIDs(), "added": added})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.Msg(i18n.MsgChangesSaved), "album": a, "photos": photos.UIDs(), "added": added})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -379,27 +380,26 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
if len(f.Photos) == 0 {
|
||||
log.Error("no items selected")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst("no items selected")})
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
a, err := query.AlbumByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -407,15 +407,15 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if len(removed) > 0 {
|
||||
if len(removed) == 1 {
|
||||
event.Success(fmt.Sprintf("one entry removed from %s", txt.Quote(a.Title())))
|
||||
event.SuccessMsg(i18n.MsgEntryRemovedFrom, txt.Quote(a.Title()))
|
||||
} else {
|
||||
event.Success(fmt.Sprintf("%d entries removed from %s", len(removed), txt.Quote(txt.Quote(a.Title()))))
|
||||
event.SuccessMsg(i18n.MsgEntriesRemovedFrom, len(removed), txt.Quote(txt.Quote(a.Title())))
|
||||
}
|
||||
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "entries removed from album", "album": a, "photos": f.Photos, "removed": removed})
|
||||
c.JSON(http.StatusOK, gin.H{"message": i18n.Msg(i18n.MsgChangesSaved), "album": a, "photos": f.Photos, "removed": removed})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -423,7 +423,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
|||
func DownloadAlbum(router *gin.RouterGroup) {
|
||||
router.GET("/albums/:uid/dl", func(c *gin.Context) {
|
||||
if InvalidDownloadToken(c) {
|
||||
c.Data(http.StatusForbidden, "image/svg+xml", brokenIconSvg)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -432,14 +432,14 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
|||
a, err := query.AlbumByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
p, err := query.AlbumPhotos(a, 10000)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -450,7 +450,7 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if err := os.MkdirAll(zipPath, 0700); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst("failed to create zip folder")})
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrCreateFolder)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -458,7 +458,7 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrCreateFile)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -475,7 +475,7 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
|||
if fs.FileExists(fileName) {
|
||||
if err := addFileToZip(zipWriter, fileName, fileAlias); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst("failed to create zip file")})
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrCreateFile)
|
||||
return
|
||||
}
|
||||
log.Infof("album: added %s as %s", txt.Quote(f.FileName), txt.Quote(fileAlias))
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -182,7 +183,7 @@ func TestAddPhotosToAlbum(t *testing.T) {
|
|||
AddPhotosToAlbum(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/albums/"+uid+"/photos", `{"photos": ["pt9jtdre2lvl0y12", "pt9jtdre2lvl0y11"]}`)
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Equal(t, "photos added to album", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgChangesSaved), val.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
t.Run("add one photo to album", func(t *testing.T) {
|
||||
|
@ -190,7 +191,7 @@ func TestAddPhotosToAlbum(t *testing.T) {
|
|||
AddPhotosToAlbum(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/albums/"+uid+"/photos", `{"photos": ["pt9jtdre2lvl0y12"]}`)
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Equal(t, "photos added to album", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgChangesSaved), val.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
|
@ -222,7 +223,7 @@ func TestRemovePhotosFromAlbum(t *testing.T) {
|
|||
RemovePhotosFromAlbum(router)
|
||||
r := PerformRequestWithBody(app, "DELETE", "/api/v1/albums/"+uid+"/photos", `{"photos": ["pt9jtdre2lvl0y12", "pt9jtdre2lvl0y11"]}`)
|
||||
val := gjson.Get(r.Body.String(), "message")
|
||||
assert.Equal(t, "entries removed from album", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.MsgChangesSaved), val.String())
|
||||
assert.Equal(t, http.StatusOK, r.Code)
|
||||
})
|
||||
t.Run("no items selected", func(t *testing.T) {
|
||||
|
|
|
@ -32,7 +32,11 @@ https://docs.photoprism.org/developer-guide/
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
)
|
||||
|
||||
|
@ -49,3 +53,33 @@ func UpdateClientConfig() {
|
|||
|
||||
event.Publish("config.updated", event.Data{"config": conf.UserConfig()})
|
||||
}
|
||||
|
||||
func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
|
||||
resp := i18n.NewResponse(code, id, params...)
|
||||
log.Errorf("api: %s", resp.LowerString())
|
||||
c.AbortWithStatusJSON(code, resp)
|
||||
}
|
||||
|
||||
func AbortUnauthorized(c *gin.Context) {
|
||||
Abort(c, http.StatusUnauthorized, i18n.ErrUnauthorized)
|
||||
}
|
||||
|
||||
func AbortEntityNotFound(c *gin.Context) {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrEntityNotFound)
|
||||
}
|
||||
|
||||
func AbortSaveFailed(c *gin.Context) {
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrSaveFailed)
|
||||
}
|
||||
|
||||
func AbortUnexpected(c *gin.Context) {
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrUnexpected)
|
||||
}
|
||||
|
||||
func AbortBadRequest(c *gin.Context) {
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrBadRequest)
|
||||
}
|
||||
|
||||
func AbortAlreadyExists(c *gin.Context, s string) {
|
||||
Abort(c, http.StatusConflict, i18n.ErrAlreadyExists, s)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionDelete)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
|
|||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
|
|||
err := entity.Db().Where("photo_uid IN (?)", f.Photos).Delete(&entity.Photo{}).Error
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionDelete)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
|
|||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
|
|||
UpdateColumn("deleted_at", gorm.Expr("NULL")).Error
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -123,14 +123,14 @@ func BatchAlbumsDelete(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionDelete)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionPrivate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
|
|||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
|
|||
gorm.Expr("CASE WHEN photo_private > 0 THEN 0 ELSE 1 END")).Error
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -210,14 +210,14 @@ func BatchLabelsDelete(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceLabels, acl.ActionDelete)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Selection
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ func GetConfig(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceConfig, acl.ActionRead)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ func GetErrors(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceLogs, acl.ActionSearch)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
|
@ -23,7 +21,7 @@ func PublishPhotoEvent(e EntityEvent, uid string, c *gin.Context) {
|
|||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrUnexpectedError)
|
||||
AbortUnexpected(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -36,7 +34,7 @@ func PublishAlbumEvent(e EntityEvent, uid string, c *gin.Context) {
|
|||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrUnexpectedError)
|
||||
AbortUnexpected(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -49,7 +47,7 @@ func PublishLabelEvent(e EntityEvent, uid string, c *gin.Context) {
|
|||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrUnexpectedError)
|
||||
AbortUnexpected(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -17,14 +17,14 @@ func GetFile(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceFiles, acl.ActionRead)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
p, err := query.FileByHash(c.Param("hash"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
type FoldersResponse struct {
|
||||
|
@ -32,7 +31,7 @@ func GetFolders(router *gin.RouterGroup, urlPath, rootName, rootPath string) {
|
|||
s := Auth(SessionID(c), acl.ResourceFolders, acl.ActionSearch)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -42,7 +41,7 @@ func GetFolders(router *gin.RouterGroup, urlPath, rootName, rootPath string) {
|
|||
err := c.MustBindWith(&f, binding.Form)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ func GetGeo(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionSearch)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ func GetGeo(router *gin.RouterGroup) {
|
|||
err := c.MustBindWith(&f, binding.Form)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ func StartImport(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionImport)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ func StartImport(router *gin.RouterGroup) {
|
|||
var f form.ImportOptions
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ func CancelImport(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionImport)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ func StartIndexing(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ func StartIndexing(router *gin.RouterGroup) {
|
|||
var f form.IndexOptions
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ func CancelIndexing(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ func GetLabels(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceLabels, acl.ActionSearch)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ func GetLabels(router *gin.RouterGroup) {
|
|||
err := c.MustBindWith(&f, binding.Form)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -62,14 +62,14 @@ func UpdateLabel(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceLabels, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Label
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ func LikeLabel(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceLabels, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ func DislikeLabel(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceLabels, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,14 @@ func UpdateLink(c *gin.Context) {
|
|||
s := Auth(SessionID(c), acl.ResourceLinks, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Link
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ func DeleteLink(c *gin.Context) {
|
|||
s := Auth(SessionID(c), acl.ResourceLinks, acl.ActionDelete)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -82,14 +82,14 @@ func CreateLink(c *gin.Context) {
|
|||
s := Auth(SessionID(c), acl.ResourceLinks, acl.ActionCreate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Link
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,7 @@ func GetAlbumLinks(router *gin.RouterGroup) {
|
|||
func CreatePhotoLink(router *gin.RouterGroup) {
|
||||
router.POST("/photos/:uid/links", func(c *gin.Context) {
|
||||
if _, err := query.PhotoByUID(c.Param("uid")); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ func GetMomentsTime(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceAlbums, acl.ActionExport)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
@ -41,14 +42,14 @@ func GetPhoto(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionRead)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
p, err := query.PhotoPreloadByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -62,7 +63,7 @@ func UpdatePhoto(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -71,7 +72,7 @@ func UpdatePhoto(router *gin.RouterGroup) {
|
|||
m, err := query.PhotoByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -80,33 +81,30 @@ func UpdatePhoto(router *gin.RouterGroup) {
|
|||
f, err := form.NewPhoto(m)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrSaveFailed)
|
||||
return
|
||||
}
|
||||
|
||||
// 2) Update form with values from request
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormInvalid)
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 3) Save model with values from form
|
||||
if err := entity.SavePhotoForm(m, f, conf.GeoCodingApi()); err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrSaveFailed)
|
||||
return
|
||||
}
|
||||
|
||||
PublishPhotoEvent(EntityUpdated, uid, c)
|
||||
|
||||
event.Success("photo saved")
|
||||
event.SuccessMsg(i18n.MsgChangesSaved)
|
||||
|
||||
p, err := query.PhotoPreloadByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -130,7 +128,7 @@ func GetPhotoDownload(router *gin.RouterGroup) {
|
|||
f, err := query.FileByPhotoUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -163,7 +161,7 @@ func GetPhotoYaml(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionExport)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -198,7 +196,7 @@ func ApprovePhoto(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -206,13 +204,13 @@ func ApprovePhoto(router *gin.RouterGroup) {
|
|||
m, err := query.PhotoByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.Approve(); err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -233,7 +231,7 @@ func LikePhoto(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionLike)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -241,13 +239,13 @@ func LikePhoto(router *gin.RouterGroup) {
|
|||
m, err := query.PhotoByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.SetFavorite(true); err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -268,7 +266,7 @@ func DislikePhoto(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionLike)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -276,13 +274,13 @@ func DislikePhoto(router *gin.RouterGroup) {
|
|||
m, err := query.PhotoByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.SetFavorite(false); err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -304,7 +302,7 @@ func PhotoFilePrimary(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -313,18 +311,18 @@ func PhotoFilePrimary(router *gin.RouterGroup) {
|
|||
err := query.SetPhotoPrimary(uid, fileUID)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
PublishPhotoEvent(EntityUpdated, uid, c)
|
||||
|
||||
event.Success("photo saved")
|
||||
event.SuccessMsg(i18n.MsgChangesSaved)
|
||||
|
||||
p, err := query.PhotoPreloadByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -342,7 +340,7 @@ func PhotoFileUngroup(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -353,13 +351,13 @@ func PhotoFileUngroup(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Errorf("photo: %s (ungroup)", err)
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrFileNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
if file.FilePrimary {
|
||||
log.Errorf("photo: can't ungroup primary files")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormInvalid)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -368,7 +366,7 @@ func PhotoFileUngroup(router *gin.RouterGroup) {
|
|||
|
||||
if err := entity.UnscopedDb().Create(&newPhoto).Error; err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -378,7 +376,7 @@ func PhotoFileUngroup(router *gin.RouterGroup) {
|
|||
|
||||
if err := file.Save(); err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -388,25 +386,25 @@ func PhotoFileUngroup(router *gin.RouterGroup) {
|
|||
|
||||
if err != nil {
|
||||
log.Errorf("photo: %s (ungroup)", err)
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrFileNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.Index().MediaFile(f, photoprism.IndexOptions{Rescan: true}, existingPhoto.OriginalName).Error; err != nil {
|
||||
log.Errorf("photo: %s", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
PublishPhotoEvent(EntityCreated, file.PhotoUID, c)
|
||||
PublishPhotoEvent(EntityUpdated, photoUID, c)
|
||||
|
||||
event.Success("file ungrouped")
|
||||
event.SuccessMsg(i18n.MsgFileUngrouped)
|
||||
|
||||
p, err := query.PhotoPreloadByUID(photoUID)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -23,21 +23,21 @@ func AddPhotoLabel(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
m, err := query.PhotoByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.Label
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ func AddPhotoLabel(router *gin.RouterGroup) {
|
|||
p, err := query.PhotoPreloadByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -98,14 +98,14 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
m, err := query.PhotoByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
|
|||
p, err := query.PhotoPreloadByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
|||
m, err := query.PhotoByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if err := c.BindJSON(&label); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
|||
p, err := query.PhotoPreloadByUID(c.Param("uid"))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
@ -29,7 +30,7 @@ func TestAddPhotoLabel(t *testing.T) {
|
|||
AddPhotoLabel(router)
|
||||
r := PerformRequestWithBody(app, "POST", "/api/v1/photos/xxx/label", `{"Name": "Flower", "Uncertainty": 10, "Priority": 2}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Photo not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrEntityNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
t.Run("invalid request", func(t *testing.T) {
|
||||
|
@ -65,7 +66,7 @@ func TestRemovePhotoLabel(t *testing.T) {
|
|||
RemovePhotoLabel(router)
|
||||
r := PerformRequest(app, "DELETE", "/api/v1/photos/xxx/label/10000001")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Photo not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrEntityNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
t.Run("label not existing", func(t *testing.T) {
|
||||
|
@ -105,7 +106,7 @@ func TestUpdatePhotoLabel(t *testing.T) {
|
|||
r := PerformRequestWithBody(app, "PUT", "/api/v1/photos/xxx/label/1000006", `{"Label": {"Name": "NewLabelName"}}`)
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Photo not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrEntityNotFound), val.String())
|
||||
})
|
||||
t.Run("label not existing", func(t *testing.T) {
|
||||
app, router, _ := NewApiTest()
|
||||
|
|
|
@ -4,13 +4,11 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
)
|
||||
|
||||
// GET /api/v1/photos
|
||||
|
@ -32,7 +30,7 @@ func GetPhotos(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionSearch)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -41,14 +39,14 @@ func GetPhotos(router *gin.RouterGroup) {
|
|||
err := c.MustBindWith(&f, binding.Form)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Guests may only see public content in shared albums.
|
||||
if s.Guest() {
|
||||
if f.Album == "" || !s.HasShare(f.Album) {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -62,7 +60,8 @@ func GetPhotos(router *gin.RouterGroup) {
|
|||
result, count, err := query.PhotoSearch(f)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
log.Error(err)
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
@ -50,7 +51,7 @@ func TestUpdatePhoto(t *testing.T) {
|
|||
UpdatePhoto(router)
|
||||
r := PerformRequestWithBody(app, "PUT", "/api/v1/photos/xxx", `{"Name": "Updated01", "Country": "de"}`)
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Photo not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrEntityNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
}
|
||||
|
@ -131,7 +132,7 @@ func TestSetPhotoPrimary(t *testing.T) {
|
|||
PhotoFilePrimary(router)
|
||||
r := PerformRequest(app, "POST", "/api/v1/photos/xxx/files/ft1es39w45bnlqdw/primary")
|
||||
val := gjson.Get(r.Body.String(), "error")
|
||||
assert.Equal(t, "Photo not found", val.String())
|
||||
assert.Equal(t, i18n.Msg(i18n.ErrEntityNotFound), val.String())
|
||||
assert.Equal(t, http.StatusNotFound, r.Code)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/session"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// POST /api/v1/session
|
||||
|
@ -18,7 +17,7 @@ func CreateSession(router *gin.RouterGroup) {
|
|||
var f form.Login
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// GET /api/v1/settings
|
||||
|
@ -15,7 +15,7 @@ func GetSettings(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceSettings, acl.ActionRead)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -33,21 +33,21 @@ func SaveSettings(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourceSettings, acl.ActionUpdate)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
conf := service.Config()
|
||||
|
||||
if conf.SettingsHidden() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
settings := conf.Settings()
|
||||
|
||||
if err := c.BindJSON(settings); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ func SaveSettings(router *gin.RouterGroup) {
|
|||
|
||||
UpdateClientConfig()
|
||||
|
||||
log.Infof("settings saved")
|
||||
log.Infof(i18n.Msg(i18n.MsgSettingsSaved))
|
||||
|
||||
c.JSON(http.StatusOK, settings)
|
||||
})
|
||||
|
|
|
@ -28,7 +28,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionUpload)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
f, err := c.MultipartForm()
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
p := path.Join(conf.ImportPath(), "upload", subPath)
|
||||
|
||||
if err := os.MkdirAll(p, os.ModePerm); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
log.Debugf("upload: saving file %s", txt.Quote(file.Filename))
|
||||
|
||||
if err := c.SaveUploadedFile(file, filename); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ func ChangePassword(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePeople, acl.ActionUpdateSelf)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ func CreateZip(router *gin.RouterGroup) {
|
|||
s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionDownload)
|
||||
|
||||
if s.Invalid() {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
|
||||
AbortUnauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ func CreateZip(router *gin.RouterGroup) {
|
|||
start := time.Now()
|
||||
|
||||
if err := c.BindJSON(&f); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
@ -106,8 +107,8 @@ func NewSettings() *Settings {
|
|||
}
|
||||
|
||||
// Propagate updates settings in other packages as needed.
|
||||
func (s *Settings) Propagate() {
|
||||
|
||||
func (s Settings) Propagate() {
|
||||
i18n.SetLang(s.Language)
|
||||
}
|
||||
|
||||
// Load uses a yaml config file to initiate the configuration entity.
|
||||
|
|
|
@ -150,6 +150,9 @@ func NewTestConfig() *Config {
|
|||
// NewTestErrorConfig inits invalid config used for testing
|
||||
func NewTestErrorConfig() *Config {
|
||||
c := &Config{params: NewTestParamsError()}
|
||||
|
||||
c.initSettings()
|
||||
|
||||
err := c.Init(context.Background())
|
||||
if err != nil {
|
||||
log.Fatalf("config: %s", err.Error())
|
||||
|
|
|
@ -2,6 +2,7 @@ package event
|
|||
|
||||
import (
|
||||
"github.com/leandro-lugaresi/hub"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
)
|
||||
|
||||
type Hub = hub.Hub
|
||||
|
@ -39,6 +40,22 @@ func Warning(msg string) {
|
|||
Publish("notify.warning", Data{"msg": msg})
|
||||
}
|
||||
|
||||
func ErrorMsg(id i18n.Message, params ...interface{}) {
|
||||
Error(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
func SuccessMsg(id i18n.Message, params ...interface{}) {
|
||||
Success(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
func InfoMsg(id i18n.Message, params ...interface{}) {
|
||||
Info(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
func WarningMsg(id i18n.Message, params ...interface{}) {
|
||||
Warning(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
func Publish(event string, data Data) {
|
||||
SharedHub().Publish(Message{
|
||||
Name: event,
|
||||
|
|
97
internal/i18n/i18n.go
Normal file
97
internal/i18n/i18n.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
|
||||
Package i18n contains PhotoPrism status and error message strings.
|
||||
|
||||
Copyright (c) 2018 - 2020 Michael Mayer <hello@photoprism.org>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
PhotoPrism™ is a registered trademark of Michael Mayer. You may use it as required
|
||||
to describe our software, run your own server, for educational purposes, but not for
|
||||
offering commercial goods, products, or services without prior written permission.
|
||||
In other words, please ask.
|
||||
|
||||
Feel free to send an e-mail to hello@photoprism.org if you have questions,
|
||||
want to support our work, or just want to say hello.
|
||||
|
||||
Additional information can be found in our Developer Guide:
|
||||
https://docs.photoprism.org/developer-guide/
|
||||
|
||||
*/
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Message int
|
||||
type MessageMap map[Message]string
|
||||
|
||||
func Msg(id Message, params ...interface{}) string {
|
||||
return LangMsg(id, Lang, params...)
|
||||
}
|
||||
|
||||
func LangMsg(id Message, lang Language, params ...interface{}) string {
|
||||
msgs, ok := Languages[lang]
|
||||
|
||||
if !ok && lang != Default {
|
||||
msgs, ok = Languages[Default]
|
||||
}
|
||||
|
||||
msg, ok := msgs[id]
|
||||
|
||||
if !ok {
|
||||
msg, ok = Languages[Default][id]
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return fmt.Sprintf("i18n: unknown message id %d", id)
|
||||
}
|
||||
|
||||
if strings.Contains(msg, "%") {
|
||||
msg = fmt.Sprintf(msg, params...)
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func DefaultMsg(id Message, params ...interface{}) string {
|
||||
return LangMsg(id, Default, params...)
|
||||
}
|
||||
|
||||
func Error(id Message, params ...interface{}) error {
|
||||
return errors.New(Msg(id, params...))
|
||||
}
|
||||
|
||||
func LangError(id Message, lang Language, params ...interface{}) error {
|
||||
return errors.New(LangMsg(id, lang, params...))
|
||||
}
|
||||
|
||||
func DefaultError(id Message, params ...interface{}) error {
|
||||
return LangError(id, Default, params...)
|
||||
}
|
||||
|
||||
/*
|
||||
func JsonBadRequest(id Message) gin.H {
|
||||
return JsonError(http.StatusBadRequest, msg)
|
||||
}
|
||||
|
||||
func JsonForbidden(id Message) gin.H {
|
||||
return JsonError(http.StatusForbidden, msg)
|
||||
}
|
||||
|
||||
|
||||
*/
|
121
internal/i18n/i18n_test.go
Normal file
121
internal/i18n/i18n_test.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMsg(t *testing.T) {
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
msg := Msg(ErrAlreadyExists, "A cat")
|
||||
assert.Equal(t, "A cat already exists", msg)
|
||||
})
|
||||
|
||||
t.Run("unexpected error", func(t *testing.T) {
|
||||
msg := Msg(ErrUnexpected, "A cat")
|
||||
assert.Equal(t, "Unexpected error, please try again", msg)
|
||||
})
|
||||
|
||||
t.Run("already exists german", func(t *testing.T) {
|
||||
SetLang("de")
|
||||
msgGerman := Msg(ErrAlreadyExists, "Eine Katze")
|
||||
assert.Equal(t, "Eine Katze existiert bereits", msgGerman)
|
||||
SetLang("")
|
||||
msgDefault := Msg(ErrAlreadyExists, "A cat")
|
||||
assert.Equal(t, "A cat already exists", msgDefault)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLangMsg(t *testing.T) {
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
msgDefault := LangMsg(ErrAlreadyExists, Default, "A cat")
|
||||
assert.Equal(t, "A cat already exists", msgDefault)
|
||||
msgEnglish := LangMsg(ErrAlreadyExists, English, "A cat")
|
||||
assert.Equal(t, msgEnglish, msgDefault)
|
||||
})
|
||||
|
||||
t.Run("unexpected error", func(t *testing.T) {
|
||||
msgDefault := LangMsg(ErrUnexpected, Default, "A cat")
|
||||
assert.Equal(t, "Unexpected error, please try again", msgDefault)
|
||||
msgEnglish := LangMsg(ErrUnexpected, English, "A cat")
|
||||
assert.Equal(t, msgEnglish, msgDefault)
|
||||
})
|
||||
|
||||
t.Run("already exists german", func(t *testing.T) {
|
||||
msg := LangMsg(ErrAlreadyExists, German, "Eine Katze")
|
||||
assert.Equal(t, "Eine Katze existiert bereits", msg)
|
||||
})
|
||||
|
||||
t.Run("unexpected error german", func(t *testing.T) {
|
||||
msg := LangMsg(ErrUnexpected, German, "Eine Katze")
|
||||
assert.Equal(t, "Unerwarteter Fehler, bitte erneut versuchen", msg)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultMsg(t *testing.T) {
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
msg := DefaultMsg(ErrAlreadyExists, "A cat")
|
||||
assert.Equal(t, "A cat already exists", msg)
|
||||
})
|
||||
|
||||
t.Run("unexpected error", func(t *testing.T) {
|
||||
msg := DefaultMsg(ErrUnexpected, "A cat")
|
||||
assert.Equal(t, "Unexpected error, please try again", msg)
|
||||
})
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
err := Error(ErrAlreadyExists, "A cat")
|
||||
assert.EqualError(t, err, "A cat already exists")
|
||||
})
|
||||
|
||||
t.Run("unexpected error", func(t *testing.T) {
|
||||
err := Error(ErrUnexpected, "A cat")
|
||||
assert.EqualError(t, err, "Unexpected error, please try again")
|
||||
})
|
||||
|
||||
t.Run("already exists german", func(t *testing.T) {
|
||||
SetLang("de")
|
||||
errGerman := Error(ErrAlreadyExists, "Eine Katze")
|
||||
assert.EqualError(t, errGerman, "Eine Katze existiert bereits")
|
||||
SetLang("")
|
||||
errDefault := Error(ErrAlreadyExists, "A cat")
|
||||
assert.EqualError(t, errDefault, "A cat already exists")
|
||||
})
|
||||
}
|
||||
|
||||
func TestLangError(t *testing.T) {
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
err := LangError(ErrAlreadyExists, English, "A cat")
|
||||
assert.EqualError(t, err, "A cat already exists")
|
||||
})
|
||||
|
||||
t.Run("unexpected error", func(t *testing.T) {
|
||||
err := LangError(ErrUnexpected, English, "A cat")
|
||||
assert.EqualError(t, err, "Unexpected error, please try again")
|
||||
})
|
||||
|
||||
t.Run("already exists german", func(t *testing.T) {
|
||||
err := LangError(ErrAlreadyExists, German, "Eine Katze")
|
||||
assert.EqualError(t, err, "Eine Katze existiert bereits")
|
||||
})
|
||||
|
||||
t.Run("unexpected error german", func(t *testing.T) {
|
||||
err := LangError(ErrUnexpected, German, "Eine Katze")
|
||||
assert.EqualError(t, err, "Unerwarteter Fehler, bitte erneut versuchen")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultError(t *testing.T) {
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
err := DefaultError(ErrAlreadyExists, "A cat")
|
||||
assert.EqualError(t, err, "A cat already exists")
|
||||
})
|
||||
|
||||
t.Run("unexpected error", func(t *testing.T) {
|
||||
err := DefaultError(ErrUnexpected, "A cat")
|
||||
assert.EqualError(t, err, "Unexpected error, please try again")
|
||||
})
|
||||
}
|
33
internal/i18n/lang-de.go
Normal file
33
internal/i18n/lang-de.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package i18n
|
||||
|
||||
var MsgGerman = MessageMap{
|
||||
ErrUnexpected: "Unerwarteter Fehler, bitte erneut versuchen",
|
||||
ErrSaveFailed: "Daten gekonnten nicht gespeichert werden",
|
||||
ErrBadRequest: "Ungültige Eingabe, bitte erneut versuchen",
|
||||
ErrAlreadyExists: "%s existiert bereits",
|
||||
ErrEntityNotFound: "Eintrag nicht gefunden",
|
||||
ErrAccountNotFound: "Unbekannter Account",
|
||||
ErrAlbumNotFound: "Album nicht gefunden - gelöscht?",
|
||||
ErrReadOnly: "Funktion im 'read-only' Modus nicht verfügbar",
|
||||
ErrUnauthorized: "Anmeldung erforderlich",
|
||||
ErrUploadNSFW: "Inhalt könnte anstößig sein und wurde abgelehnt",
|
||||
ErrNoItemsSelected: "Auswahl ist leer, bitte erneut versuchen",
|
||||
ErrCreateFile: "Datei konnte nicht angelegt werden",
|
||||
ErrCreateFolder: "Verzeichnis konnte nicht angelegt werden",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
|
||||
MsgChangesSaved: "Änderungen erfolgreich gespeichert",
|
||||
MsgAlbumCreated: "Album erstellt",
|
||||
MsgAlbumSaved: "Album gespeichert",
|
||||
MsgAlbumDeleted: "Album %s gelöscht",
|
||||
MsgAlbumCloned: "Album-Einträge kopiert",
|
||||
MsgFileUngrouped: "Datei-Gruppierung aufgehoben",
|
||||
MsgSelectionAddedTo: "Auswahl zu %s hinzugefügt",
|
||||
MsgEntryAddedTo: "Ein Eintrag zu %s hinzugefügt",
|
||||
MsgEntriesAddedTo: "%d Einträge zu %s hinzugefügt",
|
||||
MsgEntryRemovedFrom: "Ein Eintrag aus %s entfernt",
|
||||
MsgEntriesRemovedFrom: "%d Einträge aus %s entfernt",
|
||||
MsgAccountCreated: "Server-Konfiguration angelegt",
|
||||
MsgAccountSaved: "Server-Konfiguration gespeichert",
|
||||
MsgAccountDeleted: "Server-Konfiguration gelöscht",
|
||||
}
|
67
internal/i18n/lang-en.go
Normal file
67
internal/i18n/lang-en.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package i18n
|
||||
|
||||
const (
|
||||
ErrUnexpected Message = iota + 1
|
||||
ErrBadRequest
|
||||
ErrSaveFailed
|
||||
ErrAlreadyExists
|
||||
ErrEntityNotFound
|
||||
ErrAccountNotFound
|
||||
ErrAlbumNotFound
|
||||
ErrReadOnly
|
||||
ErrUnauthorized
|
||||
ErrUploadNSFW
|
||||
ErrNoItemsSelected
|
||||
ErrCreateFile
|
||||
ErrCreateFolder
|
||||
ErrConnectionFailed
|
||||
|
||||
MsgChangesSaved
|
||||
MsgAlbumCreated
|
||||
MsgAlbumSaved
|
||||
MsgAlbumDeleted
|
||||
MsgAlbumCloned
|
||||
MsgFileUngrouped
|
||||
MsgSelectionAddedTo
|
||||
MsgEntryAddedTo
|
||||
MsgEntriesAddedTo
|
||||
MsgEntryRemovedFrom
|
||||
MsgEntriesRemovedFrom
|
||||
MsgAccountCreated
|
||||
MsgAccountSaved
|
||||
MsgAccountDeleted
|
||||
MsgSettingsSaved
|
||||
)
|
||||
|
||||
var MsgEnglish = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request, please try again",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "Please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
ErrConnectionFailed: "Could not connect, please try again",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
MsgSettingsSaved: "Settings saved",
|
||||
}
|
32
internal/i18n/lang-fr.go
Normal file
32
internal/i18n/lang-fr.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package i18n
|
||||
|
||||
var MsgFrench = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request, please try again",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
}
|
32
internal/i18n/lang-nl.go
Normal file
32
internal/i18n/lang-nl.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package i18n
|
||||
|
||||
var MsgDutch = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request, please try again",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
}
|
32
internal/i18n/lang-ru.go
Normal file
32
internal/i18n/lang-ru.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package i18n
|
||||
|
||||
var MsgRussian = MessageMap{
|
||||
ErrUnexpected: "Unexpected error, please try again",
|
||||
ErrBadRequest: "Invalid request, please try again",
|
||||
ErrSaveFailed: "Changes could not be saved",
|
||||
ErrAlreadyExists: "%s already exists",
|
||||
ErrEntityNotFound: "Unknown entity",
|
||||
ErrAccountNotFound: "Unknown account",
|
||||
ErrAlbumNotFound: "Album not found",
|
||||
ErrReadOnly: "not available in read-only mode",
|
||||
ErrUnauthorized: "please log in and try again",
|
||||
ErrUploadNSFW: "Upload might be offensive",
|
||||
ErrNoItemsSelected: "No items selected",
|
||||
ErrCreateFile: "Failed creating file, please check permissions",
|
||||
ErrCreateFolder: "Failed creating folder, please check permissions",
|
||||
|
||||
MsgChangesSaved: "Changes successfully saved",
|
||||
MsgAlbumCreated: "Album created",
|
||||
MsgAlbumSaved: "Album saved",
|
||||
MsgAlbumDeleted: "Album %s deleted",
|
||||
MsgAlbumCloned: "Album contents cloned",
|
||||
MsgFileUngrouped: "File successfully ungrouped",
|
||||
MsgSelectionAddedTo: "Selection added to %s",
|
||||
MsgEntryAddedTo: "One entry added to %s",
|
||||
MsgEntriesAddedTo: "%d entries added to %s",
|
||||
MsgEntryRemovedFrom: "One entry removed from %s",
|
||||
MsgEntriesRemovedFrom: "%d entries removed from %s",
|
||||
MsgAccountCreated: "Account created",
|
||||
MsgAccountSaved: "Account saved",
|
||||
MsgAccountDeleted: "Account deleted",
|
||||
}
|
35
internal/i18n/lang.go
Normal file
35
internal/i18n/lang.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package i18n
|
||||
|
||||
import "strings"
|
||||
|
||||
type Language string
|
||||
|
||||
type LanguageMap map[Language]MessageMap
|
||||
|
||||
const (
|
||||
English Language = "en"
|
||||
Dutch Language = "nl"
|
||||
French Language = "fr"
|
||||
German Language = "de"
|
||||
Russian Language = "ru"
|
||||
Default = English
|
||||
)
|
||||
|
||||
var Languages = LanguageMap{
|
||||
English: MsgEnglish,
|
||||
Dutch: MsgDutch,
|
||||
French: MsgFrench,
|
||||
German: MsgGerman,
|
||||
Russian: MsgRussian,
|
||||
}
|
||||
|
||||
var Lang = Default
|
||||
|
||||
func SetLang(s string) {
|
||||
if len(s) != 2 {
|
||||
Lang = Default
|
||||
} else {
|
||||
s = strings.ToLower(s)
|
||||
Lang = Language(s)
|
||||
}
|
||||
}
|
18
internal/i18n/lang_test.go
Normal file
18
internal/i18n/lang_test.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSetLang(t *testing.T) {
|
||||
assert.Equal(t, English, Lang)
|
||||
SetLang("D")
|
||||
assert.Equal(t, English, Lang)
|
||||
SetLang("De")
|
||||
assert.Equal(t, German, Lang)
|
||||
SetLang("")
|
||||
assert.Equal(t, English, Lang)
|
||||
assert.Equal(t, Default, Lang)
|
||||
}
|
33
internal/i18n/response.go
Normal file
33
internal/i18n/response.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package i18n
|
||||
|
||||
import "strings"
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Err string `json:"error,omitempty"`
|
||||
Msg string `json:"success,omitempty"`
|
||||
}
|
||||
|
||||
func (r Response) String() string {
|
||||
if r.Err != "" {
|
||||
return r.Err
|
||||
} else {
|
||||
return r.Msg
|
||||
}
|
||||
}
|
||||
|
||||
func (r Response) LowerString() string {
|
||||
return strings.ToLower(r.String())
|
||||
}
|
||||
|
||||
func (r Response) Error() string {
|
||||
return r.Err
|
||||
}
|
||||
|
||||
func NewResponse(code int, id Message, params ...interface{}) Response {
|
||||
if code < 400 {
|
||||
return Response{Code: code, Msg: Msg(id, params...)}
|
||||
} else {
|
||||
return Response{Code: code, Err: Msg(id, params...)}
|
||||
}
|
||||
}
|
38
internal/i18n/response_test.go
Normal file
38
internal/i18n/response_test.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewResponse(t *testing.T) {
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
resp := NewResponse(http.StatusConflict, ErrAlreadyExists, "A cat")
|
||||
assert.Equal(t, http.StatusConflict, resp.Code)
|
||||
assert.Equal(t, "A cat already exists", resp.Err)
|
||||
assert.Equal(t, "", resp.Msg)
|
||||
})
|
||||
|
||||
t.Run("unexpected error", func(t *testing.T) {
|
||||
resp := NewResponse(http.StatusInternalServerError, ErrUnexpected, "A cat")
|
||||
assert.Equal(t, http.StatusInternalServerError, resp.Code)
|
||||
assert.Equal(t, "Unexpected error, please try again", resp.Err)
|
||||
assert.Equal(t, "", resp.Msg)
|
||||
})
|
||||
|
||||
t.Run("changes saved", func(t *testing.T) {
|
||||
resp := NewResponse(http.StatusOK, MsgChangesSaved)
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
assert.Equal(t, "", resp.Err)
|
||||
assert.Equal(t, "Changes successfully saved", resp.Msg)
|
||||
|
||||
if s, err := json.Marshal(resp); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
assert.Equal(t, `{"code":200,"success":"Changes successfully saved"}`, string(s))
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue