Initial stub for feature flags in settings #284

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-04-12 18:00:31 +02:00
parent 265dafa08e
commit 47814e2fde
16 changed files with 160 additions and 119 deletions

View file

@ -225,7 +225,7 @@
</v-list-tile-content>
</v-list-tile>
<v-list-tile to="/settings" @click="" class="p-navigation-settings">
<v-list-tile to="/settings" @click="" class="p-navigation-settings" v-if="!config.disableSettings">
<v-list-tile-action>
<v-icon>settings</v-icon>
</v-list-tile-action>

View file

@ -26,7 +26,7 @@ func GetSettings(router *gin.RouterGroup, conf *config.Config) {
// POST /api/v1/settings
func SaveSettings(router *gin.RouterGroup, conf *config.Config) {
router.POST("/settings", func(c *gin.Context) {
if Unauthorized(c, conf) {
if conf.DisableSettings() || Unauthorized(c, conf) {
c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized)
return
}

View file

@ -14,7 +14,7 @@ func TestTensorFlow_LabelsFromFile(t *testing.T) {
t.Run("chameleon_lime.jpg", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
result, err := tensorFlow.File(conf.ExamplesPath() + "/chameleon_lime.jpg")
@ -38,7 +38,7 @@ func TestTensorFlow_LabelsFromFile(t *testing.T) {
t.Run("not existing file", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
result, err := tensorFlow.File(conf.ExamplesPath() + "/notexisting.jpg")
assert.Contains(t, err.Error(), "no such file or directory")
@ -54,7 +54,7 @@ func TestTensorFlow_Labels(t *testing.T) {
t.Run("chameleon_lime.jpg", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
if imageBuffer, err := ioutil.ReadFile(conf.ExamplesPath() + "/chameleon_lime.jpg"); err != nil {
t.Error(err)
@ -77,7 +77,7 @@ func TestTensorFlow_Labels(t *testing.T) {
t.Run("dog_orange.jpg", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
if imageBuffer, err := ioutil.ReadFile(conf.ExamplesPath() + "/dog_orange.jpg"); err != nil {
t.Error(err)
@ -100,7 +100,7 @@ func TestTensorFlow_Labels(t *testing.T) {
t.Run("Random.docx", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
if imageBuffer, err := ioutil.ReadFile(conf.ExamplesPath() + "/Random.docx"); err != nil {
t.Error(err)
@ -113,7 +113,7 @@ func TestTensorFlow_Labels(t *testing.T) {
t.Run("6720px_white.jpg", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
if imageBuffer, err := ioutil.ReadFile(conf.ExamplesPath() + "/6720px_white.jpg"); err != nil {
t.Error(err)
@ -129,7 +129,7 @@ func TestTensorFlow_LoadModel(t *testing.T) {
t.Run("model path exists", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
result := tensorFlow.loadModel()
assert.Nil(t, result)
@ -137,7 +137,7 @@ func TestTensorFlow_LoadModel(t *testing.T) {
t.Run("model path does not exist", func(t *testing.T) {
conf := config.NewTestErrorConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
result := tensorFlow.loadModel()
assert.Contains(t, result.Error(), "Could not find SavedModel")
@ -148,7 +148,7 @@ func TestTensorFlow_BestLabels(t *testing.T) {
t.Run("labels not loaded", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
p := make([]float32, 1000)
@ -160,7 +160,7 @@ func TestTensorFlow_BestLabels(t *testing.T) {
t.Run("labels loaded", func(t *testing.T) {
conf := config.TestConfig()
path := conf.TensorFlowModelPath()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
tensorFlow.loadLabels(path)
p := make([]float32, 1000)
@ -183,7 +183,7 @@ func TestTensorFlow_MakeTensor(t *testing.T) {
t.Run("cat_brown.jpg", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
imageBuffer, err := ioutil.ReadFile(conf.ExamplesPath() + "/cat_brown.jpg")
assert.Nil(t, err)
@ -195,7 +195,7 @@ func TestTensorFlow_MakeTensor(t *testing.T) {
t.Run("Random.docx", func(t *testing.T) {
conf := config.TestConfig()
tensorFlow := New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tensorFlow := New(conf.ResourcesPath(), conf.DisableTensorFlow())
imageBuffer, err := ioutil.ReadFile(conf.ExamplesPath() + "/Random.docx")
assert.Nil(t, err)

View file

@ -52,7 +52,6 @@ func configAction(ctx *cli.Context) error {
fmt.Printf("resources-path %s\n", conf.ResourcesPath())
fmt.Printf("tf-version %s\n", conf.TensorFlowVersion())
fmt.Printf("tf-model-path %s\n", conf.TensorFlowModelPath())
fmt.Printf("tf-disabled %t\n", conf.TensorFlowDisabled())
fmt.Printf("templates-path %s\n", conf.HttpTemplatesPath())
fmt.Printf("favicons-path %s\n", conf.HttpFaviconsPath())
fmt.Printf("static-path %s\n", conf.HttpStaticPath())
@ -83,5 +82,8 @@ func configAction(ctx *cli.Context) error {
fmt.Printf("thumb-limit %d\n", conf.ThumbLimit())
fmt.Printf("thumb-filter %s\n", conf.ThumbFilter())
fmt.Printf("disable-tf %t\n", conf.DisableTensorFlow())
fmt.Printf("disable-settings %t\n", conf.DisableSettings())
return nil
}

View file

@ -12,6 +12,31 @@ import (
// ClientConfig contains HTTP client / Web UI config values
type ClientConfig map[string]interface{}
// Flags returns config flags as string slice.
func (c *Config) Flags() (flags []string) {
if c.Public() {
flags = append(flags, "public")
}
if c.Debug() {
flags = append(flags, "debug")
}
if c.Experimental() {
flags = append(flags, "experimental")
}
if c.ReadOnly() {
flags = append(flags, "readonly")
}
if !c.DisableSettings() {
flags = append(flags, "settings")
}
return flags
}
// PublicClientConfig returns reduced config values for non-public sites.
func (c *Config) PublicClientConfig() ClientConfig {
if c.Public() {
@ -20,22 +45,7 @@ func (c *Config) PublicClientConfig() ClientConfig {
jsHash := fs.Checksum(c.HttpStaticBuildPath() + "/app.js")
cssHash := fs.Checksum(c.HttpStaticBuildPath() + "/app.css")
// Feature Flags
var flags []string
if c.Public() {
flags = append(flags, "public")
}
if c.Debug() {
flags = append(flags, "debug")
}
if c.Experimental() {
flags = append(flags, "experimental")
}
if c.ReadOnly() {
flags = append(flags, "readonly")
}
configFlags := c.Flags()
var noPos = struct {
PhotoUUID string `json:"photo"`
@ -58,7 +68,7 @@ func (c *Config) PublicClientConfig() ClientConfig {
result := ClientConfig{
"settings": c.Settings(),
"flags": strings.Join(flags, " "),
"flags": strings.Join(configFlags, " "),
"name": c.Name(),
"url": c.Url(),
"title": c.Title(),
@ -73,6 +83,7 @@ func (c *Config) PublicClientConfig() ClientConfig {
"uploadNSFW": c.UploadNSFW(),
"public": c.Public(),
"experimental": c.Experimental(),
"disableSettings": c.DisableSettings(),
"albums": []string{},
"cameras": []string{},
"lenses": []string{},
@ -198,25 +209,10 @@ func (c *Config) ClientConfig() ClientConfig {
jsHash := fs.Checksum(c.HttpStaticBuildPath() + "/app.js")
cssHash := fs.Checksum(c.HttpStaticBuildPath() + "/app.css")
// Feature Flags
var flags []string
if c.Public() {
flags = append(flags, "public")
}
if c.Debug() {
flags = append(flags, "debug")
}
if c.Experimental() {
flags = append(flags, "experimental")
}
if c.ReadOnly() {
flags = append(flags, "readonly")
}
configFlags := c.Flags()
result := ClientConfig{
"flags": strings.Join(flags, " "),
"flags": strings.Join(configFlags, " "),
"name": c.Name(),
"url": c.Url(),
"title": c.Title(),
@ -231,6 +227,7 @@ func (c *Config) ClientConfig() ClientConfig {
"uploadNSFW": c.UploadNSFW(),
"public": c.Public(),
"experimental": c.Experimental(),
"disableSettings": c.DisableSettings(),
"albums": albums,
"cameras": cameras,
"lenses": lenses,

View file

@ -51,7 +51,7 @@ func TestConfig_TensorFlowDisabled(t *testing.T) {
ctx := CliTestContext()
c := NewConfig(ctx)
version := c.TensorFlowDisabled()
version := c.DisableTensorFlow()
assert.Equal(t, false, version)
}

View file

@ -230,11 +230,6 @@ var GlobalFlags = []cli.Flag{
Usage: "allow uploads that may contain offensive content",
EnvVar: "PHOTOPRISM_UPLOAD_NSFW",
},
cli.BoolFlag{
Name: "tf-disabled, t",
Usage: "don't use TensorFlow for image classification",
EnvVar: "PHOTOPRISM_TF_DISABLED",
},
cli.StringFlag{
Name: "geocoding-api, g",
Usage: "geocoding api (none, osm or places)",
@ -265,4 +260,14 @@ var GlobalFlags = []cli.Flag{
Value: "lanczos",
EnvVar: "PHOTOPRISM_THUMB_FILTER",
},
cli.BoolFlag{
Name: "disable-tf",
Usage: "don't use TensorFlow for image classification",
EnvVar: "PHOTOPRISM_DISABLE_TF",
},
cli.BoolFlag{
Name: "disable-settings",
Usage: "user can not change settings",
EnvVar: "PHOTOPRISM_DISABLE_SETTINGS",
},
}

View file

@ -73,12 +73,13 @@ type Params struct {
DetachServer bool `yaml:"detach-server" flag:"detach-server"`
DetectNSFW bool `yaml:"detect-nsfw" flag:"detect-nsfw"`
UploadNSFW bool `yaml:"upload-nsfw" flag:"upload-nsfw"`
DisableTensorFlow bool `yaml:"tf-disabled" flag:"tf-disabled"`
GeoCodingApi string `yaml:"geocoding-api" flag:"geocoding-api"`
ThumbQuality int `yaml:"thumb-quality" flag:"thumb-quality"`
ThumbSize int `yaml:"thumb-size" flag:"thumb-size"`
ThumbLimit int `yaml:"thumb-limit" flag:"thumb-limit"`
ThumbFilter string `yaml:"thumb-filter" flag:"thumb-filter"`
DisableTensorFlow bool `yaml:"disable-tf" flag:"disable-tf"`
DisableSettings bool `yaml:"disable-settings" flag:"disable-settings"`
}
// NewParams creates a new configuration entity by using two methods:

View file

@ -9,16 +9,33 @@ import (
"gopkg.in/yaml.v2"
)
// DisableSettings returns true if the user is not allowed to change settings.
func (c *Config) DisableSettings() bool {
return c.config.DisableSettings
}
type MapsSettings struct {
Animate int `json:"animate" yaml:"animate"`
Style string `json:"style" yaml:"style"`
}
type FeatureFlags struct {
Upload bool `json:"upload" yaml:"upload"`
Import bool `json:"import" yaml:"import"`
Labels bool `json:"labels" yaml:"labels"`
Places bool `json:"places" yaml:"places"`
Archive bool `json:"archive" yaml:"archive"`
Download bool `json:"download" yaml:"download"`
Edit bool `json:"edit" yaml:"edit"`
Share bool `json:"share" yaml:"share"`
}
// Settings contains Web UI settings
type Settings struct {
Theme string `json:"theme" yaml:"theme"`
Language string `json:"language" yaml:"language"`
Maps MapsSettings `json:"maps" yaml:"maps"`
Features FeatureFlags `json:"features" yaml:"features"`
}
// NewSettings returns a empty Settings
@ -30,6 +47,16 @@ func NewSettings() *Settings {
Animate: 0,
Style: "streets",
},
Features: FeatureFlags{
Upload: true,
Import: true,
Labels: true,
Places: true,
Archive: true,
Download: true,
Edit: true,
Share: true,
},
}
}

View file

@ -7,7 +7,7 @@ func (c *Config) TensorFlowVersion() string {
return tf.Version()
}
// TensorFlowDisabled returns true if the use of TensorFlow is disabled for image classification.
func (c *Config) TensorFlowDisabled() bool {
// DisableTensorFlow returns true if the use of TensorFlow is disabled for image classification.
func (c *Config) DisableTensorFlow() bool {
return c.config.DisableTensorFlow
}

View file

@ -3,3 +3,12 @@ language: german
maps:
animate: 0
style: streets
features:
upload: true
import: true
labels: true
places: true
archive: true
download: true
edit: true
share: true

View file

@ -12,7 +12,7 @@ import (
func TestNewImport(t *testing.T) {
conf := config.TestConfig()
tf := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow())
nd := nsfw.New(conf.NSFWModelPath())
ind := NewIndex(conf, tf, nd)
@ -29,7 +29,7 @@ func TestImport_DestinationFilename(t *testing.T) {
conf.InitializeTestData(t)
tf := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow())
nd := nsfw.New(conf.NSFWModelPath())
ind := NewIndex(conf, tf, nd)
@ -58,7 +58,7 @@ func TestImport_Start(t *testing.T) {
conf.InitializeTestData(t)
tf := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow())
nd := nsfw.New(conf.NSFWModelPath())
ind := NewIndex(conf, tf, nd)

View file

@ -134,7 +134,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
if file.FilePrimary {
primaryFile = file
if !ind.conf.TensorFlowDisabled() && (fileChanged || o.UpdateKeywords || o.UpdateLabels || o.UpdateTitle) {
if !ind.conf.DisableTensorFlow() && (fileChanged || o.UpdateKeywords || o.UpdateLabels || o.UpdateTitle) {
// Image classification via TensorFlow
labels = ind.classifyImage(m)
photo.PhotoNSFW = ind.isNSFW(m)

View file

@ -17,7 +17,7 @@ func TestIndex_Start(t *testing.T) {
conf.InitializeTestData(t)
tf := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow())
nd := nsfw.New(conf.NSFWModelPath())
ind := NewIndex(conf, tf, nd)

View file

@ -27,7 +27,7 @@ func TestResample_Start(t *testing.T) {
conf.InitializeTestData(t)
tf := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
tf := classify.New(conf.ResourcesPath(), conf.DisableTensorFlow())
nd := nsfw.New(conf.NSFWModelPath())
ind := NewIndex(conf, tf, nd)

View file

@ -9,7 +9,7 @@ import (
var onceClassify sync.Once
func initClassify() {
services.Classify = classify.New(Config().ResourcesPath(), Config().TensorFlowDisabled())
services.Classify = classify.New(Config().ResourcesPath(), Config().DisableTensorFlow())
}
func Classify() *classify.TensorFlow {