photoprism/internal/api/config.go
Michael Mayer 82d61d1f93 File Types: Add experimental support for animated GIFs #590 #2207
Animated GIFs are transcoded to AVC because it is much smaller and
thus also suitable for long/large animations. In addition, this commit
adds support for more metadata fields such as frame rate, number of
frames, file capture timestamp (unix milliseconds), media type,
and software version. Support for SVG files can later be implemented in
a similar way.
2022-04-13 22:17:59 +02:00

146 lines
3.6 KiB
Go

package api
import (
"net/http"
"os"
"path/filepath"
"github.com/photoprism/photoprism/pkg/sanitize"
"github.com/gin-gonic/gin"
"gopkg.in/yaml.v2"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/i18n"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/fs"
)
// GetConfig returns client config values.
//
// GET /api/v1/config
func GetConfig(router *gin.RouterGroup) {
router.GET("/config", func(c *gin.Context) {
s := Auth(SessionID(c), acl.ResourceConfig, acl.ActionRead)
if s.Invalid() {
AbortUnauthorized(c)
return
}
conf := service.Config()
if s.User.Guest() {
c.JSON(http.StatusOK, conf.GuestConfig())
} else if s.User.Registered() {
c.JSON(http.StatusOK, conf.UserConfig())
} else {
c.JSON(http.StatusOK, conf.PublicConfig())
}
})
}
// GetConfigOptions returns backend config options.
//
// GET /api/v1/config/options
func GetConfigOptions(router *gin.RouterGroup) {
router.GET("/config/options", func(c *gin.Context) {
s := Auth(SessionID(c), acl.ResourceConfigOptions, acl.ActionRead)
conf := service.Config()
if s.Invalid() || conf.Public() || conf.DisableSettings() {
AbortUnauthorized(c)
return
}
c.JSON(http.StatusOK, conf.Options())
})
}
// SaveConfigOptions updates backend config options.
//
// POST /api/v1/config/options
func SaveConfigOptions(router *gin.RouterGroup) {
router.POST("/config/options", func(c *gin.Context) {
s := Auth(SessionID(c), acl.ResourceConfigOptions, acl.ActionUpdate)
conf := service.Config()
if s.Invalid() || conf.Public() || conf.DisableSettings() {
AbortUnauthorized(c)
return
}
fileName := conf.OptionsYaml()
if fileName == "" {
log.Errorf("config: empty options.yml file path")
AbortSaveFailed(c)
return
}
type valueMap map[string]interface{}
v := make(valueMap)
if fs.FileExists(fileName) {
yamlData, err := os.ReadFile(fileName)
if err != nil {
log.Errorf("config: failed loading values from %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
if err := yaml.Unmarshal(yamlData, v); err != nil {
log.Warnf("config: failed parsing values in %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
}
if err := c.BindJSON(&v); err != nil {
log.Errorf("config: %s (bind json)", err)
AbortBadRequest(c)
return
}
yamlData, err := yaml.Marshal(v)
if err != nil {
log.Errorf("config: %s (marshal yaml)", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
// Make sure directory exists.
if err := os.MkdirAll(filepath.Dir(fileName), os.ModePerm); err != nil {
log.Errorf("config: failed creating config path %s (%s)", filepath.Dir(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
// Write YAML data to file.
if err := os.WriteFile(fileName, yamlData, os.ModePerm); err != nil {
log.Errorf("config: failed writing values to %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
// Reload options.
if err := conf.Options().Load(fileName); err != nil {
log.Warnf("config: failed loading values from %s (%s)", sanitize.Log(fileName), err)
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
return
}
conf.Propagate()
UpdateClientConfig()
event.InfoMsg(i18n.MsgSettingsSaved)
c.JSON(http.StatusOK, conf.Options())
})
}