From aca929e3c967c1b6ac6bee73e4c680ce32cd74b8 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sun, 15 Jan 2023 15:36:20 +0100 Subject: [PATCH] Docs: Add command that generates an overview of YAML config values see https://docs.photoprism.app/getting-started/config-files/ Signed-off-by: Michael Mayer --- internal/commands/show.go | 3 +- internal/commands/show_flags.go | 112 ++++++++++++++++++++++++++++++ internal/commands/show_options.go | 48 ++++++------- internal/config/flags.go | 16 +++++ internal/config/options.go | 10 +-- internal/config/options_report.go | 32 +++++++++ 6 files changed, 187 insertions(+), 34 deletions(-) create mode 100644 internal/commands/show_flags.go create mode 100644 internal/config/options_report.go diff --git a/internal/commands/show.go b/internal/commands/show.go index 09a079307..f2a14fafb 100644 --- a/internal/commands/show.go +++ b/internal/commands/show.go @@ -7,9 +7,10 @@ import ( // ShowCommand registers the show subcommands. var ShowCommand = cli.Command{ Name: "show", - Usage: "Shows supported formats, standards, and features", + Usage: "Shows supported formats, features, and config options", Subcommands: []cli.Command{ ShowConfigCommand, + ShowFlagsCommand, ShowOptionsCommand, ShowFiltersCommand, ShowFormatsCommand, diff --git a/internal/commands/show_flags.go b/internal/commands/show_flags.go new file mode 100644 index 000000000..dcef9e387 --- /dev/null +++ b/internal/commands/show_flags.go @@ -0,0 +1,112 @@ +package commands + +import ( + "fmt" + + "github.com/sirupsen/logrus" + "github.com/urfave/cli" + + "github.com/photoprism/photoprism/internal/config" + "github.com/photoprism/photoprism/pkg/report" +) + +// ShowFlagsCommand configures the command name, flags, and action. +var ShowFlagsCommand = cli.Command{ + Name: "flags", + Usage: "Displays supported environment variables and CLI flags", + Flags: report.CliFlags, + Action: showFlagsAction, +} + +var faceFlagsInfo = `!!! info "" + To [recognize faces](https://docs.photoprism.app/user-guide/organize/people/), PhotoPrism first extracts crops from your images using a [library](https://github.com/esimov/pigo) based on [pixel intensity comparisons](https://dl.photoprism.app/pdf/20140820-Pixel_Intensity_Comparisons.pdf). These are then fed into TensorFlow to compute [512-dimensional vectors](https://dl.photoprism.app/pdf/20150101-FaceNet.pdf) for characterization. In the final step, the [DBSCAN algorithm](https://en.wikipedia.org/wiki/DBSCAN) attempts to cluster these so-called face embeddings, so they can be matched to persons with just a few clicks. A reasonable range for the similarity distance between face embeddings is between 0.60 and 0.70, with a higher value being more aggressive and leading to larger clusters with more false positives. To cluster a smaller number of faces, you can reduce the core to 3 or 2 similar faces. + +We recommend that only advanced users change these parameters:` + +// showFlagsAction shows environment variable command-line parameter names. +func showFlagsAction(ctx *cli.Context) error { + conf := config.NewConfig(ctx) + conf.SetLogLevel(logrus.FatalLevel) + + rows, cols := config.Flags.Report() + + // CSV Export? + if ctx.Bool("csv") || ctx.Bool("tsv") { + result, err := report.RenderFormat(rows, cols, report.CliFormat(ctx)) + + fmt.Println(result) + + return err + } + + type Section struct { + Start string + Title string + Info string + } + + s := []Section{ + {Start: "PHOTOPRISM_ADMIN_PASSWORD", Title: "Authentication"}, + {Start: "PHOTOPRISM_LOG_LEVEL", Title: "Logging"}, + {Start: "PHOTOPRISM_CONFIG_PATH", Title: "Storage"}, + {Start: "PHOTOPRISM_WORKERS", Title: "Index Workers"}, + {Start: "PHOTOPRISM_READONLY", Title: "Feature Flags"}, + {Start: "PHOTOPRISM_DEFAULT_LOCALE", Title: "Customization"}, + {Start: "PHOTOPRISM_CDN_URL", Title: "Site Information"}, + {Start: "PHOTOPRISM_TRUSTED_PROXY", Title: "Web Server"}, + {Start: "PHOTOPRISM_DATABASE_DRIVER", Title: "Database Connection"}, + {Start: "PHOTOPRISM_DARKTABLE_BIN", Title: "File Converters"}, + {Start: "PHOTOPRISM_DOWNLOAD_TOKEN", Title: "Security Tokens"}, + {Start: "PHOTOPRISM_THUMB_COLOR", Title: "Image Quality"}, + {Start: "PHOTOPRISM_FACE_SIZE", Title: "Face Recognition", + Info: faceFlagsInfo}, + {Start: "PHOTOPRISM_PID_FILENAME", Title: "Daemon Mode", + Info: "If you start the server as a *daemon* in the background, you can additionally specify a filename for the log and the process ID:"}, + } + + j := 0 + + for i, sec := range s { + fmt.Printf("### %s ###\n\n", sec.Title) + if sec.Info != "" && ctx.Bool("md") { + fmt.Printf("%s\n\n", sec.Info) + } + + secRows := make([][]string, 0, len(rows)) + + for { + row := rows[j] + + if len(row) < 1 { + continue + } + + if i < len(s)-1 { + if s[i+1].Start == row[0] { + break + } + } + + secRows = append(secRows, row) + j++ + + if j >= len(rows) { + break + } + } + + result, err := report.RenderFormat(secRows, cols, report.CliFormat(ctx)) + + if err != nil { + return err + } + + fmt.Println(result) + + if j >= len(rows) { + break + } + } + + return nil +} diff --git a/internal/commands/show_options.go b/internal/commands/show_options.go index 3438e1604..677eacc18 100644 --- a/internal/commands/show_options.go +++ b/internal/commands/show_options.go @@ -12,24 +12,18 @@ import ( // ShowOptionsCommand configures the command name, flags, and action. var ShowOptionsCommand = cli.Command{ - Name: "options", - Aliases: []string{"flags"}, - Usage: "Displays supported config flags and variable names", - Flags: report.CliFlags, - Action: showOptionsAction, + Name: "options", + Usage: "Displays supported YAML config options and CLI flags", + Flags: report.CliFlags, + Action: showOptionsAction, } -var faceOptionsInfo = `!!! info "" - To [recognize faces](https://docs.photoprism.app/user-guide/organize/people/), PhotoPrism first extracts crops from your images using a [library](https://github.com/esimov/pigo) based on [pixel intensity comparisons](https://dl.photoprism.app/pdf/20140820-Pixel_Intensity_Comparisons.pdf). These are then fed into TensorFlow to compute [512-dimensional vectors](https://dl.photoprism.app/pdf/20150101-FaceNet.pdf) for characterization. In the final step, the [DBSCAN algorithm](https://en.wikipedia.org/wiki/DBSCAN) attempts to cluster these so-called face embeddings, so they can be matched to persons with just a few clicks. A reasonable range for the similarity distance between face embeddings is between 0.60 and 0.70, with a higher value being more aggressive and leading to larger clusters with more false positives. To cluster a smaller number of faces, you can reduce the core to 3 or 2 similar faces. - -We recommend that only advanced users change these parameters:` - -// showOptionsAction shows environment variable command-line parameter names. +// showOptionsAction shows supported YAML config file options. func showOptionsAction(ctx *cli.Context) error { conf := config.NewConfig(ctx) - conf.SetLogLevel(logrus.FatalLevel) + conf.SetLogLevel(logrus.TraceLevel) - rows, cols := config.Flags.Report() + rows, cols := conf.Options().Report() // CSV Export? if ctx.Bool("csv") || ctx.Bool("tsv") { @@ -47,21 +41,19 @@ func showOptionsAction(ctx *cli.Context) error { } s := []Section{ - {Start: "PHOTOPRISM_ADMIN_PASSWORD", Title: "Authentication"}, - {Start: "PHOTOPRISM_LOG_LEVEL", Title: "Logging"}, - {Start: "PHOTOPRISM_CONFIG_PATH", Title: "Storage"}, - {Start: "PHOTOPRISM_WORKERS", Title: "Index Workers"}, - {Start: "PHOTOPRISM_READONLY", Title: "Feature Flags"}, - {Start: "PHOTOPRISM_DEFAULT_LOCALE", Title: "Customization"}, - {Start: "PHOTOPRISM_CDN_URL", Title: "Site Information"}, - {Start: "PHOTOPRISM_TRUSTED_PROXY", Title: "Web Server"}, - {Start: "PHOTOPRISM_DATABASE_DRIVER", Title: "Database Connection"}, - {Start: "PHOTOPRISM_DARKTABLE_BIN", Title: "File Converters"}, - {Start: "PHOTOPRISM_DOWNLOAD_TOKEN", Title: "Security Tokens"}, - {Start: "PHOTOPRISM_THUMB_COLOR", Title: "Image Quality"}, - {Start: "PHOTOPRISM_FACE_SIZE", Title: "Face Recognition", - Info: faceOptionsInfo}, - {Start: "PHOTOPRISM_PID_FILENAME", Title: "Daemon Mode", + {Start: "AuthMode", Title: "Authentication"}, + {Start: "LogLevel", Title: "Logging"}, + {Start: "ConfigPath", Title: "Storage"}, + {Start: "Workers", Title: "Index Workers"}, + {Start: "ReadOnly", Title: "Feature Flags"}, + {Start: "DefaultTheme", Title: "Customization"}, + {Start: "CdnUrl", Title: "Site Information"}, + {Start: "TrustedProxies", Title: "Web Server"}, + {Start: "DatabaseDriver", Title: "Database Connection"}, + {Start: "DarktableBin", Title: "File Converters"}, + {Start: "DownloadToken", Title: "Security Tokens"}, + {Start: "ThumbColor", Title: "Image Quality"}, + {Start: "PIDFilename", Title: "Daemon Mode", Info: "If you start the server as a *daemon* in the background, you can additionally specify a filename for the log and the process ID:"}, } diff --git a/internal/config/flags.go b/internal/config/flags.go index 7c69f82d7..0f8df566c 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -451,6 +451,22 @@ var Flags = CliFlags{ Usage: "disable HTTPS even if a certificate is available", EnvVar: "PHOTOPRISM_DISABLE_TLS", }}, { + Flag: cli.StringFlag{ + Name: "tls-email", + Usage: "`EMAIL` address to enable automatic HTTPS via Let's Encrypt", + EnvVar: "PHOTOPRISM_TLS_EMAIL", + Hidden: true, + }}, { + Flag: cli.StringFlag{ + Name: "tls-cert", + Usage: "public HTTPS certificate `FILE` (.crt)", + EnvVar: "PHOTOPRISM_TLS_CERT", + }}, { + Flag: cli.StringFlag{ + Name: "tls-key", + Usage: "private HTTPS key `FILE` (.key)", + EnvVar: "PHOTOPRISM_TLS_KEY", + }}, { Flag: cli.StringFlag{ Name: "database-driver, db", Usage: "database `DRIVER` (sqlite, mysql)", diff --git a/internal/config/options.go b/internal/config/options.go index 67e3f59a7..e329a2033 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -38,10 +38,8 @@ type Options struct { Trace bool `yaml:"Trace" json:"Trace" flag:"trace"` Test bool `yaml:"-" json:"Test,omitempty" flag:"test"` Unsafe bool `yaml:"-" json:"-" flag:"unsafe"` - Demo bool `yaml:"Demo" json:"-" flag:"demo"` + Demo bool `yaml:"-" json:"-" flag:"demo"` Sponsor bool `yaml:"-" json:"-" flag:"sponsor"` - ReadOnly bool `yaml:"ReadOnly" json:"ReadOnly" flag:"read-only"` - Experimental bool `yaml:"Experimental" json:"Experimental" flag:"experimental"` ConfigPath string `yaml:"ConfigPath" json:"-" flag:"config-path"` DefaultsYaml string `json:"-" yaml:"-" flag:"defaults-yaml"` OriginalsPath string `yaml:"OriginalsPath" json:"-" flag:"originals-path"` @@ -61,6 +59,8 @@ type Options struct { WakeupInterval time.Duration `yaml:"WakeupInterval" json:"WakeupInterval" flag:"wakeup-interval"` AutoIndex int `yaml:"AutoIndex" json:"AutoIndex" flag:"auto-index"` AutoImport int `yaml:"AutoImport" json:"AutoImport" flag:"auto-import"` + ReadOnly bool `yaml:"ReadOnly" json:"ReadOnly" flag:"read-only"` + Experimental bool `yaml:"Experimental" json:"Experimental" flag:"experimental"` DisableWebDAV bool `yaml:"DisableWebDAV" json:"DisableWebDAV" flag:"disable-webdav"` DisableBackups bool `yaml:"DisableBackups" json:"DisableBackups" flag:"disable-backups"` DisableSettings bool `yaml:"DisableSettings" json:"-" flag:"disable-settings"` @@ -102,7 +102,7 @@ type Options struct { HttpHost string `yaml:"HttpHost" json:"-" flag:"http-host"` HttpPort int `yaml:"HttpPort" json:"-" flag:"http-port"` DisableTLS bool `yaml:"DisableTLS" json:"DisableTLS" flag:"disable-tls"` - TLSEmail string `yaml:"TLSEmail" json:"TLSEmail" flag:"tls-email"` // TLSEmail enabled automatic HTTPS via Let's Encrypt if set a valid email address. + TLSEmail string `yaml:"TLSEmail" json:"TLSEmail" flag:"tls-email"` TLSCert string `yaml:"TLSCert" json:"TLSCert" flag:"tls-cert"` TLSKey string `yaml:"TLSKey" json:"TLSKey" flag:"tls-key"` DatabaseDriver string `yaml:"DatabaseDriver" json:"-" flag:"database-driver"` @@ -126,7 +126,6 @@ type Options struct { FFmpegEncoder string `yaml:"FFmpegEncoder" json:"FFmpegEncoder" flag:"ffmpeg-encoder"` FFmpegBitrate int `yaml:"FFmpegBitrate" json:"FFmpegBitrate" flag:"ffmpeg-bitrate"` ExifToolBin string `yaml:"ExifToolBin" json:"-" flag:"exiftool-bin"` - DetachServer bool `yaml:"DetachServer" json:"-" flag:"detach-server"` DownloadToken string `yaml:"DownloadToken" json:"-" flag:"download-token"` PreviewToken string `yaml:"PreviewToken" json:"-" flag:"preview-token"` ThumbColor string `yaml:"ThumbColor" json:"ThumbColor" flag:"thumb-color"` @@ -146,6 +145,7 @@ type Options struct { FaceMatchDist float64 `yaml:"-" json:"-" flag:"face-match-dist"` PIDFilename string `yaml:"PIDFilename" json:"-" flag:"pid-filename"` LogFilename string `yaml:"LogFilename" json:"-" flag:"log-filename"` + DetachServer bool `yaml:"DetachServer" json:"-" flag:"detach-server"` } // NewOptions creates a new configuration entity by using two methods: diff --git a/internal/config/options_report.go b/internal/config/options_report.go new file mode 100644 index 000000000..4a066db78 --- /dev/null +++ b/internal/config/options_report.go @@ -0,0 +1,32 @@ +package config + +import ( + "fmt" + "reflect" +) + +// Report returns global config values as a table for reporting. +func (c Options) Report() (rows [][]string, cols []string) { + v := reflect.ValueOf(c) + + cols = []string{"Name", "Type", "CLI Flag"} + rows = make([][]string, 0, v.NumField()) + + // Iterate through all config fields. + for i := 0; i < v.NumField(); i++ { + fieldValue := v.Field(i) + + yamlName := v.Type().Field(i).Tag.Get("yaml") + flagName := v.Type().Field(i).Tag.Get("flag") + + if yamlName == "" || yamlName == "-" || flagName == "" { + continue + } + + fieldType := fmt.Sprintf("%T", fieldValue.Interface()) + + rows = append(rows, []string{yamlName, fieldType, "--" + flagName}) + } + + return rows, cols +}