Initial stub for feature flags in settings #284
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
265dafa08e
commit
47814e2fde
16 changed files with 160 additions and 119 deletions
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
9
internal/config/testdata/configEmpty.yml
vendored
9
internal/config/testdata/configEmpty.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue