mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Config: Refactor initialization of settings and database connection
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -214,7 +214,7 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
|||||||
// Set user avatar image?
|
// Set user avatar image?
|
||||||
if avatarUrl := userInfo.Picture; avatarUrl == "" || user.HasAvatar() {
|
if avatarUrl := userInfo.Picture; avatarUrl == "" || user.HasAvatar() {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
} else if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC); err != nil {
|
} else if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil {
|
||||||
event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()})
|
event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()})
|
||||||
}
|
}
|
||||||
} else if conf.OIDCRegister() {
|
} else if conf.OIDCRegister() {
|
||||||
@@ -287,7 +287,7 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
|||||||
// Set user avatar image.
|
// Set user avatar image.
|
||||||
if avatarUrl := userInfo.Picture; avatarUrl == "" {
|
if avatarUrl := userInfo.Picture; avatarUrl == "" {
|
||||||
event.AuditDebug([]string{clientIp, "create session", "oidc", userName, "no avatar image provided"})
|
event.AuditDebug([]string{clientIp, "create session", "oidc", userName, "no avatar image provided"})
|
||||||
} else if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC); err != nil {
|
} else if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil {
|
||||||
event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()})
|
event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ func UploadUserAvatar(router *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set user avatar image.
|
// Set user avatar image.
|
||||||
if err = avatar.SetUserImage(m, filePath, entity.SrcManual); err != nil {
|
if err = avatar.SetUserImage(m, filePath, entity.SrcManual, conf.ThumbCachePath()); err != nil {
|
||||||
event.AuditErr([]string{ClientIP(c), "session %s", "upload avatar", "%s"}, s.RefID, err)
|
event.AuditErr([]string{ClientIP(c), "session %s", "upload avatar", "%s"}, s.RefID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
// init initializes the package.
|
// init initializes the package.
|
||||||
func init() {
|
func init() {
|
||||||
// Register OpenID Connect extension.
|
// Register OpenID Connect extension.
|
||||||
config.Register("oidc", UpdateConfig, ClientConfig)
|
config.Register("oidc", InitConfig, ClientConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientConfig returns the OIDC client config values.
|
// ClientConfig returns the OIDC client config values.
|
||||||
@@ -24,7 +24,7 @@ func ClientConfig(c *config.Config, t config.ClientType) config.Map {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateConfig initializes the OIDC config options.
|
// InitConfig initializes the OIDC config options.
|
||||||
func UpdateConfig(c *config.Config) error {
|
func InitConfig(c *config.Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ func backupAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
if backupDatabase {
|
if backupDatabase {
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ func CallWithDependencies(ctx *cli.Context, action func(conf *config.Config) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
// Run command.
|
// Run command.
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ func convertAction(ctx *cli.Context) error {
|
|||||||
return config.ErrReadOnly
|
return config.ErrReadOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
convertPath := conf.OriginalsPath()
|
convertPath := conf.OriginalsPath()
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ func findAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
frm := form.SearchPhotos{
|
frm := form.SearchPhotos{
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ func migrationsStatusAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
var ids []string
|
var ids []string
|
||||||
@@ -153,7 +152,6 @@ func migrationsRunAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
if ctx.Bool("trace") {
|
if ctx.Bool("trace") {
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ func resetAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
if !ctx.Bool("yes") {
|
if !ctx.Bool("yes") {
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ func restoreAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
// Restore database from backup dump?
|
// Restore database from backup dump?
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ func thumbsAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.RegisterDb()
|
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
dir := strings.TrimSpace(ctx.Args().First())
|
dir := strings.TrimSpace(ctx.Args().First())
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -128,7 +127,7 @@ func NewConfig(ctx *cli.Context) *Config {
|
|||||||
// Initialize logger.
|
// Initialize logger.
|
||||||
initLogger()
|
initLogger()
|
||||||
|
|
||||||
// Initialize options from config file and CLI context.
|
// Initialize options from the "defaults.yml" file and CLI context.
|
||||||
c := &Config{
|
c := &Config{
|
||||||
cliCtx: ctx,
|
cliCtx: ctx,
|
||||||
options: NewOptions(ctx),
|
options: NewOptions(ctx),
|
||||||
@@ -137,7 +136,7 @@ func NewConfig(ctx *cli.Context) *Config {
|
|||||||
start: start,
|
start: start,
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteFile values with options.yml from config path.
|
// Override options with values from the "options.yml" file, if it exists.
|
||||||
if optionsYaml := c.OptionsYaml(); fs.FileExists(optionsYaml) {
|
if optionsYaml := c.OptionsYaml(); fs.FileExists(optionsYaml) {
|
||||||
if err := c.options.Load(optionsYaml); err != nil {
|
if err := c.options.Load(optionsYaml); err != nil {
|
||||||
log.Warnf("config: failed loading values from %s (%s)", clean.Log(optionsYaml), err)
|
log.Warnf("config: failed loading values from %s (%s)", clean.Log(optionsYaml), err)
|
||||||
@@ -146,102 +145,9 @@ func NewConfig(ctx *cli.Context) *Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ext().Init(c)
|
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsafe checks if unsafe settings are allowed.
|
|
||||||
func (c *Config) Unsafe() bool {
|
|
||||||
return c.options.Unsafe
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restart checks if the application should be restarted, e.g. after an update or a config changes.
|
|
||||||
func (c *Config) Restart() bool {
|
|
||||||
return mutex.Restart.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CliContext returns the cli context if set.
|
|
||||||
func (c *Config) CliContext() *cli.Context {
|
|
||||||
if c.cliCtx == nil {
|
|
||||||
log.Warnf("config: cli context not set - you may have found a bug")
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.cliCtx
|
|
||||||
}
|
|
||||||
|
|
||||||
// CliGlobalString returns a global cli string flag value if set.
|
|
||||||
func (c *Config) CliGlobalString(name string) string {
|
|
||||||
if c.cliCtx == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.cliCtx.GlobalString(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options returns the raw config options.
|
|
||||||
func (c *Config) Options() *Options {
|
|
||||||
if c.options == nil {
|
|
||||||
log.Warnf("config: options should not be nil - you may have found a bug")
|
|
||||||
c.options = NewOptions(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.options
|
|
||||||
}
|
|
||||||
|
|
||||||
// Propagate updates config options in other packages as needed.
|
|
||||||
func (c *Config) Propagate() {
|
|
||||||
FlushCache()
|
|
||||||
log.SetLevel(c.LogLevel())
|
|
||||||
|
|
||||||
// Initialize the thumbnail generation package.
|
|
||||||
thumb.Library = c.ThumbLibrary()
|
|
||||||
thumb.Color = c.ThumbColor()
|
|
||||||
thumb.Filter = c.ThumbFilter()
|
|
||||||
thumb.SizeCached = c.ThumbSizePrecached()
|
|
||||||
thumb.SizeOnDemand = c.ThumbSizeUncached()
|
|
||||||
thumb.JpegQualityDefault = c.JpegQuality()
|
|
||||||
thumb.CachePublic = c.HttpCachePublic()
|
|
||||||
|
|
||||||
// Set cache expiration defaults.
|
|
||||||
ttl.CacheDefault = c.HttpCacheMaxAge()
|
|
||||||
ttl.CacheVideo = c.HttpVideoMaxAge()
|
|
||||||
|
|
||||||
// Set geocoding parameters.
|
|
||||||
places.UserAgent = c.UserAgent()
|
|
||||||
entity.GeoApi = c.GeoApi()
|
|
||||||
|
|
||||||
// Set session cache duration.
|
|
||||||
entity.SessionCacheDuration = c.SessionCacheDuration()
|
|
||||||
|
|
||||||
// Set minimum password length.
|
|
||||||
entity.PasswordLength = c.PasswordLength()
|
|
||||||
|
|
||||||
// Set path for user assets.
|
|
||||||
entity.UsersPath = c.UsersPath()
|
|
||||||
|
|
||||||
// Set API preview and download default tokens.
|
|
||||||
entity.PreviewToken.Set(c.PreviewToken(), entity.TokenConfig)
|
|
||||||
entity.DownloadToken.Set(c.DownloadToken(), entity.TokenConfig)
|
|
||||||
entity.ValidateTokens = !c.Public()
|
|
||||||
|
|
||||||
// Set face recognition parameters.
|
|
||||||
face.ScoreThreshold = c.FaceScore()
|
|
||||||
face.OverlapThreshold = c.FaceOverlap()
|
|
||||||
face.ClusterScoreThreshold = c.FaceClusterScore()
|
|
||||||
face.ClusterSizeThreshold = c.FaceClusterSize()
|
|
||||||
face.ClusterCore = c.FaceClusterCore()
|
|
||||||
face.ClusterDist = c.FaceClusterDist()
|
|
||||||
face.MatchDist = c.FaceMatchDist()
|
|
||||||
|
|
||||||
// Set default theme and locale.
|
|
||||||
customize.DefaultTheme = c.DefaultTheme()
|
|
||||||
customize.DefaultLocale = c.DefaultLocale()
|
|
||||||
|
|
||||||
c.Settings().Propagate()
|
|
||||||
c.Hub().Propagate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init creates directories, parses additional config files, opens a database connection and initializes dependencies.
|
// Init creates directories, parses additional config files, opens a database connection and initializes dependencies.
|
||||||
func (c *Config) Init() error {
|
func (c *Config) Init() error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
@@ -304,11 +210,18 @@ func (c *Config) Init() error {
|
|||||||
_ = os.Setenv("HTTPS_PROXY", httpsProxy)
|
_ = os.Setenv("HTTPS_PROXY", httpsProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure HTTP user agent.
|
// Load settings from the "settings.yml" config file.
|
||||||
places.UserAgent = c.UserAgent()
|
|
||||||
|
|
||||||
c.initSettings()
|
c.initSettings()
|
||||||
c.initHub()
|
|
||||||
|
// Connect to database.
|
||||||
|
if err := c.connectDb(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
c.RegisterDb()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize extensions.
|
||||||
|
Ext().Init(c)
|
||||||
|
|
||||||
// Initialize the thumbnail generation package.
|
// Initialize the thumbnail generation package.
|
||||||
thumb.Init(memory.FreeMemory(), c.IndexWorkers(), c.ThumbLibrary())
|
thumb.Init(memory.FreeMemory(), c.IndexWorkers(), c.ThumbLibrary())
|
||||||
@@ -316,10 +229,8 @@ func (c *Config) Init() error {
|
|||||||
// Update package defaults.
|
// Update package defaults.
|
||||||
c.Propagate()
|
c.Propagate()
|
||||||
|
|
||||||
// Connect to database.
|
// Show support information.
|
||||||
if err := c.connectDb(); err != nil {
|
if !c.Sponsor() {
|
||||||
return err
|
|
||||||
} else if !c.Sponsor() {
|
|
||||||
log.Info(MsgSponsor)
|
log.Info(MsgSponsor)
|
||||||
log.Info(MsgSignUp)
|
log.Info(MsgSignUp)
|
||||||
}
|
}
|
||||||
@@ -330,6 +241,97 @@ func (c *Config) Init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Propagate updates config options in other packages as needed.
|
||||||
|
func (c *Config) Propagate() {
|
||||||
|
FlushCache()
|
||||||
|
log.SetLevel(c.LogLevel())
|
||||||
|
|
||||||
|
// Initialize the thumbnail generation package.
|
||||||
|
thumb.Library = c.ThumbLibrary()
|
||||||
|
thumb.Color = c.ThumbColor()
|
||||||
|
thumb.Filter = c.ThumbFilter()
|
||||||
|
thumb.SizeCached = c.ThumbSizePrecached()
|
||||||
|
thumb.SizeOnDemand = c.ThumbSizeUncached()
|
||||||
|
thumb.JpegQualityDefault = c.JpegQuality()
|
||||||
|
thumb.CachePublic = c.HttpCachePublic()
|
||||||
|
|
||||||
|
// Set cache expiration defaults.
|
||||||
|
ttl.CacheDefault = c.HttpCacheMaxAge()
|
||||||
|
ttl.CacheVideo = c.HttpVideoMaxAge()
|
||||||
|
|
||||||
|
// Set geocoding parameters.
|
||||||
|
places.UserAgent = c.UserAgent()
|
||||||
|
entity.GeoApi = c.GeoApi()
|
||||||
|
|
||||||
|
// Set session cache duration.
|
||||||
|
entity.SessionCacheDuration = c.SessionCacheDuration()
|
||||||
|
|
||||||
|
// Set minimum password length.
|
||||||
|
entity.PasswordLength = c.PasswordLength()
|
||||||
|
|
||||||
|
// Set path for user assets.
|
||||||
|
entity.UsersPath = c.UsersPath()
|
||||||
|
|
||||||
|
// Set API preview and download default tokens.
|
||||||
|
entity.PreviewToken.Set(c.PreviewToken(), entity.TokenConfig)
|
||||||
|
entity.DownloadToken.Set(c.DownloadToken(), entity.TokenConfig)
|
||||||
|
entity.ValidateTokens = !c.Public()
|
||||||
|
|
||||||
|
// Set face recognition parameters.
|
||||||
|
face.ScoreThreshold = c.FaceScore()
|
||||||
|
face.OverlapThreshold = c.FaceOverlap()
|
||||||
|
face.ClusterScoreThreshold = c.FaceClusterScore()
|
||||||
|
face.ClusterSizeThreshold = c.FaceClusterSize()
|
||||||
|
face.ClusterCore = c.FaceClusterCore()
|
||||||
|
face.ClusterDist = c.FaceClusterDist()
|
||||||
|
face.MatchDist = c.FaceMatchDist()
|
||||||
|
|
||||||
|
// Set default theme and locale.
|
||||||
|
customize.DefaultTheme = c.DefaultTheme()
|
||||||
|
customize.DefaultLocale = c.DefaultLocale()
|
||||||
|
|
||||||
|
c.Settings().Propagate()
|
||||||
|
c.Hub().Propagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options returns the raw config options.
|
||||||
|
func (c *Config) Options() *Options {
|
||||||
|
if c.options == nil {
|
||||||
|
log.Warnf("config: options should not be nil - you may have found a bug")
|
||||||
|
c.options = NewOptions(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe checks if unsafe settings are allowed.
|
||||||
|
func (c *Config) Unsafe() bool {
|
||||||
|
return c.options.Unsafe
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart checks if the application should be restarted, e.g. after an update or a config changes.
|
||||||
|
func (c *Config) Restart() bool {
|
||||||
|
return mutex.Restart.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CliContext returns the cli context if set.
|
||||||
|
func (c *Config) CliContext() *cli.Context {
|
||||||
|
if c.cliCtx == nil {
|
||||||
|
log.Warnf("config: cli context not set - you may have found a bug")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.cliCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
// CliGlobalString returns a global cli string flag value if set.
|
||||||
|
func (c *Config) CliGlobalString(name string) string {
|
||||||
|
if c.cliCtx == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.cliCtx.GlobalString(name)
|
||||||
|
}
|
||||||
|
|
||||||
// readSerial reads and returns the current storage serial.
|
// readSerial reads and returns the current storage serial.
|
||||||
func (c *Config) readSerial() string {
|
func (c *Config) readSerial() string {
|
||||||
storageName := filepath.Join(c.StoragePath(), serialName)
|
storageName := filepath.Join(c.StoragePath(), serialName)
|
||||||
@@ -437,192 +439,6 @@ func (c *Config) Copyright() string {
|
|||||||
return c.options.Copyright
|
return c.options.Copyright
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseUri returns the site base URI for a given resource.
|
|
||||||
func (c *Config) BaseUri(res string) string {
|
|
||||||
if c.SiteUrl() == "" {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := url.Parse(c.SiteUrl())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimRight(u.EscapedPath(), "/") + res
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApiUri returns the api URI.
|
|
||||||
func (c *Config) ApiUri() string {
|
|
||||||
return c.BaseUri(ApiUri)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CdnUrl returns the optional content delivery network URI without trailing slash.
|
|
||||||
func (c *Config) CdnUrl(res string) string {
|
|
||||||
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimRight(c.options.CdnUrl, "/") + res
|
|
||||||
}
|
|
||||||
|
|
||||||
// UseCdn checks if a Content Deliver Network (CDN) is used to serve static content.
|
|
||||||
func (c *Config) UseCdn() bool {
|
|
||||||
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoCdn checks if there is no Content Deliver Network (CDN) configured to serve static content.
|
|
||||||
func (c *Config) NoCdn() bool {
|
|
||||||
return !c.UseCdn()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CdnDomain returns the content delivery network domain name if specified.
|
|
||||||
func (c *Config) CdnDomain() string {
|
|
||||||
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
|
||||||
return ""
|
|
||||||
} else if u, err := url.Parse(c.options.CdnUrl); err != nil {
|
|
||||||
return ""
|
|
||||||
} else {
|
|
||||||
return u.Hostname()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CdnVideo checks if videos should be streamed using the configured CDN.
|
|
||||||
func (c *Config) CdnVideo() bool {
|
|
||||||
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.options.CdnVideo
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORSOrigin returns the value for the Access-Control-Allow-Origin header, if any.
|
|
||||||
func (c *Config) CORSOrigin() string {
|
|
||||||
return clean.Header(c.options.CORSOrigin)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORSHeaders returns the value for the Access-Control-Allow-Headers header, if any.
|
|
||||||
func (c *Config) CORSHeaders() string {
|
|
||||||
return clean.Header(c.options.CORSHeaders)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORSMethods returns the value for the Access-Control-Allow-Methods header, if any.
|
|
||||||
func (c *Config) CORSMethods() string {
|
|
||||||
return clean.Header(c.options.CORSMethods)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContentUri returns the content delivery URI.
|
|
||||||
func (c *Config) ContentUri() string {
|
|
||||||
return c.CdnUrl(c.ApiUri())
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoUri returns the video streaming URI.
|
|
||||||
func (c *Config) VideoUri() string {
|
|
||||||
if c.CdnVideo() {
|
|
||||||
return c.ContentUri()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.ApiUri()
|
|
||||||
}
|
|
||||||
|
|
||||||
// StaticUri returns the static content URI.
|
|
||||||
func (c *Config) StaticUri() string {
|
|
||||||
return c.CdnUrl(c.BaseUri(StaticUri))
|
|
||||||
}
|
|
||||||
|
|
||||||
// StaticAssetUri returns the resource URI of the static file asset.
|
|
||||||
func (c *Config) StaticAssetUri(res string) string {
|
|
||||||
return c.StaticUri() + "/" + res
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteUrl returns the public server URL (default is "http://localhost:2342/").
|
|
||||||
func (c *Config) SiteUrl() string {
|
|
||||||
if c.options.SiteUrl == "" {
|
|
||||||
return "http://localhost:2342/"
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimRight(c.options.SiteUrl, "/") + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteHttps checks if the site URL uses HTTPS.
|
|
||||||
func (c *Config) SiteHttps() bool {
|
|
||||||
if c.options.SiteUrl == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.HasPrefix(c.options.SiteUrl, "https://")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteDomain returns the public server domain.
|
|
||||||
func (c *Config) SiteDomain() string {
|
|
||||||
if u, err := url.Parse(c.SiteUrl()); err != nil {
|
|
||||||
return "localhost"
|
|
||||||
} else {
|
|
||||||
return u.Hostname()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteAuthor returns the site author / copyright.
|
|
||||||
func (c *Config) SiteAuthor() string {
|
|
||||||
return c.options.SiteAuthor
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteTitle returns the main site title (default is application name).
|
|
||||||
func (c *Config) SiteTitle() string {
|
|
||||||
if c.options.SiteTitle == "" {
|
|
||||||
return c.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.options.SiteTitle
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteCaption returns a short site caption.
|
|
||||||
func (c *Config) SiteCaption() string {
|
|
||||||
return c.options.SiteCaption
|
|
||||||
}
|
|
||||||
|
|
||||||
// SiteDescription returns a long site description.
|
|
||||||
func (c *Config) SiteDescription() string {
|
|
||||||
return c.options.SiteDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
// SitePreview returns the site preview image URL for sharing.
|
|
||||||
func (c *Config) SitePreview() string {
|
|
||||||
if c.options.SitePreview == "" {
|
|
||||||
return fmt.Sprintf("https://i.photoprism.app/prism?cover=64&style=centered%%20dark&caption=none&title=%s", url.QueryEscape(c.AppName()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(c.options.SitePreview, "http") {
|
|
||||||
return c.SiteUrl() + strings.TrimPrefix(c.options.SitePreview, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.options.SitePreview
|
|
||||||
}
|
|
||||||
|
|
||||||
// LegalInfo returns the legal info text for the page footer.
|
|
||||||
func (c *Config) LegalInfo() string {
|
|
||||||
if s := c.CliGlobalString("imprint"); s != "" {
|
|
||||||
log.Warnf("config: option 'imprint' is deprecated, please use 'legal-info'")
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.options.LegalInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// LegalUrl returns the legal info url.
|
|
||||||
func (c *Config) LegalUrl() string {
|
|
||||||
if s := c.CliGlobalString("imprint-url"); s != "" {
|
|
||||||
log.Warnf("config: option 'imprint-url' is deprecated, please use 'legal-url'")
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.options.LegalUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prod checks if production mode is enabled, hides non-essential log messages.
|
// Prod checks if production mode is enabled, hides non-essential log messages.
|
||||||
func (c *Config) Prod() bool {
|
func (c *Config) Prod() bool {
|
||||||
return c.options.Prod
|
return c.options.Prod
|
||||||
@@ -679,16 +495,6 @@ func (c *Config) ReadOnly() bool {
|
|||||||
return c.options.ReadOnly
|
return c.options.ReadOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
// DetectNSFW checks if NSFW photos should be detected and flagged.
|
|
||||||
func (c *Config) DetectNSFW() bool {
|
|
||||||
return c.options.DetectNSFW
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadNSFW checks if NSFW photos can be uploaded.
|
|
||||||
func (c *Config) UploadNSFW() bool {
|
|
||||||
return c.options.UploadNSFW
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogLevel returns the Logrus log level.
|
// LogLevel returns the Logrus log level.
|
||||||
func (c *Config) LogLevel() logrus.Level {
|
func (c *Config) LogLevel() logrus.Level {
|
||||||
// Normalize string.
|
// Normalize string.
|
||||||
|
|||||||
@@ -16,12 +16,22 @@ func (c *Config) TensorFlowModelPath() string {
|
|||||||
return filepath.Join(c.AssetsPath(), "nasnet")
|
return filepath.Join(c.AssetsPath(), "nasnet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FaceNetModelPath returns the FaceNet model path.
|
||||||
|
func (c *Config) FaceNetModelPath() string {
|
||||||
|
return filepath.Join(c.AssetsPath(), "facenet")
|
||||||
|
}
|
||||||
|
|
||||||
// NSFWModelPath returns the "not safe for work" TensorFlow model path.
|
// NSFWModelPath returns the "not safe for work" TensorFlow model path.
|
||||||
func (c *Config) NSFWModelPath() string {
|
func (c *Config) NSFWModelPath() string {
|
||||||
return filepath.Join(c.AssetsPath(), "nsfw")
|
return filepath.Join(c.AssetsPath(), "nsfw")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FaceNetModelPath returns the FaceNet model path.
|
// DetectNSFW checks if NSFW photos should be detected and flagged.
|
||||||
func (c *Config) FaceNetModelPath() string {
|
func (c *Config) DetectNSFW() bool {
|
||||||
return filepath.Join(c.AssetsPath(), "facenet")
|
return c.options.DetectNSFW
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadNSFW checks if NSFW photos can be uploaded.
|
||||||
|
func (c *Config) UploadNSFW() bool {
|
||||||
|
return c.options.UploadNSFW
|
||||||
}
|
}
|
||||||
47
internal/config/config_ai_test.go
Normal file
47
internal/config/config_ai_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_TensorFlowVersion(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
version := c.TensorFlowVersion()
|
||||||
|
assert.IsType(t, "1.15.0", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_TensorFlowModelPath(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
path := c.TensorFlowModelPath()
|
||||||
|
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/nasnet", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_TensorFlowDisabled(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
version := c.DisableTensorFlow()
|
||||||
|
assert.Equal(t, false, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_NSFWModelPath(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Contains(t, c.NSFWModelPath(), "/assets/nsfw")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_FaceNetModelPath(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Contains(t, c.FaceNetModelPath(), "/assets/facenet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_DetectNSFW(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
result := c.DetectNSFW()
|
||||||
|
assert.Equal(t, true, result)
|
||||||
|
}
|
||||||
66
internal/config/config_cdn.go
Normal file
66
internal/config/config_cdn.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/clean"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CdnUrl returns the optional content delivery network URI without trailing slash.
|
||||||
|
func (c *Config) CdnUrl(res string) string {
|
||||||
|
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimRight(c.options.CdnUrl, "/") + res
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseCdn checks if a Content Deliver Network (CDN) is used to serve static content.
|
||||||
|
func (c *Config) UseCdn() bool {
|
||||||
|
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoCdn checks if there is no Content Deliver Network (CDN) configured to serve static content.
|
||||||
|
func (c *Config) NoCdn() bool {
|
||||||
|
return !c.UseCdn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CdnDomain returns the content delivery network domain name if specified.
|
||||||
|
func (c *Config) CdnDomain() string {
|
||||||
|
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
||||||
|
return ""
|
||||||
|
} else if u, err := url.Parse(c.options.CdnUrl); err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return u.Hostname()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CdnVideo checks if videos should be streamed using the configured CDN.
|
||||||
|
func (c *Config) CdnVideo() bool {
|
||||||
|
if c.options.CdnUrl == "" || c.options.CdnUrl == c.options.SiteUrl {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options.CdnVideo
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORSOrigin returns the value for the Access-Control-Allow-Origin header, if any.
|
||||||
|
func (c *Config) CORSOrigin() string {
|
||||||
|
return clean.Header(c.options.CORSOrigin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORSHeaders returns the value for the Access-Control-Allow-Headers header, if any.
|
||||||
|
func (c *Config) CORSHeaders() string {
|
||||||
|
return clean.Header(c.options.CORSHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORSMethods returns the value for the Access-Control-Allow-Methods header, if any.
|
||||||
|
func (c *Config) CORSMethods() string {
|
||||||
|
return clean.Header(c.options.CORSMethods)
|
||||||
|
}
|
||||||
104
internal/config/config_cdn_test.go
Normal file
104
internal/config/config_cdn_test.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/header"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_CdnUrl(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.options.SiteUrl)
|
||||||
|
assert.Equal(t, "", c.CdnUrl(""))
|
||||||
|
assert.True(t, c.NoCdn())
|
||||||
|
assert.False(t, c.UseCdn())
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, "/", c.CdnUrl("/"))
|
||||||
|
c.options.CdnUrl = "http://foo:2342/foo/"
|
||||||
|
assert.Equal(t, "http://foo:2342/foo", c.CdnUrl(""))
|
||||||
|
assert.Equal(t, "http://foo:2342/foo/", c.CdnUrl("/"))
|
||||||
|
assert.False(t, c.NoCdn())
|
||||||
|
assert.True(t, c.UseCdn())
|
||||||
|
c.options.SiteUrl = c.options.CdnUrl
|
||||||
|
assert.Equal(t, "/", c.CdnUrl("/"))
|
||||||
|
assert.Equal(t, "", c.CdnUrl(""))
|
||||||
|
assert.True(t, c.NoCdn())
|
||||||
|
assert.False(t, c.UseCdn())
|
||||||
|
c.options.SiteUrl = ""
|
||||||
|
assert.False(t, c.NoCdn())
|
||||||
|
assert.True(t, c.UseCdn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_CdnDomain(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.options.SiteUrl)
|
||||||
|
assert.Equal(t, "", c.CdnDomain())
|
||||||
|
c.options.CdnUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, "superhost", c.CdnDomain())
|
||||||
|
c.options.CdnUrl = "https://foo.bar.com:2342/foo/"
|
||||||
|
assert.Equal(t, "foo.bar.com", c.CdnDomain())
|
||||||
|
c.options.SiteUrl = c.options.CdnUrl
|
||||||
|
assert.Equal(t, "", c.CdnDomain())
|
||||||
|
c.options.SiteUrl = ""
|
||||||
|
c.options.CdnUrl = "http:/invalid:2342/foo/"
|
||||||
|
assert.Equal(t, "", c.CdnDomain())
|
||||||
|
c.options.CdnUrl = ""
|
||||||
|
assert.Equal(t, "", c.CdnDomain())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_CdnVideo(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.False(t, c.CdnVideo())
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.False(t, c.CdnVideo())
|
||||||
|
c.options.CdnUrl = "http://foo:2342/foo/"
|
||||||
|
assert.False(t, c.CdnVideo())
|
||||||
|
c.options.CdnVideo = true
|
||||||
|
assert.True(t, c.CdnVideo())
|
||||||
|
c.options.SiteUrl = c.options.CdnUrl
|
||||||
|
assert.False(t, c.CdnVideo())
|
||||||
|
c.options.SiteUrl = ""
|
||||||
|
assert.True(t, c.CdnVideo())
|
||||||
|
c.options.CdnVideo = false
|
||||||
|
assert.False(t, c.CdnVideo())
|
||||||
|
c.options.CdnUrl = ""
|
||||||
|
assert.False(t, c.CdnVideo())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_CORSOrigin(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
c.Options().CORSOrigin = ""
|
||||||
|
assert.Equal(t, "", c.CORSOrigin())
|
||||||
|
c.Options().CORSOrigin = "*"
|
||||||
|
assert.Equal(t, "*", c.CORSOrigin())
|
||||||
|
c.Options().CORSOrigin = "https://developer.mozilla.org"
|
||||||
|
assert.Equal(t, "https://developer.mozilla.org", c.CORSOrigin())
|
||||||
|
c.Options().CORSOrigin = ""
|
||||||
|
assert.Equal(t, "", c.CORSOrigin())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_CORSHeaders(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.CORSHeaders())
|
||||||
|
c.Options().CORSHeaders = header.DefaultAccessControlAllowHeaders
|
||||||
|
assert.Equal(t, header.DefaultAccessControlAllowHeaders, c.CORSHeaders())
|
||||||
|
c.Options().CORSHeaders = ""
|
||||||
|
assert.Equal(t, "", c.CORSHeaders())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_CORSMethods(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.CORSMethods())
|
||||||
|
c.Options().CORSMethods = header.DefaultAccessControlAllowMethods
|
||||||
|
assert.Equal(t, header.DefaultAccessControlAllowMethods, c.CORSMethods())
|
||||||
|
c.Options().CORSMethods = ""
|
||||||
|
assert.Equal(t, "", c.CORSMethods())
|
||||||
|
}
|
||||||
135
internal/config/config_site.go
Normal file
135
internal/config/config_site.go
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseUri returns the site base URI for a given resource.
|
||||||
|
func (c *Config) BaseUri(res string) string {
|
||||||
|
if c.SiteUrl() == "" {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(c.SiteUrl())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimRight(u.EscapedPath(), "/") + res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApiUri returns the api URI.
|
||||||
|
func (c *Config) ApiUri() string {
|
||||||
|
return c.BaseUri(ApiUri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentUri returns the content delivery URI based on the CdnUrl and the ApiUri.
|
||||||
|
func (c *Config) ContentUri() string {
|
||||||
|
return c.CdnUrl(c.ApiUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
// VideoUri returns the video streaming URI.
|
||||||
|
func (c *Config) VideoUri() string {
|
||||||
|
if c.CdnVideo() {
|
||||||
|
return c.ContentUri()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.ApiUri()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticUri returns the static content URI.
|
||||||
|
func (c *Config) StaticUri() string {
|
||||||
|
return c.CdnUrl(c.BaseUri(StaticUri))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticAssetUri returns the resource URI of the static file asset.
|
||||||
|
func (c *Config) StaticAssetUri(res string) string {
|
||||||
|
return c.StaticUri() + "/" + res
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiteUrl returns the public server URL (default is "http://localhost:2342/").
|
||||||
|
func (c *Config) SiteUrl() string {
|
||||||
|
if c.options.SiteUrl == "" {
|
||||||
|
return "http://localhost:2342/"
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimRight(c.options.SiteUrl, "/") + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiteHttps checks if the site URL uses HTTPS.
|
||||||
|
func (c *Config) SiteHttps() bool {
|
||||||
|
if c.options.SiteUrl == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.HasPrefix(c.options.SiteUrl, "https://")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiteDomain returns the public server domain.
|
||||||
|
func (c *Config) SiteDomain() string {
|
||||||
|
if u, err := url.Parse(c.SiteUrl()); err != nil {
|
||||||
|
return "localhost"
|
||||||
|
} else {
|
||||||
|
return u.Hostname()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiteAuthor returns the site author / copyright.
|
||||||
|
func (c *Config) SiteAuthor() string {
|
||||||
|
return c.options.SiteAuthor
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiteTitle returns the main site title (default is application name).
|
||||||
|
func (c *Config) SiteTitle() string {
|
||||||
|
if c.options.SiteTitle == "" {
|
||||||
|
return c.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options.SiteTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiteCaption returns a short site caption.
|
||||||
|
func (c *Config) SiteCaption() string {
|
||||||
|
return c.options.SiteCaption
|
||||||
|
}
|
||||||
|
|
||||||
|
// SiteDescription returns a long site description.
|
||||||
|
func (c *Config) SiteDescription() string {
|
||||||
|
return c.options.SiteDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
// SitePreview returns the site preview image URL for sharing.
|
||||||
|
func (c *Config) SitePreview() string {
|
||||||
|
if c.options.SitePreview == "" {
|
||||||
|
return fmt.Sprintf("https://i.photoprism.app/prism?cover=64&style=centered%%20dark&caption=none&title=%s", url.QueryEscape(c.AppName()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(c.options.SitePreview, "http") {
|
||||||
|
return c.SiteUrl() + strings.TrimPrefix(c.options.SitePreview, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options.SitePreview
|
||||||
|
}
|
||||||
|
|
||||||
|
// LegalInfo returns the legal info text for the page footer.
|
||||||
|
func (c *Config) LegalInfo() string {
|
||||||
|
if s := c.CliGlobalString("imprint"); s != "" {
|
||||||
|
log.Warnf("config: option 'imprint' is deprecated, please use 'legal-info'")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options.LegalInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// LegalUrl returns the legal info url.
|
||||||
|
func (c *Config) LegalUrl() string {
|
||||||
|
if s := c.CliGlobalString("imprint-url"); s != "" {
|
||||||
|
log.Warnf("config: option 'imprint-url' is deprecated, please use 'legal-url'")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options.LegalUrl
|
||||||
|
}
|
||||||
143
internal/config/config_site_test.go
Normal file
143
internal/config/config_site_test.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_BaseUri(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.BaseUri(""))
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, "", c.BaseUri(""))
|
||||||
|
c.options.SiteUrl = "http://foo:2342/foo bar/"
|
||||||
|
assert.Equal(t, "/foo%20bar", c.BaseUri(""))
|
||||||
|
assert.Equal(t, "/foo%20bar/baz", c.BaseUri("/baz"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_StaticUri(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "/static", c.StaticUri())
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, "/static", c.StaticUri())
|
||||||
|
c.options.SiteUrl = "http://foo:2342/foo/"
|
||||||
|
assert.Equal(t, "/foo/static", c.StaticUri())
|
||||||
|
c.options.CdnUrl = "http://foo:2342/bar"
|
||||||
|
assert.Equal(t, "http://foo:2342/bar/foo"+StaticUri, c.StaticUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_ApiUri(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, ApiUri, c.ApiUri())
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, ApiUri, c.ApiUri())
|
||||||
|
c.options.SiteUrl = "http://foo:2342/foo/"
|
||||||
|
assert.Equal(t, "/foo"+ApiUri, c.ApiUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_ContentUri(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, ApiUri, c.ContentUri())
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, ApiUri, c.ContentUri())
|
||||||
|
c.options.CdnUrl = "http://foo:2342//"
|
||||||
|
assert.Equal(t, "http://foo:2342"+ApiUri, c.ContentUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_VideoUri(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, ApiUri, c.VideoUri())
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, ApiUri, c.VideoUri())
|
||||||
|
c.options.CdnUrl = "http://foo:2342//"
|
||||||
|
c.options.CdnVideo = true
|
||||||
|
assert.Equal(t, "http://foo:2342"+ApiUri, c.VideoUri())
|
||||||
|
c.options.CdnVideo = false
|
||||||
|
assert.Equal(t, ApiUri, c.VideoUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SiteUrl(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "http://localhost:2342/", c.SiteUrl())
|
||||||
|
c.options.SiteUrl = "http://superhost:2342/"
|
||||||
|
assert.Equal(t, "http://superhost:2342/", c.SiteUrl())
|
||||||
|
c.options.SiteUrl = "http://superhost"
|
||||||
|
assert.Equal(t, "http://superhost/", c.SiteUrl())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SiteHttps(t *testing.T) {
|
||||||
|
t.Run("Default", func(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
assert.False(t, c.SiteHttps())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SiteDomain(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "localhost", c.SiteDomain())
|
||||||
|
c.options.SiteUrl = "https://foo.bar.com:2342/"
|
||||||
|
assert.Equal(t, "foo.bar.com", c.SiteDomain())
|
||||||
|
c.options.SiteUrl = ""
|
||||||
|
assert.Equal(t, "localhost", c.SiteDomain())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SitePreview(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
assert.Equal(t, "https://i.photoprism.app/prism?cover=64&style=centered%20dark&caption=none&title=PhotoPrism", c.SitePreview())
|
||||||
|
c.options.SitePreview = "http://preview.jpg"
|
||||||
|
assert.Equal(t, "http://preview.jpg", c.SitePreview())
|
||||||
|
c.options.SitePreview = "preview123.jpg"
|
||||||
|
assert.Equal(t, "http://localhost:2342/preview123.jpg", c.SitePreview())
|
||||||
|
c.options.SitePreview = "foo/preview123.jpg"
|
||||||
|
assert.Equal(t, "http://localhost:2342/foo/preview123.jpg", c.SitePreview())
|
||||||
|
c.options.SitePreview = "/foo/preview123.jpg"
|
||||||
|
assert.Equal(t, "http://localhost:2342/foo/preview123.jpg", c.SitePreview())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SiteAuthor(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.SiteAuthor())
|
||||||
|
c.options.SiteAuthor = "@Jens.Mander"
|
||||||
|
assert.Equal(t, "@Jens.Mander", c.SiteAuthor())
|
||||||
|
c.options.SiteAuthor = ""
|
||||||
|
assert.Equal(t, "", c.SiteAuthor())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SiteTitle(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "PhotoPrism", c.SiteTitle())
|
||||||
|
c.options.SiteTitle = "Cats"
|
||||||
|
assert.Equal(t, "Cats", c.SiteTitle())
|
||||||
|
c.options.SiteTitle = "PhotoPrism"
|
||||||
|
assert.Equal(t, "PhotoPrism", c.SiteTitle())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SiteCaption(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.SiteCaption())
|
||||||
|
c.options.SiteCaption = "PhotoPrism App"
|
||||||
|
assert.Equal(t, "PhotoPrism App", c.SiteCaption())
|
||||||
|
c.options.SiteCaption = ""
|
||||||
|
assert.Equal(t, "", c.SiteCaption())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_SiteDescription(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.SiteDescription())
|
||||||
|
c.options.SiteDescription = "My Description!"
|
||||||
|
assert.Equal(t, "My Description!", c.SiteDescription())
|
||||||
|
c.options.SiteDescription = ""
|
||||||
|
assert.Equal(t, "", c.SiteDescription())
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/pkg/fs"
|
"github.com/photoprism/photoprism/pkg/fs"
|
||||||
"github.com/photoprism/photoprism/pkg/header"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@@ -97,20 +96,6 @@ func TestConfig_VersionChecksum(t *testing.T) {
|
|||||||
assert.Equal(t, uint32(0x2e5b4b86), c.VersionChecksum())
|
assert.Equal(t, uint32(0x2e5b4b86), c.VersionChecksum())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_TensorFlowVersion(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
version := c.TensorFlowVersion()
|
|
||||||
assert.IsType(t, "1.15.0", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_TensorFlowDisabled(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
version := c.DisableTensorFlow()
|
|
||||||
assert.Equal(t, false, version)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_Copyright(t *testing.T) {
|
func TestConfig_Copyright(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
@@ -207,13 +192,6 @@ func TestConfig_CustomAssetsPath(t *testing.T) {
|
|||||||
assert.Equal(t, "", c.CustomAssetsPath())
|
assert.Equal(t, "", c.CustomAssetsPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_DetectNSFW(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
result := c.DetectNSFW()
|
|
||||||
assert.Equal(t, true, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_AdminUser(t *testing.T) {
|
func TestConfig_AdminUser(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
@@ -230,18 +208,6 @@ func TestConfig_AdminPassword(t *testing.T) {
|
|||||||
assert.Equal(t, "photoprism", result)
|
assert.Equal(t, "photoprism", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_NSFWModelPath(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Contains(t, c.NSFWModelPath(), "/assets/nsfw")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_FaceNetModelPath(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Contains(t, c.FaceNetModelPath(), "/assets/facenet")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_ExamplesPath(t *testing.T) {
|
func TestConfig_ExamplesPath(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
@@ -249,13 +215,6 @@ func TestConfig_ExamplesPath(t *testing.T) {
|
|||||||
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples", path)
|
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/examples", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_TensorFlowModelPath(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
path := c.TensorFlowModelPath()
|
|
||||||
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets/nasnet", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_TemplatesPath(t *testing.T) {
|
func TestConfig_TemplatesPath(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
@@ -383,198 +342,6 @@ func TestConfig_ResolutionLimit(t *testing.T) {
|
|||||||
assert.Equal(t, -1, c.ResolutionLimit())
|
assert.Equal(t, -1, c.ResolutionLimit())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_BaseUri(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "", c.BaseUri(""))
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, "", c.BaseUri(""))
|
|
||||||
c.options.SiteUrl = "http://foo:2342/foo bar/"
|
|
||||||
assert.Equal(t, "/foo%20bar", c.BaseUri(""))
|
|
||||||
assert.Equal(t, "/foo%20bar/baz", c.BaseUri("/baz"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_StaticUri(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "/static", c.StaticUri())
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, "/static", c.StaticUri())
|
|
||||||
c.options.SiteUrl = "http://foo:2342/foo/"
|
|
||||||
assert.Equal(t, "/foo/static", c.StaticUri())
|
|
||||||
c.options.CdnUrl = "http://foo:2342/bar"
|
|
||||||
assert.Equal(t, "http://foo:2342/bar/foo"+StaticUri, c.StaticUri())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_ApiUri(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, ApiUri, c.ApiUri())
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, ApiUri, c.ApiUri())
|
|
||||||
c.options.SiteUrl = "http://foo:2342/foo/"
|
|
||||||
assert.Equal(t, "/foo"+ApiUri, c.ApiUri())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_CdnUrl(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "", c.options.SiteUrl)
|
|
||||||
assert.Equal(t, "", c.CdnUrl(""))
|
|
||||||
assert.True(t, c.NoCdn())
|
|
||||||
assert.False(t, c.UseCdn())
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, "/", c.CdnUrl("/"))
|
|
||||||
c.options.CdnUrl = "http://foo:2342/foo/"
|
|
||||||
assert.Equal(t, "http://foo:2342/foo", c.CdnUrl(""))
|
|
||||||
assert.Equal(t, "http://foo:2342/foo/", c.CdnUrl("/"))
|
|
||||||
assert.False(t, c.NoCdn())
|
|
||||||
assert.True(t, c.UseCdn())
|
|
||||||
c.options.SiteUrl = c.options.CdnUrl
|
|
||||||
assert.Equal(t, "/", c.CdnUrl("/"))
|
|
||||||
assert.Equal(t, "", c.CdnUrl(""))
|
|
||||||
assert.True(t, c.NoCdn())
|
|
||||||
assert.False(t, c.UseCdn())
|
|
||||||
c.options.SiteUrl = ""
|
|
||||||
assert.False(t, c.NoCdn())
|
|
||||||
assert.True(t, c.UseCdn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_CdnDomain(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "", c.options.SiteUrl)
|
|
||||||
assert.Equal(t, "", c.CdnDomain())
|
|
||||||
c.options.CdnUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, "superhost", c.CdnDomain())
|
|
||||||
c.options.CdnUrl = "https://foo.bar.com:2342/foo/"
|
|
||||||
assert.Equal(t, "foo.bar.com", c.CdnDomain())
|
|
||||||
c.options.SiteUrl = c.options.CdnUrl
|
|
||||||
assert.Equal(t, "", c.CdnDomain())
|
|
||||||
c.options.SiteUrl = ""
|
|
||||||
c.options.CdnUrl = "http:/invalid:2342/foo/"
|
|
||||||
assert.Equal(t, "", c.CdnDomain())
|
|
||||||
c.options.CdnUrl = ""
|
|
||||||
assert.Equal(t, "", c.CdnDomain())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_CdnVideo(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.False(t, c.CdnVideo())
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.False(t, c.CdnVideo())
|
|
||||||
c.options.CdnUrl = "http://foo:2342/foo/"
|
|
||||||
assert.False(t, c.CdnVideo())
|
|
||||||
c.options.CdnVideo = true
|
|
||||||
assert.True(t, c.CdnVideo())
|
|
||||||
c.options.SiteUrl = c.options.CdnUrl
|
|
||||||
assert.False(t, c.CdnVideo())
|
|
||||||
c.options.SiteUrl = ""
|
|
||||||
assert.True(t, c.CdnVideo())
|
|
||||||
c.options.CdnVideo = false
|
|
||||||
assert.False(t, c.CdnVideo())
|
|
||||||
c.options.CdnUrl = ""
|
|
||||||
assert.False(t, c.CdnVideo())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_CORSOrigin(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
c.Options().CORSOrigin = ""
|
|
||||||
assert.Equal(t, "", c.CORSOrigin())
|
|
||||||
c.Options().CORSOrigin = "*"
|
|
||||||
assert.Equal(t, "*", c.CORSOrigin())
|
|
||||||
c.Options().CORSOrigin = "https://developer.mozilla.org"
|
|
||||||
assert.Equal(t, "https://developer.mozilla.org", c.CORSOrigin())
|
|
||||||
c.Options().CORSOrigin = ""
|
|
||||||
assert.Equal(t, "", c.CORSOrigin())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_CORSHeaders(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "", c.CORSHeaders())
|
|
||||||
c.Options().CORSHeaders = header.DefaultAccessControlAllowHeaders
|
|
||||||
assert.Equal(t, header.DefaultAccessControlAllowHeaders, c.CORSHeaders())
|
|
||||||
c.Options().CORSHeaders = ""
|
|
||||||
assert.Equal(t, "", c.CORSHeaders())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_CORSMethods(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "", c.CORSMethods())
|
|
||||||
c.Options().CORSMethods = header.DefaultAccessControlAllowMethods
|
|
||||||
assert.Equal(t, header.DefaultAccessControlAllowMethods, c.CORSMethods())
|
|
||||||
c.Options().CORSMethods = ""
|
|
||||||
assert.Equal(t, "", c.CORSMethods())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_ContentUri(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, ApiUri, c.ContentUri())
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, ApiUri, c.ContentUri())
|
|
||||||
c.options.CdnUrl = "http://foo:2342//"
|
|
||||||
assert.Equal(t, "http://foo:2342"+ApiUri, c.ContentUri())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_VideoUri(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, ApiUri, c.VideoUri())
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, ApiUri, c.VideoUri())
|
|
||||||
c.options.CdnUrl = "http://foo:2342//"
|
|
||||||
c.options.CdnVideo = true
|
|
||||||
assert.Equal(t, "http://foo:2342"+ApiUri, c.VideoUri())
|
|
||||||
c.options.CdnVideo = false
|
|
||||||
assert.Equal(t, ApiUri, c.VideoUri())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_SiteUrl(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "http://localhost:2342/", c.SiteUrl())
|
|
||||||
c.options.SiteUrl = "http://superhost:2342/"
|
|
||||||
assert.Equal(t, "http://superhost:2342/", c.SiteUrl())
|
|
||||||
c.options.SiteUrl = "http://superhost"
|
|
||||||
assert.Equal(t, "http://superhost/", c.SiteUrl())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_SiteDomain(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "localhost", c.SiteDomain())
|
|
||||||
c.options.SiteUrl = "https://foo.bar.com:2342/"
|
|
||||||
assert.Equal(t, "foo.bar.com", c.SiteDomain())
|
|
||||||
c.options.SiteUrl = ""
|
|
||||||
assert.Equal(t, "localhost", c.SiteDomain())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_SitePreview(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
assert.Equal(t, "https://i.photoprism.app/prism?cover=64&style=centered%20dark&caption=none&title=PhotoPrism", c.SitePreview())
|
|
||||||
c.options.SitePreview = "http://preview.jpg"
|
|
||||||
assert.Equal(t, "http://preview.jpg", c.SitePreview())
|
|
||||||
c.options.SitePreview = "preview123.jpg"
|
|
||||||
assert.Equal(t, "http://localhost:2342/preview123.jpg", c.SitePreview())
|
|
||||||
c.options.SitePreview = "foo/preview123.jpg"
|
|
||||||
assert.Equal(t, "http://localhost:2342/foo/preview123.jpg", c.SitePreview())
|
|
||||||
c.options.SitePreview = "/foo/preview123.jpg"
|
|
||||||
assert.Equal(t, "http://localhost:2342/foo/preview123.jpg", c.SitePreview())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_SiteTitle(t *testing.T) {
|
|
||||||
c := NewConfig(CliTestContext())
|
|
||||||
|
|
||||||
assert.Equal(t, "PhotoPrism", c.SiteTitle())
|
|
||||||
c.options.SiteTitle = "Cats"
|
|
||||||
assert.Equal(t, "Cats", c.SiteTitle())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_Serial(t *testing.T) {
|
func TestConfig_Serial(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ func NewOptions(ctx *cli.Context) *Options {
|
|||||||
c.BackupDatabase = true
|
c.BackupDatabase = true
|
||||||
c.BackupAlbums = true
|
c.BackupAlbums = true
|
||||||
|
|
||||||
// Load defaults from YAML file?
|
// Initialize options with the values from the "defaults.yml" file, if it exists.
|
||||||
if defaultsYaml := ctx.GlobalString("defaults-yaml"); defaultsYaml == "" {
|
if defaultsYaml := ctx.GlobalString("defaults-yaml"); defaultsYaml == "" {
|
||||||
log.Tracef("config: defaults file was not specified")
|
log.Tracef("config: defaults file was not specified")
|
||||||
} else if c.DefaultsYaml = fs.Abs(defaultsYaml); !fs.FileExists(c.DefaultsYaml) {
|
} else if c.DefaultsYaml = fs.Abs(defaultsYaml); !fs.FileExists(c.DefaultsYaml) {
|
||||||
@@ -243,6 +243,7 @@ func NewOptions(ctx *cli.Context) *Options {
|
|||||||
log.Warnf("config: failed loading defaults from %s (%s)", clean.Log(c.DefaultsYaml), err)
|
log.Warnf("config: failed loading defaults from %s (%s)", clean.Log(c.DefaultsYaml), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply options specified with environment variables and command-line flags.
|
||||||
if err := c.ApplyCliContext(ctx); err != nil {
|
if err := c.ApplyCliContext(ctx); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/photoprism/photoprism/pkg/i18n"
|
"github.com/photoprism/photoprism/pkg/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// initSettings initializes user settings from a config file.
|
// initSettings initializes the customization settings from the "settings.yml" file.
|
||||||
func (c *Config) initSettings() {
|
func (c *Config) initSettings() {
|
||||||
if c.settings != nil {
|
if c.settings != nil {
|
||||||
return
|
return
|
||||||
@@ -43,6 +43,7 @@ func (c *Config) initSettings() {
|
|||||||
|
|
||||||
// Settings returns the global app settings.
|
// Settings returns the global app settings.
|
||||||
func (c *Config) Settings() *customize.Settings {
|
func (c *Config) Settings() *customize.Settings {
|
||||||
|
// Load settings from the "settings.yml" config file.
|
||||||
c.initSettings()
|
c.initSettings()
|
||||||
|
|
||||||
if c.DisablePlaces() {
|
if c.DisablePlaces() {
|
||||||
|
|||||||
@@ -202,6 +202,35 @@ var UserFixtures = UserMap{
|
|||||||
BirthYear: 2001,
|
BirthYear: 2001,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"guest": {
|
||||||
|
ID: 10000025,
|
||||||
|
UserUID: "usg73p55zwgr1gbq",
|
||||||
|
UserName: "guest",
|
||||||
|
DisplayName: "Guest User",
|
||||||
|
UserEmail: "guest@example.com",
|
||||||
|
UserRole: acl.RoleGuest.String(),
|
||||||
|
AuthProvider: authn.ProviderOIDC.String(),
|
||||||
|
AuthMethod: authn.MethodDefault.String(),
|
||||||
|
SuperAdmin: false,
|
||||||
|
CanLogin: true,
|
||||||
|
WebDAV: false,
|
||||||
|
CanInvite: false,
|
||||||
|
InviteToken: "",
|
||||||
|
UserSettings: &UserSettings{
|
||||||
|
UITheme: "default",
|
||||||
|
MapsStyle: "default",
|
||||||
|
MapsAnimate: 6250,
|
||||||
|
UILanguage: "en",
|
||||||
|
UITimeZone: "UTC",
|
||||||
|
},
|
||||||
|
UserDetails: &UserDetails{
|
||||||
|
NickName: "Gustav",
|
||||||
|
UserGender: GenderMale,
|
||||||
|
BirthDay: 23,
|
||||||
|
BirthMonth: 1,
|
||||||
|
BirthYear: 1999,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUserFixtures creates the user fixtures specified above
|
// CreateUserFixtures creates the user fixtures specified above
|
||||||
|
|||||||
@@ -11,12 +11,37 @@ import (
|
|||||||
// RegisteredUsers finds all registered users.
|
// RegisteredUsers finds all registered users.
|
||||||
func RegisteredUsers() (result entity.Users) {
|
func RegisteredUsers() (result entity.Users) {
|
||||||
if err := Db().Where("id > 0").Find(&result).Error; err != nil {
|
if err := Db().Where("id > 0").Find(&result).Error; err != nil {
|
||||||
log.Errorf("users: %s", err)
|
log.Errorf("users: %s (find)", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountUsers returns the number of users based on the specified filter options.
|
||||||
|
func CountUsers(registered, active bool, roles, excludeRoles []string) (count int) {
|
||||||
|
stmt := Db().Model(entity.Users{})
|
||||||
|
|
||||||
|
if registered {
|
||||||
|
stmt = stmt.Where("id > 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if active {
|
||||||
|
stmt = stmt.Where("(can_login > 0 OR webdav > 0) AND user_name <> ''")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(roles) > 0 {
|
||||||
|
stmt = stmt.Where("user_role IN (?)", roles)
|
||||||
|
} else if len(excludeRoles) > 0 {
|
||||||
|
stmt = stmt.Where("user_role NOT IN (?)", excludeRoles)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stmt.Count(&count).Error; err != nil {
|
||||||
|
log.Errorf("users: %s (count)", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
// Users finds users and returns them.
|
// Users finds users and returns them.
|
||||||
func Users(limit, offset int, sortOrder, search string) (result entity.Users, err error) {
|
func Users(limit, offset int, sortOrder, search string) (result entity.Users, err error) {
|
||||||
result = entity.Users{}
|
result = entity.Users{}
|
||||||
|
|||||||
@@ -19,6 +19,30 @@ func TestRegisteredUsers(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCountUsers(t *testing.T) {
|
||||||
|
t.Run("All", func(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, CountUsers(false, false, nil, nil), 10)
|
||||||
|
})
|
||||||
|
t.Run("Registered", func(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, CountUsers(true, false, nil, nil), 8)
|
||||||
|
})
|
||||||
|
t.Run("Active", func(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, CountUsers(false, true, nil, nil), 8)
|
||||||
|
})
|
||||||
|
t.Run("RegisteredActive", func(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, CountUsers(true, true, nil, nil), 8)
|
||||||
|
})
|
||||||
|
t.Run("Admins", func(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, CountUsers(true, true, []string{"admin"}, nil), 6)
|
||||||
|
})
|
||||||
|
t.Run("NoAdmins", func(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, CountUsers(true, true, []string{}, []string{"admin"}), 2)
|
||||||
|
})
|
||||||
|
t.Run("Guests", func(t *testing.T) {
|
||||||
|
assert.LessOrEqual(t, CountUsers(true, true, []string{"guest"}, nil), 2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestUsers(t *testing.T) {
|
func TestUsers(t *testing.T) {
|
||||||
t.Run("Default", func(t *testing.T) {
|
t.Run("Default", func(t *testing.T) {
|
||||||
if results, err := Users(0, 0, "", ""); err != nil {
|
if results, err := Users(0, 0, "", ""); err != nil {
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ import (
|
|||||||
"github.com/photoprism/photoprism/pkg/txt"
|
"github.com/photoprism/photoprism/pkg/txt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FeedbackURL is the service endpoint for submitting user feedback.
|
||||||
var FeedbackURL = ServiceURL + "/%s/feedback"
|
var FeedbackURL = ServiceURL + "/%s/feedback"
|
||||||
|
|
||||||
|
// Feedback represents user feedback submitted through the user interface.
|
||||||
type Feedback struct {
|
type Feedback struct {
|
||||||
Category string `json:"Category"`
|
Category string `json:"Category"`
|
||||||
Subject string `json:"Subject"`
|
Subject string `json:"Subject"`
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServiceURL specifies the service endpoint URL.
|
||||||
var ServiceURL = "https://my.photoprism.app/v1/hello"
|
var ServiceURL = "https://my.photoprism.app/v1/hello"
|
||||||
|
|
||||||
// Request represents basic environment specs for debugging.
|
// Request represents basic environment specs for debugging.
|
||||||
@@ -15,10 +16,16 @@ type Request struct {
|
|||||||
ClientArch string `json:"ClientArch"`
|
ClientArch string `json:"ClientArch"`
|
||||||
ClientCPU int `json:"ClientCPU"`
|
ClientCPU int `json:"ClientCPU"`
|
||||||
ClientEnv string `json:"ClientEnv"`
|
ClientEnv string `json:"ClientEnv"`
|
||||||
|
ClientOpt string `json:"ClientOpt"`
|
||||||
PartnerID string `json:"PartnerID"`
|
PartnerID string `json:"PartnerID"`
|
||||||
ApiToken string `json:"ApiToken"`
|
ApiToken string `json:"ApiToken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientOpt returns a custom request option.
|
||||||
|
var ClientOpt = func() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// NewRequest creates a new backend key request instance.
|
// NewRequest creates a new backend key request instance.
|
||||||
func NewRequest(version, serial, env, partnerId, token string) *Request {
|
func NewRequest(version, serial, env, partnerId, token string) *Request {
|
||||||
return &Request{
|
return &Request{
|
||||||
@@ -28,6 +35,7 @@ func NewRequest(version, serial, env, partnerId, token string) *Request {
|
|||||||
ClientArch: runtime.GOARCH,
|
ClientArch: runtime.GOARCH,
|
||||||
ClientCPU: runtime.NumCPU(),
|
ClientCPU: runtime.NumCPU(),
|
||||||
ClientEnv: env,
|
ClientEnv: env,
|
||||||
|
ClientOpt: ClientOpt(),
|
||||||
PartnerID: partnerId,
|
PartnerID: partnerId,
|
||||||
ApiToken: token,
|
ApiToken: token,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SetUserImageURL sets a new user avatar URL.
|
// SetUserImageURL sets a new user avatar URL.
|
||||||
func SetUserImageURL(m *entity.User, imageUrl, imageSrc string) error {
|
func SetUserImageURL(m *entity.User, imageUrl, imageSrc, thumbPath string) error {
|
||||||
if imageUrl == "" {
|
if imageUrl == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ func SetUserImageURL(m *entity.User, imageUrl, imageSrc string) error {
|
|||||||
return fmt.Errorf("failed to rename avatar image (%w)", err)
|
return fmt.Errorf("failed to rename avatar image (%w)", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = SetUserImage(m, imageName, imageSrc); err != nil {
|
if err = SetUserImage(m, imageName, imageSrc, thumbPath); err != nil {
|
||||||
return fmt.Errorf("failed to set avatar image (%w)", err)
|
return fmt.Errorf("failed to set avatar image (%w)", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,14 +64,16 @@ func SetUserImageURL(m *entity.User, imageUrl, imageSrc string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetUserImage sets a new user avatar image.
|
// SetUserImage sets a new user avatar image.
|
||||||
func SetUserImage(m *entity.User, imageName, imageSrc string) error {
|
func SetUserImage(m *entity.User, imageName, imageSrc, thumbPath string) error {
|
||||||
var conf *config.Config
|
var conf *config.Config
|
||||||
|
|
||||||
if conf = get.Config(); conf == nil {
|
if conf = get.Config(); conf == nil {
|
||||||
return fmt.Errorf("config required")
|
return fmt.Errorf("config required")
|
||||||
}
|
}
|
||||||
|
|
||||||
thumbPath := conf.ThumbCachePath()
|
if thumbPath == "" {
|
||||||
|
thumbPath = conf.ThumbCachePath()
|
||||||
|
}
|
||||||
|
|
||||||
if mediaFile, mediaErr := photoprism.NewMediaFile(imageName); mediaErr != nil {
|
if mediaFile, mediaErr := photoprism.NewMediaFile(imageName); mediaErr != nil {
|
||||||
return mediaErr
|
return mediaErr
|
||||||
|
|||||||
@@ -10,31 +10,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestSetUserAvatarURL(t *testing.T) {
|
func TestSetUserAvatarURL(t *testing.T) {
|
||||||
|
thumbPath := fs.Abs("testdata/cache")
|
||||||
|
|
||||||
t.Run("PNG", func(t *testing.T) {
|
t.Run("PNG", func(t *testing.T) {
|
||||||
admin := entity.UserFixtures.Get("alice")
|
admin := entity.UserFixtures.Get("alice")
|
||||||
imageUrl := "https://dl.photoprism.app/icons/logo/256.png"
|
imageUrl := "https://dl.photoprism.app/icons/logo/256.png"
|
||||||
err := SetUserImageURL(&admin, imageUrl, entity.SrcAuto)
|
err := SetUserImageURL(&admin, imageUrl, entity.SrcAuto, thumbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
t.Run("JPEG", func(t *testing.T) {
|
t.Run("JPEG", func(t *testing.T) {
|
||||||
admin := entity.UserFixtures.Get("bob")
|
admin := entity.UserFixtures.Get("bob")
|
||||||
imageUrl := "https://dl.photoprism.app/img/team/avatar.jpg"
|
imageUrl := "https://dl.photoprism.app/img/team/avatar.jpg"
|
||||||
err := SetUserImageURL(&admin, imageUrl, entity.SrcOIDC)
|
err := SetUserImageURL(&admin, imageUrl, entity.SrcOIDC, thumbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
t.Run("NotFound", func(t *testing.T) {
|
t.Run("NotFound", func(t *testing.T) {
|
||||||
admin := entity.UserFixtures.Get("alice")
|
admin := entity.UserFixtures.Get("alice")
|
||||||
imageUrl := "https://dl.photoprism.app/img/team/avatar-invalid.jpg"
|
imageUrl := "https://dl.photoprism.app/img/team/avatar-invalid.jpg"
|
||||||
err := SetUserImageURL(&admin, imageUrl, entity.SrcAuto)
|
err := SetUserImageURL(&admin, imageUrl, entity.SrcAuto, thumbPath)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetUserAvatarImage(t *testing.T) {
|
func TestSetUserAvatarImage(t *testing.T) {
|
||||||
|
thumbPath := fs.Abs("testdata/cache")
|
||||||
|
|
||||||
t.Run("Admin", func(t *testing.T) {
|
t.Run("Admin", func(t *testing.T) {
|
||||||
admin := entity.UserFixtures.Get("friend")
|
admin := entity.UserFixtures.Get("friend")
|
||||||
fileName := fs.Abs("testdata/avatar.png")
|
fileName := fs.Abs("testdata/avatar.png")
|
||||||
err := SetUserImage(&admin, fileName, entity.SrcAuto)
|
err := SetUserImage(&admin, fileName, entity.SrcAuto, thumbPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user