2019-05-06 23:18:10 +02:00
|
|
|
package config
|
2019-05-03 18:57:28 +02:00
|
|
|
|
|
|
|
import (
|
2019-06-03 22:58:15 +02:00
|
|
|
"context"
|
2020-09-06 14:18:40 +02:00
|
|
|
"fmt"
|
2020-01-02 00:03:07 +01:00
|
|
|
"runtime"
|
2020-06-26 14:26:36 +02:00
|
|
|
"strings"
|
2020-01-31 15:29:06 +01:00
|
|
|
"sync"
|
2019-05-03 18:57:28 +02:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/mysql"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
2019-12-02 00:30:58 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/event"
|
2020-01-19 12:50:44 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/mutex"
|
2020-10-03 13:50:30 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/pro"
|
|
|
|
"github.com/photoprism/photoprism/internal/pro/places"
|
2020-01-06 14:32:15 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/thumb"
|
2020-05-27 19:38:40 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/rnd"
|
2019-12-02 00:30:58 +01:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-05-03 18:57:28 +02:00
|
|
|
"github.com/urfave/cli"
|
|
|
|
)
|
|
|
|
|
2019-12-02 00:30:58 +01:00
|
|
|
var log = event.Log
|
2020-04-13 18:08:21 +02:00
|
|
|
var once sync.Once
|
2019-12-02 00:30:58 +01:00
|
|
|
|
2020-02-21 01:14:45 +01:00
|
|
|
// Config holds database, cache and all parameters of photoprism
|
2019-05-06 23:18:10 +02:00
|
|
|
type Config struct {
|
2020-10-03 13:50:30 +02:00
|
|
|
once sync.Once
|
|
|
|
db *gorm.DB
|
|
|
|
params *Params
|
|
|
|
settings *Settings
|
|
|
|
pro *pro.Config
|
|
|
|
token string
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
|
2020-01-06 14:32:15 +01:00
|
|
|
func init() {
|
2020-07-13 15:23:54 +02:00
|
|
|
// Init public thumb sizes for use in client apps.
|
|
|
|
for i := len(thumb.DefaultTypes) - 1; i >= 0; i-- {
|
|
|
|
size := thumb.DefaultTypes[i]
|
|
|
|
t := thumb.Types[size]
|
|
|
|
|
2020-01-06 14:32:15 +01:00
|
|
|
if t.Public {
|
2020-07-13 17:25:27 +02:00
|
|
|
Thumbs = append(Thumbs, Thumb{Size: size, Use: t.Use, Width: t.Width, Height: t.Height})
|
2020-01-06 14:32:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-04 09:11:33 +02:00
|
|
|
func initLogger(debug bool) {
|
2020-01-31 15:29:06 +01:00
|
|
|
once.Do(func() {
|
|
|
|
log.SetFormatter(&logrus.TextFormatter{
|
|
|
|
DisableColors: false,
|
|
|
|
FullTimestamp: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
if debug {
|
|
|
|
log.SetLevel(logrus.DebugLevel)
|
|
|
|
} else {
|
|
|
|
log.SetLevel(logrus.InfoLevel)
|
|
|
|
}
|
2019-05-03 18:57:28 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-02-21 01:14:45 +01:00
|
|
|
// NewConfig initialises a new configuration file
|
2019-05-06 23:18:10 +02:00
|
|
|
func NewConfig(ctx *cli.Context) *Config {
|
2019-05-04 05:25:00 +02:00
|
|
|
initLogger(ctx.GlobalBool("debug"))
|
2019-05-03 18:57:28 +02:00
|
|
|
|
2019-05-14 16:04:17 +02:00
|
|
|
c := &Config{
|
2020-04-13 18:08:21 +02:00
|
|
|
params: NewParams(ctx),
|
2020-05-27 19:38:40 +02:00
|
|
|
token: rnd.Token(8),
|
2019-05-14 16:04:17 +02:00
|
|
|
}
|
2019-05-03 18:57:28 +02:00
|
|
|
|
2020-04-13 18:08:21 +02:00
|
|
|
c.initSettings()
|
2020-10-03 13:50:30 +02:00
|
|
|
c.initPro()
|
2020-04-13 18:08:21 +02:00
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// Propagate updates config values in other packages as needed.
|
|
|
|
func (c *Config) Propagate() {
|
2019-05-03 18:57:28 +02:00
|
|
|
log.SetLevel(c.LogLevel())
|
|
|
|
|
2020-05-05 17:04:13 +02:00
|
|
|
thumb.Size = c.ThumbSize()
|
2020-07-18 17:33:02 +02:00
|
|
|
thumb.SizeUncached = c.ThumbSizeUncached()
|
2020-05-05 17:17:19 +02:00
|
|
|
thumb.Filter = c.ThumbFilter()
|
2020-05-05 15:42:54 +02:00
|
|
|
thumb.JpegQuality = c.JpegQuality()
|
2020-09-06 14:18:40 +02:00
|
|
|
places.UserAgent = c.UserAgent()
|
2020-01-06 14:32:15 +01:00
|
|
|
|
2020-04-13 18:08:21 +02:00
|
|
|
c.Settings().Propagate()
|
2020-10-03 13:50:30 +02:00
|
|
|
c.Pro().Propagate()
|
2020-04-13 18:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Init initialises the database connection and dependencies.
|
2020-09-06 14:18:40 +02:00
|
|
|
func (c *Config) Init(_ context.Context) error {
|
2020-04-13 18:08:21 +02:00
|
|
|
c.Propagate()
|
2020-07-13 10:41:45 +02:00
|
|
|
return c.connectDb()
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// Name returns the application name ("PhotoPrism").
|
2019-05-06 23:18:10 +02:00
|
|
|
func (c *Config) Name() string {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.Name
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// Version returns the application version.
|
|
|
|
func (c *Config) Version() string {
|
|
|
|
return c.params.Version
|
|
|
|
}
|
2019-12-11 14:10:20 +01:00
|
|
|
|
2020-09-06 14:18:40 +02:00
|
|
|
// UserAgent returns a HTTP user agent string based on app name & version.
|
|
|
|
func (c *Config) UserAgent() string {
|
|
|
|
return fmt.Sprintf("%s/%s", c.Name(), c.Version())
|
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// Copyright returns the application copyright.
|
|
|
|
func (c *Config) Copyright() string {
|
|
|
|
return c.params.Copyright
|
2019-12-11 14:10:20 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// SiteUrl returns the public server URL (default is "http://localhost:2342/").
|
|
|
|
func (c *Config) SiteUrl() string {
|
|
|
|
if c.params.SiteUrl == "" {
|
|
|
|
return "http://localhost:2342/"
|
2019-12-11 14:10:20 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
return c.params.SiteUrl
|
2019-12-11 14:10:20 +01:00
|
|
|
}
|
|
|
|
|
2020-06-26 14:26:36 +02:00
|
|
|
// SitePreview returns the site preview image URL for sharing.
|
|
|
|
func (c *Config) SitePreview() string {
|
|
|
|
if c.params.SitePreview == "" {
|
|
|
|
return c.SiteUrl() + "static/img/preview.jpg"
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.HasPrefix(c.params.SitePreview, "http") {
|
|
|
|
return c.SiteUrl() + c.params.SitePreview
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.params.SitePreview
|
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// SiteTitle returns the main site title (default is application name).
|
|
|
|
func (c *Config) SiteTitle() string {
|
|
|
|
if c.params.SiteTitle == "" {
|
|
|
|
return c.Name()
|
|
|
|
}
|
2019-12-11 14:10:20 +01:00
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
return c.params.SiteTitle
|
2019-12-11 14:10:20 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// SiteCaption returns a short site caption.
|
|
|
|
func (c *Config) SiteCaption() string {
|
|
|
|
return c.params.SiteCaption
|
2019-12-11 14:10:20 +01:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// SiteDescription returns a long site description.
|
|
|
|
func (c *Config) SiteDescription() string {
|
|
|
|
return c.params.SiteDescription
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
|
2020-05-31 02:09:52 +02:00
|
|
|
// SiteAuthor returns the site author / copyright.
|
|
|
|
func (c *Config) SiteAuthor() string {
|
|
|
|
return c.params.SiteAuthor
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Debug returns true if Debug mode is on.
|
2019-05-06 23:18:10 +02:00
|
|
|
func (c *Config) Debug() bool {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.Debug
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
|
2019-11-11 21:10:41 +01:00
|
|
|
// Public returns true if app requires no authentication.
|
|
|
|
func (c *Config) Public() bool {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.Public
|
2019-11-11 21:10:41 +01:00
|
|
|
}
|
|
|
|
|
2019-12-30 12:38:11 +01:00
|
|
|
// Experimental returns true if experimental features should be enabled.
|
|
|
|
func (c *Config) Experimental() bool {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.Experimental
|
2019-12-30 12:38:11 +01:00
|
|
|
}
|
|
|
|
|
2019-05-04 09:11:33 +02:00
|
|
|
// ReadOnly returns true if photo directories are write protected.
|
2019-05-06 23:18:10 +02:00
|
|
|
func (c *Config) ReadOnly() bool {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.ReadOnly
|
2019-05-04 09:11:33 +02:00
|
|
|
}
|
|
|
|
|
2020-01-13 16:48:32 +01:00
|
|
|
// DetectNSFW returns true if NSFW photos should be detected and flagged.
|
|
|
|
func (c *Config) DetectNSFW() bool {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.DetectNSFW
|
2019-12-15 17:19:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// UploadNSFW returns true if NSFW photos can be uploaded.
|
|
|
|
func (c *Config) UploadNSFW() bool {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.UploadNSFW
|
2019-12-15 17:19:16 +01:00
|
|
|
}
|
|
|
|
|
2020-06-29 21:14:34 +02:00
|
|
|
// AdminPassword returns the initial admin password.
|
2019-11-08 06:53:40 +01:00
|
|
|
func (c *Config) AdminPassword() string {
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.AdminPassword
|
2019-11-08 06:53:40 +01:00
|
|
|
}
|
|
|
|
|
2019-05-03 18:57:28 +02:00
|
|
|
// LogLevel returns the logrus log level.
|
2019-12-02 00:30:58 +01:00
|
|
|
func (c *Config) LogLevel() logrus.Level {
|
2019-05-03 18:57:28 +02:00
|
|
|
if c.Debug() {
|
2020-04-13 18:08:21 +02:00
|
|
|
c.params.LogLevel = "debug"
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
|
2020-04-13 18:08:21 +02:00
|
|
|
if logLevel, err := logrus.ParseLevel(c.params.LogLevel); err == nil {
|
2019-05-03 18:57:28 +02:00
|
|
|
return logLevel
|
|
|
|
} else {
|
2019-12-02 00:30:58 +01:00
|
|
|
return logrus.InfoLevel
|
2019-05-03 18:57:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-19 12:50:44 +01:00
|
|
|
// Shutdown services and workers.
|
2019-05-06 23:18:10 +02:00
|
|
|
func (c *Config) Shutdown() {
|
2020-05-26 15:15:14 +02:00
|
|
|
mutex.MainWorker.Cancel()
|
|
|
|
mutex.ShareWorker.Cancel()
|
|
|
|
mutex.SyncWorker.Cancel()
|
2020-06-29 13:35:38 +02:00
|
|
|
mutex.MetaWorker.Cancel()
|
2020-01-19 12:50:44 +01:00
|
|
|
|
2019-05-04 17:34:51 +02:00
|
|
|
if err := c.CloseDb(); err != nil {
|
|
|
|
log.Errorf("could not close database connection: %s", err)
|
|
|
|
} else {
|
|
|
|
log.Info("closed database connection")
|
|
|
|
}
|
|
|
|
}
|
2019-11-12 04:34:37 +01:00
|
|
|
|
2020-01-02 00:03:07 +01:00
|
|
|
// Workers returns the number of workers e.g. for indexing files.
|
2020-01-02 04:08:33 +01:00
|
|
|
func (c *Config) Workers() int {
|
2020-03-09 00:51:10 +01:00
|
|
|
numCPU := runtime.NumCPU()
|
|
|
|
|
2020-04-13 18:08:21 +02:00
|
|
|
if c.params.Workers > 0 && c.params.Workers <= numCPU {
|
|
|
|
return c.params.Workers
|
2020-01-06 23:43:19 +01:00
|
|
|
}
|
|
|
|
|
2020-03-09 00:51:10 +01:00
|
|
|
if numCPU > 1 {
|
|
|
|
return numCPU - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1
|
2019-11-12 04:34:37 +01:00
|
|
|
}
|
2020-01-06 06:59:35 +01:00
|
|
|
|
2020-04-06 22:09:45 +02:00
|
|
|
// WakeupInterval returns the background worker wakeup interval.
|
|
|
|
func (c *Config) WakeupInterval() time.Duration {
|
2020-04-13 18:08:21 +02:00
|
|
|
if c.params.WakeupInterval <= 0 {
|
2020-05-30 21:11:56 +02:00
|
|
|
return 15 * time.Minute
|
2020-04-06 22:09:45 +02:00
|
|
|
}
|
|
|
|
|
2020-04-13 18:08:21 +02:00
|
|
|
return time.Duration(c.params.WakeupInterval) * time.Second
|
2020-04-06 22:09:45 +02:00
|
|
|
}
|
|
|
|
|
2020-01-06 06:59:35 +01:00
|
|
|
// GeoCodingApi returns the preferred geo coding api (none, osm or places).
|
|
|
|
func (c *Config) GeoCodingApi() string {
|
2020-04-13 18:08:21 +02:00
|
|
|
switch c.params.GeoCodingApi {
|
2020-01-06 06:59:35 +01:00
|
|
|
case "places":
|
|
|
|
return "places"
|
|
|
|
case "osm":
|
|
|
|
return "osm"
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2020-05-25 19:10:44 +02:00
|
|
|
|
|
|
|
// OriginalsLimit returns the file size limit for originals.
|
|
|
|
func (c *Config) OriginalsLimit() int64 {
|
|
|
|
if c.params.OriginalsLimit <= 0 || c.params.OriginalsLimit > 100000 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Megabyte.
|
|
|
|
return c.params.OriginalsLimit * 1024 * 1024
|
|
|
|
}
|
2020-10-03 13:50:30 +02:00
|
|
|
|
2020-10-04 22:22:53 +02:00
|
|
|
// UpdatePro updates photoprism.pro api credentials.
|
|
|
|
func (c *Config) UpdatePro() {
|
|
|
|
p := c.ProConfigFile()
|
|
|
|
|
|
|
|
if err := c.pro.Refresh(); err != nil {
|
|
|
|
log.Errorf("pro: %s", err)
|
|
|
|
} else if err := c.pro.Save(p); err != nil {
|
|
|
|
log.Errorf("pro: %s", err)
|
|
|
|
} else {
|
|
|
|
c.pro.Propagate()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-03 13:50:30 +02:00
|
|
|
// initPro initializes photoprism.pro api credentials.
|
|
|
|
func (c *Config) initPro() {
|
|
|
|
c.pro = pro.NewConfig(c.Version())
|
|
|
|
p := c.ProConfigFile()
|
|
|
|
|
|
|
|
if err := c.pro.Load(p); err == nil {
|
|
|
|
// Do nothing.
|
|
|
|
} else if err := c.pro.Refresh(); err != nil {
|
|
|
|
log.Errorf("pro: %s", err)
|
|
|
|
} else if err := c.pro.Save(p); err != nil {
|
|
|
|
log.Errorf("pro: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.pro.Propagate()
|
2020-10-04 04:47:54 +02:00
|
|
|
|
|
|
|
ticker := time.NewTicker(time.Hour * 24)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
2020-10-04 22:22:53 +02:00
|
|
|
c.UpdatePro()
|
2020-10-04 04:47:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2020-10-03 13:50:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Config returns the photoprism.pro api credentials.
|
|
|
|
func (c *Config) Pro() *pro.Config {
|
|
|
|
return c.pro
|
|
|
|
}
|