This commit is contained in:
parent
0c345d4426
commit
8638929d84
52 changed files with 1332 additions and 647 deletions
BIN
assets/static/img/wallpaper/kashmir.jpg
Normal file
BIN
assets/static/img/wallpaper/kashmir.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 286 KiB |
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0{{if not .config.Settings.UI.Zoom }}, maximum-scale=1.0, user-scalable=no{{end}}">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0{{if not .config.Settings.UI.Zoom }}, maximum-scale=1.0, user-scalable=no{{end}}">
|
||||||
<title>{{ .config.SiteTitle }}</title>
|
<title>{{if and .config.SiteCaption .config.Sponsor }}{{ .config.SiteCaption }}{{else}}{{ .config.Name }}{{end}}</title>
|
||||||
|
|
||||||
<meta property="og:url" content="{{ .config.SiteUrl }}">
|
<meta property="og:url" content="{{ .config.SiteUrl }}">
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
|
|
|
@ -28,10 +28,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/commands"
|
"github.com/photoprism/photoprism/internal/commands"
|
||||||
"github.com/photoprism/photoprism/internal/config"
|
"github.com/photoprism/photoprism/internal/config"
|
||||||
"github.com/photoprism/photoprism/internal/event"
|
"github.com/photoprism/photoprism/internal/event"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "development"
|
var version = "development"
|
||||||
|
@ -55,7 +56,7 @@ func main() {
|
||||||
app.Version = version
|
app.Version = version
|
||||||
app.Copyright = appCopyright
|
app.Copyright = appCopyright
|
||||||
app.EnableBashCompletion = true
|
app.EnableBashCompletion = true
|
||||||
app.Flags = config.GlobalFlags
|
app.Flags = config.Flags.Cli()
|
||||||
app.Commands = commands.PhotoPrism
|
app.Commands = commands.PhotoPrism
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
|
|
@ -17,7 +17,6 @@ services:
|
||||||
- "~/.cache/go-mod:/go/pkg/mod"
|
- "~/.cache/go-mod:/go/pkg/mod"
|
||||||
environment:
|
environment:
|
||||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||||
|
|
|
@ -24,7 +24,6 @@ services:
|
||||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial "admin" password (minimum 8 characters)
|
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial "admin" password (minimum 8 characters)
|
||||||
## Public server URL incl http:// or https:// and /path, :port is optional
|
## Public server URL incl http:// or https:// and /path, :port is optional
|
||||||
PHOTOPRISM_SITE_URL: "https://latest.localssl.dev/"
|
PHOTOPRISM_SITE_URL: "https://latest.localssl.dev/"
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||||
|
|
|
@ -24,7 +24,6 @@ services:
|
||||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial "admin" password (minimum 8 characters)
|
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial "admin" password (minimum 8 characters)
|
||||||
## Public server URL incl http:// or https:// and /path, :port is optional
|
## Public server URL incl http:// or https:// and /path, :port is optional
|
||||||
PHOTOPRISM_SITE_URL: "https://latest.localssl.dev/"
|
PHOTOPRISM_SITE_URL: "https://latest.localssl.dev/"
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||||
|
|
|
@ -26,7 +26,6 @@ services:
|
||||||
shm_size: "2gb"
|
shm_size: "2gb"
|
||||||
environment:
|
environment:
|
||||||
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
PHOTOPRISM_SITE_DESCRIPTION: "Open-Source Photo Management"
|
||||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||||
|
|
|
@ -36,7 +36,6 @@ services:
|
||||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial "admin" password (minimum 8 characters)
|
PHOTOPRISM_ADMIN_PASSWORD: "photoprism" # initial "admin" password (minimum 8 characters)
|
||||||
## External development server URL incl http:// or https:// and /path, :port is optional
|
## External development server URL incl http:// or https:// and /path, :port is optional
|
||||||
PHOTOPRISM_SITE_URL: "https://app.localssl.dev/"
|
PHOTOPRISM_SITE_URL: "https://app.localssl.dev/"
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: "Tags and finds pictures without getting in your way!"
|
PHOTOPRISM_SITE_DESCRIPTION: "Tags and finds pictures without getting in your way!"
|
||||||
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
PHOTOPRISM_SITE_AUTHOR: "@photoprism_app"
|
||||||
|
|
|
@ -86,7 +86,6 @@ services:
|
||||||
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
||||||
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
||||||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -81,7 +81,6 @@ services:
|
||||||
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
||||||
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
||||||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -129,7 +129,6 @@ services:
|
||||||
environment:
|
environment:
|
||||||
## !! CHANGE site url if your server has a public domain name e.g. "https://photos.yourdomain.com/" !!
|
## !! CHANGE site url if your server has a public domain name e.g. "https://photos.yourdomain.com/" !!
|
||||||
PHOTOPRISM_SITE_URL: "https://_public_ip_/"
|
PHOTOPRISM_SITE_URL: "https://_public_ip_/"
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -77,7 +77,6 @@ services:
|
||||||
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
||||||
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
||||||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -73,7 +73,6 @@ services:
|
||||||
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
||||||
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
||||||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -79,7 +79,6 @@ services:
|
||||||
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
||||||
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
||||||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -72,7 +72,6 @@ services:
|
||||||
PHOTOPRISM_DETECT_NSFW: "false" # flag photos as private that MAY be offensive (requires TensorFlow)
|
PHOTOPRISM_DETECT_NSFW: "false" # flag photos as private that MAY be offensive (requires TensorFlow)
|
||||||
PHOTOPRISM_UPLOAD_NSFW: "true" # allows uploads that MAY be offensive
|
PHOTOPRISM_UPLOAD_NSFW: "true" # allows uploads that MAY be offensive
|
||||||
PHOTOPRISM_DATABASE_DRIVER: "sqlite" # SQLite is an embedded database that doesn't require a server
|
PHOTOPRISM_DATABASE_DRIVER: "sqlite" # SQLite is an embedded database that doesn't require a server
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -78,7 +78,6 @@ services:
|
||||||
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name
|
||||||
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name
|
||||||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB or MySQL database user password
|
||||||
PHOTOPRISM_SITE_TITLE: "PhotoPrism"
|
|
||||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||||
PHOTOPRISM_SITE_DESCRIPTION: ""
|
PHOTOPRISM_SITE_DESCRIPTION: ""
|
||||||
PHOTOPRISM_SITE_AUTHOR: ""
|
PHOTOPRISM_SITE_AUTHOR: ""
|
||||||
|
|
|
@ -44,7 +44,6 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||||
PHOTOPRISM_DETECT_NSFW="false" \
|
PHOTOPRISM_DETECT_NSFW="false" \
|
||||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||||
PHOTOPRISM_SITE_TITLE="PhotoPrism" \
|
|
||||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||||
PHOTOPRISM_SITE_AUTHOR="" \
|
PHOTOPRISM_SITE_AUTHOR="" \
|
||||||
|
|
|
@ -39,7 +39,6 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||||
PHOTOPRISM_DETECT_NSFW="false" \
|
PHOTOPRISM_DETECT_NSFW="false" \
|
||||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||||
PHOTOPRISM_SITE_TITLE="PhotoPrism" \
|
|
||||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||||
PHOTOPRISM_SITE_AUTHOR="" \
|
PHOTOPRISM_SITE_AUTHOR="" \
|
||||||
|
|
|
@ -39,7 +39,6 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||||
PHOTOPRISM_DETECT_NSFW="false" \
|
PHOTOPRISM_DETECT_NSFW="false" \
|
||||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||||
PHOTOPRISM_SITE_TITLE="PhotoPrism" \
|
|
||||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||||
PHOTOPRISM_SITE_AUTHOR="" \
|
PHOTOPRISM_SITE_AUTHOR="" \
|
||||||
|
|
|
@ -44,7 +44,6 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||||
PHOTOPRISM_DETECT_NSFW="false" \
|
PHOTOPRISM_DETECT_NSFW="false" \
|
||||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||||
PHOTOPRISM_SITE_TITLE="PhotoPrism" \
|
|
||||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||||
PHOTOPRISM_SITE_AUTHOR="" \
|
PHOTOPRISM_SITE_AUTHOR="" \
|
||||||
|
|
|
@ -44,7 +44,6 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||||
PHOTOPRISM_DETECT_NSFW="false" \
|
PHOTOPRISM_DETECT_NSFW="false" \
|
||||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||||
PHOTOPRISM_SITE_TITLE="PhotoPrism" \
|
|
||||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||||
PHOTOPRISM_SITE_AUTHOR="" \
|
PHOTOPRISM_SITE_AUTHOR="" \
|
||||||
|
|
|
@ -44,7 +44,6 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
||||||
PHOTOPRISM_DETECT_NSFW="false" \
|
PHOTOPRISM_DETECT_NSFW="false" \
|
||||||
PHOTOPRISM_EXPERIMENTAL="false" \
|
PHOTOPRISM_EXPERIMENTAL="false" \
|
||||||
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
PHOTOPRISM_SITE_URL="http://localhost:2342/" \
|
||||||
PHOTOPRISM_SITE_TITLE="PhotoPrism" \
|
|
||||||
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
PHOTOPRISM_SITE_CAPTION="AI-Powered Photos App" \
|
||||||
PHOTOPRISM_SITE_DESCRIPTION="" \
|
PHOTOPRISM_SITE_DESCRIPTION="" \
|
||||||
PHOTOPRISM_SITE_AUTHOR="" \
|
PHOTOPRISM_SITE_AUTHOR="" \
|
||||||
|
|
24
frontend/package-lock.json
generated
24
frontend/package-lock.json
generated
|
@ -4402,9 +4402,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.117",
|
"version": "1.4.118",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.117.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz",
|
||||||
"integrity": "sha512-ypZHxY+Sf/PXu7LVN+xoeanyisnJeSOy8Ki439L/oLueZb4c72FI45zXcK3gPpmTwyufh9m6NnbMLXnJh/0Fxg=="
|
"integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w=="
|
||||||
},
|
},
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
|
@ -11753,9 +11753,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
},
|
},
|
||||||
"node_modules/tsscmp": {
|
"node_modules/tsscmp": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
|
@ -16059,9 +16059,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.4.117",
|
"version": "1.4.118",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.117.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz",
|
||||||
"integrity": "sha512-ypZHxY+Sf/PXu7LVN+xoeanyisnJeSOy8Ki439L/oLueZb4c72FI45zXcK3gPpmTwyufh9m6NnbMLXnJh/0Fxg=="
|
"integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w=="
|
||||||
},
|
},
|
||||||
"emoji-regex": {
|
"emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
|
@ -21324,9 +21324,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
},
|
},
|
||||||
"tsscmp": {
|
"tsscmp": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
|
|
|
@ -165,12 +165,22 @@ router.beforeEach((to, from, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach((to) => {
|
router.afterEach((to) => {
|
||||||
if (to.meta.title && config.values.siteTitle !== to.meta.title) {
|
const t = to.meta["title"] ? to.meta["title"] : "";
|
||||||
config.page.title = $gettext(to.meta.title);
|
|
||||||
window.document.title = config.values.siteTitle + ": " + config.page.title;
|
if (t !== "" && config.values.siteTitle !== t && config.values.name !== t) {
|
||||||
|
config.page.title = $gettext(t);
|
||||||
|
if (config.page.title === "") {
|
||||||
|
window.document.title = config.values.siteTitle;
|
||||||
|
} else {
|
||||||
|
window.document.title = config.page.title + " – " + config.values.siteTitle;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
config.page.title = config.values.siteTitle;
|
config.page.title = config.values.name;
|
||||||
window.document.title = config.values.siteTitle + ": " + config.values.siteCaption;
|
if (config.values.siteCaption === "" || !config.values.sponsor) {
|
||||||
|
window.document.title = config.values.siteTitle;
|
||||||
|
} else {
|
||||||
|
window.document.title = config.values.siteCaption;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,18 @@
|
||||||
<v-card flat tile class="ma-0 pa-0 application p-about-footer">
|
<v-card flat tile class="ma-0 pa-0 application p-about-footer">
|
||||||
<v-card-actions class="px-4 py-2">
|
<v-card-actions class="px-4 py-2">
|
||||||
<v-layout wrap align-top pt-3>
|
<v-layout wrap align-top pt-3>
|
||||||
<v-flex xs12 sm6 class="px-0 pb-2 body-1 text-selectable text-xs-left">
|
<v-flex xs12 sm6 class="px-0 pb-2 body-1 text-selectable text-xs-center text-sm-left">
|
||||||
Build {{ $config.get("version") }}<br>
|
|
||||||
<template v-if="sponsor"><router-link to="/about" class="text-link"><translate>Thank you for supporting PhotoPrism®</translate></router-link></template>
|
<template v-if="sponsor"><router-link to="/about" class="text-link"><translate>Thank you for supporting PhotoPrism®</translate></router-link></template>
|
||||||
<strong v-else><router-link to="/about" class="text-link"><translate>PhotoPrism® needs your support</translate></router-link></strong>
|
<strong v-else><router-link to="/about" class="text-link"><translate>PhotoPrism® needs your support</translate></router-link></strong>
|
||||||
|
<br><a href="https://docs.photoprism.app/release-notes/" target="_blank">Build {{ $config.get("version") }}</a>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
<v-flex xs12 sm6 class="px-0 pb-2 body-1 text-xs-left text-sm-right">
|
<v-flex xs12 sm6 class="px-0 pb-2 body-1 text-xs-center text-sm-right">
|
||||||
<a href="https://photoprism.app/team/" target="_blank">© 2018-2022 PhotoPrism UG</a><br>
|
<span class="hidden-sm-and-down">
|
||||||
<a href="https://raw.githubusercontent.com/photoprism/photoprism/develop/NOTICE"
|
<a href="https://raw.githubusercontent.com/photoprism/photoprism/develop/NOTICE"
|
||||||
target="_blank" class="text-link">3rd-party software packages</a>
|
target="_blank" class="text-link">3rd-party software packages</a><br>
|
||||||
|
</span>
|
||||||
|
<a href="https://photoprism.app/team/" target="_blank">© 2018-2022 PhotoPrism UG</a>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
|
|
@ -86,6 +86,10 @@ footer {
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#photoprism .p-about-footer .body-1 {
|
||||||
|
line-height: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
@ -283,6 +283,7 @@ export const MapsStyle = () => [
|
||||||
{
|
{
|
||||||
text: $gettext("Streets"),
|
text: $gettext("Streets"),
|
||||||
value: "streets",
|
value: "streets",
|
||||||
|
sponsor: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: $gettext("Hybrid"),
|
text: $gettext("Hybrid"),
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="p-page p-page-about">
|
<div class="p-page p-page-about">
|
||||||
<v-toolbar flat color="secondary" :dense="$vuetify.breakpoint.smAndDown">
|
<v-toolbar flat color="secondary" :dense="$vuetify.breakpoint.smAndDown">
|
||||||
<v-toolbar-title>
|
<v-toolbar-title>
|
||||||
<translate>About</translate>
|
<translate>About</translate> {{ $config.get('name') }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<v-container fluid fill-height class="auth-login wallpaper pa-3">
|
<v-container fluid fill-height class="auth-login wallpaper pa-3" :style="wallpaper()">
|
||||||
<v-layout align-center justify-center>
|
<v-layout align-center justify-center>
|
||||||
<v-flex xs12 sm8 md4 xl3 xxl2>
|
<v-flex xs12 sm8 md4 xl3 xxl2>
|
||||||
<v-form ref="form" dense class="auth-login-form" accept-charset="UTF-8" @submit.prevent="login">
|
<v-form ref="form" dense class="auth-login-form" accept-charset="UTF-8" @submit.prevent="login">
|
||||||
|
@ -56,17 +56,38 @@
|
||||||
</v-form>
|
</v-form>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
<footer>
|
<footer v-if="sponsor">
|
||||||
<v-layout wrap align-top pa-0 ma-0>
|
<v-layout wrap align-top pa-0 ma-0>
|
||||||
<v-flex xs12 class="pa-0 body-2 text-selectable text-xs-center white--text" :class="[config.imprint ? 'text-sm-left sm6' : '']">
|
<v-flex xs12 class="pa-0 body-2 text-selectable text-xs-center white--text"
|
||||||
<strong>{{ config.siteTitle }}</strong> – {{ config.siteCaption }}
|
:class="[config.imprint ? 'text-sm-left sm6' : '']">
|
||||||
|
<strong>{{ config.siteCaption ? config.siteCaption : config.siteTitle }}</strong>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex v-if="config.imprint" xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-right white--text">
|
<v-flex v-if="config.imprint" xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-right white--text">
|
||||||
<a v-if="config.imprintUrl" :href="config.imprintUrl" target="_blank" class="text-link" :style="`color: ${colors.link}!important`">{{ config.imprint }}</a>
|
<a v-if="config.imprintUrl" :href="config.imprintUrl" target="_blank" class="text-link"
|
||||||
|
:style="`color: ${colors.link}!important`">{{ config.imprint }}</a>
|
||||||
<span v-else>{{ config.imprint }}</span>
|
<span v-else>{{ config.imprint }}</span>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</footer>
|
</footer>
|
||||||
|
<footer v-else>
|
||||||
|
<v-layout wrap align-top pa-0 ma-0>
|
||||||
|
<v-flex xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-left white--text text-selectable">
|
||||||
|
<strong>{{ config.siteTitle }}</strong> – {{ config.siteCaption }}
|
||||||
|
</v-flex>
|
||||||
|
<v-flex xs12 sm6 class="pa-0 body-2 text-xs-center text-sm-right white--text">
|
||||||
|
<v-btn
|
||||||
|
href="https://link.photoprism.app/patreon"
|
||||||
|
target="_blank"
|
||||||
|
color="transparent"
|
||||||
|
class="white--text px-3 py-2 ma-0 action-sponsor"
|
||||||
|
round depressed small
|
||||||
|
>
|
||||||
|
<translate>Become a sponsor</translate>
|
||||||
|
<v-icon :left="rtl" :right="!rtl" size="16" class="ml-2" dark>star</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
</footer>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -76,6 +97,7 @@ export default {
|
||||||
name: "PPageAuthLogin",
|
name: "PPageAuthLogin",
|
||||||
data() {
|
data() {
|
||||||
const c = this.$config.values;
|
const c = this.$config.values;
|
||||||
|
const sponsor = this.$config.isSponsor();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
colors: {
|
colors: {
|
||||||
|
@ -85,12 +107,13 @@ export default {
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
showPassword: false,
|
showPassword: false,
|
||||||
username: "",
|
username: sponsor ? "" : "admin",
|
||||||
password: "",
|
password: "",
|
||||||
sponsor: this.$config.isSponsor(),
|
sponsor: sponsor,
|
||||||
config: this.$config.values,
|
config: this.$config.values,
|
||||||
siteDescription: c.siteDescription ? c.siteDescription : c.siteCaption,
|
siteDescription: c.siteDescription ? c.siteDescription : c.siteCaption,
|
||||||
nextUrl: this.$route.params.nextUrl ? this.$route.params.nextUrl : "/",
|
nextUrl: this.$route.params.nextUrl ? this.$route.params.nextUrl : "/",
|
||||||
|
wallpaperUri: c.wallpaperUri,
|
||||||
rtl: this.$rtl,
|
rtl: this.$rtl,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -106,6 +129,13 @@ export default {
|
||||||
this.$scrollbar.show();
|
this.$scrollbar.show();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
wallpaper() {
|
||||||
|
if (this.wallpaperUri) {
|
||||||
|
return `background-image: url(${this.wallpaperUri});`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
},
|
||||||
login() {
|
login() {
|
||||||
if (!this.username || !this.password) {
|
if (!this.username || !this.password) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -9,6 +9,7 @@ var ShowCommand = cli.Command{
|
||||||
Name: "show",
|
Name: "show",
|
||||||
Usage: "Configuration and system report subcommands",
|
Usage: "Configuration and system report subcommands",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
|
ShowFlagsCommand,
|
||||||
ShowConfigCommand,
|
ShowConfigCommand,
|
||||||
ShowTagsCommand,
|
ShowTagsCommand,
|
||||||
ShowFiltersCommand,
|
ShowFiltersCommand,
|
||||||
|
|
119
internal/commands/show_flags.go
Normal file
119
internal/commands/show_flags.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
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: "Shows environment variable command-line parameter names",
|
||||||
|
Flags: report.CliFlags,
|
||||||
|
Action: showFlagsAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
var faceFlagsInfo = `!!! info ""
|
||||||
|
To [recognize faces](../user-guide/organize/people.md), PhotoPrism first extracts crops from your images using a
|
||||||
|
[library](https://github.com/esimov/pigo) based on [pixel intensity comparisons](https://arxiv.org/pdf/1305.4537.pdf).
|
||||||
|
These are then fed into TensorFlow to compute [512-dimensional vectors](https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Schroff_FaceNet_A_Unified_2015_CVPR_paper.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.Render(rows, cols, report.CliFormat(ctx))
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Section struct {
|
||||||
|
Start string
|
||||||
|
Caption string
|
||||||
|
Info string
|
||||||
|
}
|
||||||
|
|
||||||
|
s := []Section{
|
||||||
|
{Start: "PHOTOPRISM_ADMIN_PASSWORD", Caption: "Authentication"},
|
||||||
|
{Start: "PHOTOPRISM_LOG_LEVEL", Caption: "Logging"},
|
||||||
|
{Start: "PHOTOPRISM_CONFIG_PATH", Caption: "Storage"},
|
||||||
|
{Start: "PHOTOPRISM_WORKERS", Caption: "Index Workers"},
|
||||||
|
{Start: "PHOTOPRISM_READONLY", Caption: "Feature Flags"},
|
||||||
|
{Start: "PHOTOPRISM_DEFAULT_LOCALE", Caption: "Customization"},
|
||||||
|
{Start: "PHOTOPRISM_CDN_URL", Caption: "Site Information"},
|
||||||
|
{Start: "PHOTOPRISM_HTTP_PORT", Caption: "Web Server"},
|
||||||
|
{Start: "PHOTOPRISM_DATABASE_DRIVER", Caption: "Database Connection"},
|
||||||
|
{Start: "PHOTOPRISM_DARKTABLE_BIN", Caption: "File Converters"},
|
||||||
|
{Start: "PHOTOPRISM_DOWNLOAD_TOKEN", Caption: "Security Tokens"},
|
||||||
|
{Start: "PHOTOPRISM_THUMB_COLOR", Caption: "Image Quality"},
|
||||||
|
{Start: "PHOTOPRISM_FACE_SIZE", Caption: "Face Recognition",
|
||||||
|
Info: faceFlagsInfo},
|
||||||
|
{Start: "PHOTOPRISM_PID_FILENAME", Caption: "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.Caption)
|
||||||
|
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.Render(secRows, cols, report.CliFormat(ctx))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
if j >= len(rows) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ var UsersCommand = cli.Command{
|
||||||
Name: "add",
|
Name: "add",
|
||||||
Usage: "Adds a new user",
|
Usage: "Adds a new user",
|
||||||
Action: usersAddAction,
|
Action: usersAddAction,
|
||||||
|
Hidden: !config.Sponsor(),
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "fullname, n",
|
Name: "fullname, n",
|
||||||
|
|
66
internal/config/cli_flag.go
Normal file
66
internal/config/cli_flag.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/list"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CliFlag represents a command-line parameter.
|
||||||
|
type CliFlag struct {
|
||||||
|
Flag cli.DocGenerationFlag
|
||||||
|
Tags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip checks if the parameter should be skipped based on a list of tags.
|
||||||
|
func (f CliFlag) Skip(tags []string) bool {
|
||||||
|
return len(f.Tags) > 0 && !list.ContainsAny(f.Tags, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields returns the flag struct fields.
|
||||||
|
func (f CliFlag) Fields() reflect.Value {
|
||||||
|
fields := reflect.ValueOf(f.Flag)
|
||||||
|
|
||||||
|
for fields.Kind() == reflect.Ptr {
|
||||||
|
fields = reflect.Indirect(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hidden checks if the flag is hidden.
|
||||||
|
func (f CliFlag) Hidden() bool {
|
||||||
|
field := f.Fields().FieldByName("Hidden")
|
||||||
|
|
||||||
|
if !field.IsValid() || !field.Bool() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvVar returns the flag environment variable name.
|
||||||
|
func (f CliFlag) EnvVar() string {
|
||||||
|
field := f.Fields().FieldByName("EnvVar")
|
||||||
|
|
||||||
|
if !field.IsValid() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the command flag name.
|
||||||
|
func (f CliFlag) Name() string {
|
||||||
|
return f.Flag.GetName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage returns the command flag usage.
|
||||||
|
func (f CliFlag) Usage() string {
|
||||||
|
if list.Contains(f.Tags, EnvSponsor) {
|
||||||
|
return f.Flag.GetUsage() + " *sponsors only*"
|
||||||
|
} else {
|
||||||
|
return f.Flag.GetUsage()
|
||||||
|
}
|
||||||
|
}
|
66
internal/config/cli_flag_test.go
Normal file
66
internal/config/cli_flag_test.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCliFlag_Skip(t *testing.T) {
|
||||||
|
withTags := CliFlag{
|
||||||
|
Flag: cli.StringFlag{
|
||||||
|
Name: "with-tags",
|
||||||
|
Usage: "`STRING`",
|
||||||
|
EnvVar: "PHOTOPRISM_WITH_TAGS",
|
||||||
|
},
|
||||||
|
Tags: []string{"foo", "bar"},
|
||||||
|
}
|
||||||
|
|
||||||
|
noTags := CliFlag{
|
||||||
|
Flag: cli.StringFlag{
|
||||||
|
Name: "no-tags",
|
||||||
|
Usage: "`STRING`",
|
||||||
|
EnvVar: "PHOTOPRISM_NO_TAGS",
|
||||||
|
},
|
||||||
|
Tags: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("True", func(t *testing.T) {
|
||||||
|
assert.True(t, withTags.Skip([]string{"baz"}))
|
||||||
|
assert.False(t, noTags.Skip([]string{"baz"}))
|
||||||
|
})
|
||||||
|
t.Run("False", func(t *testing.T) {
|
||||||
|
assert.False(t, withTags.Skip([]string{"foo"}))
|
||||||
|
assert.False(t, noTags.Skip([]string{"foo"}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCliFlag_Hidden(t *testing.T) {
|
||||||
|
hidden := CliFlag{
|
||||||
|
Flag: cli.StringFlag{
|
||||||
|
Name: "is-hidden",
|
||||||
|
Usage: "`STRING`",
|
||||||
|
EnvVar: "PHOTOPRISM_HIDDEN",
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
Tags: []string{"foo", "bar"},
|
||||||
|
}
|
||||||
|
|
||||||
|
visible := CliFlag{
|
||||||
|
Flag: cli.StringFlag{
|
||||||
|
Name: "is-visible",
|
||||||
|
Usage: "`STRING`",
|
||||||
|
EnvVar: "PHOTOPRISM_VISIBLE",
|
||||||
|
Hidden: false,
|
||||||
|
},
|
||||||
|
Tags: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("True", func(t *testing.T) {
|
||||||
|
assert.True(t, hidden.Hidden())
|
||||||
|
})
|
||||||
|
t.Run("False", func(t *testing.T) {
|
||||||
|
assert.False(t, visible.Hidden())
|
||||||
|
})
|
||||||
|
}
|
36
internal/config/cli_flags.go
Normal file
36
internal/config/cli_flags.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/photoprism/photoprism/pkg/list"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CliFlags represents a list of command-line parameters.
|
||||||
|
type CliFlags []CliFlag
|
||||||
|
|
||||||
|
// Cli returns the currently active command-line parameters.
|
||||||
|
func (f CliFlags) Cli() (result []cli.Flag) {
|
||||||
|
var tags []string
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case Sponsor():
|
||||||
|
tags = []string{EnvSponsor}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.Find(tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find finds command-line parameters based on a list of tags.
|
||||||
|
func (f CliFlags) Find(tags []string) (result []cli.Flag) {
|
||||||
|
result = make([]cli.Flag, 0, len(f))
|
||||||
|
|
||||||
|
for _, flag := range f {
|
||||||
|
if len(flag.Tags) > 0 && !list.ContainsAny(flag.Tags, tags) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, flag.Flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
19
internal/config/cli_flags_report.go
Normal file
19
internal/config/cli_flags_report.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
// Report returns global config values as a table for reporting.
|
||||||
|
func (f CliFlags) Report() (rows [][]string, cols []string) {
|
||||||
|
cols = []string{"Variable", "Flag", "Usage"}
|
||||||
|
|
||||||
|
rows = make([][]string, 0, len(f))
|
||||||
|
|
||||||
|
for _, flag := range Flags {
|
||||||
|
if flag.Hidden() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
row := []string{flag.EnvVar(), flag.Name(), flag.Usage()}
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, cols
|
||||||
|
}
|
25
internal/config/cli_flags_test.go
Normal file
25
internal/config/cli_flags_test.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCliFlags_Cli(t *testing.T) {
|
||||||
|
cliFlags := Flags.Cli()
|
||||||
|
standard := Flags.Find([]string{})
|
||||||
|
|
||||||
|
assert.Greater(t, len(cliFlags), len(standard))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCliFlags_Find(t *testing.T) {
|
||||||
|
cliFlags := Flags.Cli()
|
||||||
|
standard := Flags.Find([]string{})
|
||||||
|
sponsor := Flags.Find([]string{EnvSponsor})
|
||||||
|
other := Flags.Find([]string{"other"})
|
||||||
|
|
||||||
|
assert.Equal(t, len(standard), len(other))
|
||||||
|
assert.Equal(t, len(cliFlags), len(sponsor))
|
||||||
|
assert.Less(t, len(other), len(sponsor))
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ type ClientConfig struct {
|
||||||
ManifestUri string `json:"manifestUri"`
|
ManifestUri string `json:"manifestUri"`
|
||||||
ApiUri string `json:"apiUri"`
|
ApiUri string `json:"apiUri"`
|
||||||
ContentUri string `json:"contentUri"`
|
ContentUri string `json:"contentUri"`
|
||||||
|
WallpaperUri string `json:"wallpaperUri"`
|
||||||
SiteUrl string `json:"siteUrl"`
|
SiteUrl string `json:"siteUrl"`
|
||||||
SiteDomain string `json:"siteDomain"`
|
SiteDomain string `json:"siteDomain"`
|
||||||
SiteAuthor string `json:"siteAuthor"`
|
SiteAuthor string `json:"siteAuthor"`
|
||||||
|
@ -227,6 +228,7 @@ func (c *Config) PublicConfig() ClientConfig {
|
||||||
AppName: c.AppName(),
|
AppName: c.AppName(),
|
||||||
AppMode: c.AppMode(),
|
AppMode: c.AppMode(),
|
||||||
AppIcon: c.AppIcon(),
|
AppIcon: c.AppIcon(),
|
||||||
|
WallpaperUri: c.WallpaperUri(),
|
||||||
Version: c.Version(),
|
Version: c.Version(),
|
||||||
Copyright: c.Copyright(),
|
Copyright: c.Copyright(),
|
||||||
Debug: c.Debug(),
|
Debug: c.Debug(),
|
||||||
|
@ -300,6 +302,7 @@ func (c *Config) GuestConfig() ClientConfig {
|
||||||
AppName: c.AppName(),
|
AppName: c.AppName(),
|
||||||
AppMode: c.AppMode(),
|
AppMode: c.AppMode(),
|
||||||
AppIcon: c.AppIcon(),
|
AppIcon: c.AppIcon(),
|
||||||
|
WallpaperUri: c.WallpaperUri(),
|
||||||
Version: c.Version(),
|
Version: c.Version(),
|
||||||
Copyright: c.Copyright(),
|
Copyright: c.Copyright(),
|
||||||
Debug: c.Debug(),
|
Debug: c.Debug(),
|
||||||
|
@ -367,6 +370,7 @@ func (c *Config) UserConfig() ClientConfig {
|
||||||
AppName: c.AppName(),
|
AppName: c.AppName(),
|
||||||
AppMode: c.AppMode(),
|
AppMode: c.AppMode(),
|
||||||
AppIcon: c.AppIcon(),
|
AppIcon: c.AppIcon(),
|
||||||
|
WallpaperUri: c.WallpaperUri(),
|
||||||
Version: c.Version(),
|
Version: c.Version(),
|
||||||
Copyright: c.Copyright(),
|
Copyright: c.Copyright(),
|
||||||
Debug: c.Debug(),
|
Debug: c.Debug(),
|
||||||
|
|
|
@ -71,14 +71,16 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initLogger(debug bool) {
|
func initLogger() {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
log.SetFormatter(&logrus.TextFormatter{
|
log.SetFormatter(&logrus.TextFormatter{
|
||||||
DisableColors: false,
|
DisableColors: false,
|
||||||
FullTimestamp: true,
|
FullTimestamp: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
if debug {
|
if Env(EnvTrace) {
|
||||||
|
log.SetLevel(logrus.TraceLevel)
|
||||||
|
} else if Env(EnvDebug) {
|
||||||
log.SetLevel(logrus.DebugLevel)
|
log.SetLevel(logrus.DebugLevel)
|
||||||
} else {
|
} else {
|
||||||
log.SetLevel(logrus.InfoLevel)
|
log.SetLevel(logrus.InfoLevel)
|
||||||
|
@ -89,7 +91,7 @@ func initLogger(debug bool) {
|
||||||
// NewConfig initialises a new configuration file
|
// NewConfig initialises a new configuration file
|
||||||
func NewConfig(ctx *cli.Context) *Config {
|
func NewConfig(ctx *cli.Context) *Config {
|
||||||
// Initialize logger.
|
// Initialize logger.
|
||||||
initLogger(ctx.GlobalBool("debug"))
|
initLogger()
|
||||||
|
|
||||||
// Initialize options from config file and CLI context.
|
// Initialize options from config file and CLI context.
|
||||||
c := &Config{
|
c := &Config{
|
||||||
|
@ -291,6 +293,10 @@ func (c *Config) SerialChecksum() string {
|
||||||
|
|
||||||
// Name returns the application name ("PhotoPrism").
|
// Name returns the application name ("PhotoPrism").
|
||||||
func (c *Config) Name() string {
|
func (c *Config) Name() string {
|
||||||
|
if c.Sponsor() && c.options.Name == "PhotoPrism" {
|
||||||
|
c.options.Name = "PhotoPrism+"
|
||||||
|
}
|
||||||
|
|
||||||
return c.options.Name
|
return c.options.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,6 +337,10 @@ func (c *Config) ApiUri() string {
|
||||||
|
|
||||||
// CdnUrl returns the optional content delivery network URI without trailing slash.
|
// CdnUrl returns the optional content delivery network URI without trailing slash.
|
||||||
func (c *Config) CdnUrl(res string) string {
|
func (c *Config) CdnUrl(res string) string {
|
||||||
|
if c.NoSponsor() {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
return strings.TrimRight(c.options.CdnUrl, "/") + res
|
return strings.TrimRight(c.options.CdnUrl, "/") + res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +379,7 @@ func (c *Config) SiteAuthor() string {
|
||||||
|
|
||||||
// SiteTitle returns the main site title (default is application name).
|
// SiteTitle returns the main site title (default is application name).
|
||||||
func (c *Config) SiteTitle() string {
|
func (c *Config) SiteTitle() string {
|
||||||
if c.options.SiteTitle == "" {
|
if c.options.SiteTitle == "" || c.NoSponsor() {
|
||||||
return c.Name()
|
return c.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +411,7 @@ func (c *Config) SitePreview() string {
|
||||||
|
|
||||||
// Imprint returns the legal info text for the page footer.
|
// Imprint returns the legal info text for the page footer.
|
||||||
func (c *Config) Imprint() string {
|
func (c *Config) Imprint() string {
|
||||||
if !c.Sponsor() || c.Test() {
|
if c.NoSponsor() {
|
||||||
return MsgSponsor
|
return MsgSponsor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +420,7 @@ func (c *Config) Imprint() string {
|
||||||
|
|
||||||
// ImprintUrl returns the legal info url.
|
// ImprintUrl returns the legal info url.
|
||||||
func (c *Config) ImprintUrl() string {
|
func (c *Config) ImprintUrl() string {
|
||||||
if !c.Sponsor() || c.Test() {
|
if c.NoSponsor() {
|
||||||
return SignUpURL
|
return SignUpURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,6 +432,7 @@ func (c *Config) Debug() bool {
|
||||||
if c.Trace() {
|
if c.Trace() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.options.Debug
|
return c.options.Debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,6 +456,11 @@ func (c *Config) Sponsor() bool {
|
||||||
return c.options.Sponsor || c.Test()
|
return c.options.Sponsor || c.Test()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoSponsor reports if the instance is not operated by a sponsor.
|
||||||
|
func (c *Config) NoSponsor() bool {
|
||||||
|
return !c.Sponsor() && !c.Demo()
|
||||||
|
}
|
||||||
|
|
||||||
// Public checks if app runs in public mode and requires no authentication.
|
// Public checks if app runs in public mode and requires no authentication.
|
||||||
func (c *Config) Public() bool {
|
func (c *Config) Public() bool {
|
||||||
if c.Auth() {
|
if c.Auth() {
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/i18n"
|
"github.com/photoprism/photoprism/internal/i18n"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/clean"
|
||||||
"github.com/photoprism/photoprism/pkg/fs"
|
"github.com/photoprism/photoprism/pkg/fs"
|
||||||
"github.com/photoprism/photoprism/pkg/txt"
|
"github.com/photoprism/photoprism/pkg/txt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultTheme returns the default user interface theme name.
|
// DefaultTheme returns the default user interface theme name.
|
||||||
func (c *Config) DefaultTheme() string {
|
func (c *Config) DefaultTheme() string {
|
||||||
if c.options.DefaultTheme == "" || !c.Sponsor() {
|
if c.options.DefaultTheme == "" || c.NoSponsor() {
|
||||||
return "default"
|
return "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +34,7 @@ func (c *Config) DefaultLocale() string {
|
||||||
func (c *Config) AppIcon() string {
|
func (c *Config) AppIcon() string {
|
||||||
defaultIcon := "logo"
|
defaultIcon := "logo"
|
||||||
|
|
||||||
if c.options.AppIcon == "" || c.options.AppIcon == defaultIcon {
|
if c.NoSponsor() || c.options.AppIcon == "" || c.options.AppIcon == defaultIcon {
|
||||||
// Default.
|
// Default.
|
||||||
} else if fs.FileExists(c.AppIconsPath(c.options.AppIcon, "512.png")) {
|
} else if fs.FileExists(c.AppIconsPath(c.options.AppIcon, "512.png")) {
|
||||||
return c.options.AppIcon
|
return c.options.AppIcon
|
||||||
|
@ -44,9 +46,9 @@ func (c *Config) AppIcon() string {
|
||||||
// AppIconsPath returns the path to the app icons.
|
// AppIconsPath returns the path to the app icons.
|
||||||
func (c *Config) AppIconsPath(name ...string) string {
|
func (c *Config) AppIconsPath(name ...string) string {
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
folder := []string{c.StaticPath(), "icons"}
|
filePath := []string{c.StaticPath(), "icons"}
|
||||||
folder = append(folder, name...)
|
filePath = append(filePath, name...)
|
||||||
return filepath.Join(folder...)
|
return filepath.Join(filePath...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath.Join(c.StaticPath(), "icons")
|
return filepath.Join(c.StaticPath(), "icons")
|
||||||
|
@ -56,7 +58,7 @@ func (c *Config) AppIconsPath(name ...string) string {
|
||||||
func (c *Config) AppName() string {
|
func (c *Config) AppName() string {
|
||||||
name := strings.TrimSpace(c.options.AppName)
|
name := strings.TrimSpace(c.options.AppName)
|
||||||
|
|
||||||
if name == "" {
|
if c.NoSponsor() || name == "" {
|
||||||
name = c.SiteTitle()
|
name = c.SiteTitle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,3 +85,36 @@ func (c *Config) AppMode() string {
|
||||||
return "standalone"
|
return "standalone"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WallpaperUri returns the login screen background image `URI`.
|
||||||
|
func (c *Config) WallpaperUri() string {
|
||||||
|
if c.NoSponsor() {
|
||||||
|
return ""
|
||||||
|
} else if strings.Contains(c.options.WallpaperUri, "/") {
|
||||||
|
return c.options.WallpaperUri
|
||||||
|
}
|
||||||
|
|
||||||
|
assetPath := "img/wallpaper"
|
||||||
|
|
||||||
|
// Empty URI?
|
||||||
|
if c.options.WallpaperUri == "" {
|
||||||
|
if !fs.PathExists(filepath.Join(c.StaticPath(), assetPath)) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
c.options.WallpaperUri = "default.jpg"
|
||||||
|
} else if !strings.Contains(c.options.WallpaperUri, ".") {
|
||||||
|
c.options.WallpaperUri += fs.ExtJPEG
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid URI? Local file?
|
||||||
|
if p := clean.Path(c.options.WallpaperUri); p == "" {
|
||||||
|
return ""
|
||||||
|
} else if fs.FileExists(filepath.Join(c.StaticPath(), assetPath, p)) {
|
||||||
|
c.options.WallpaperUri = path.Join(c.StaticUri(), assetPath, p)
|
||||||
|
} else {
|
||||||
|
c.options.WallpaperUri = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.options.WallpaperUri
|
||||||
|
}
|
|
@ -11,11 +11,20 @@ func TestConfig_DefaultTheme(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
assert.Equal(t, "default", c.DefaultTheme())
|
assert.Equal(t, "default", c.DefaultTheme())
|
||||||
|
c.options.Demo = false
|
||||||
c.options.Sponsor = false
|
c.options.Sponsor = false
|
||||||
|
c.options.Test = false
|
||||||
c.options.DefaultTheme = "grayscale"
|
c.options.DefaultTheme = "grayscale"
|
||||||
assert.Equal(t, "default", c.DefaultTheme())
|
assert.Equal(t, "default", c.DefaultTheme())
|
||||||
c.options.Sponsor = true
|
c.options.Sponsor = true
|
||||||
assert.Equal(t, "grayscale", c.DefaultTheme())
|
assert.Equal(t, "grayscale", c.DefaultTheme())
|
||||||
|
c.options.Sponsor = false
|
||||||
|
c.options.Test = true
|
||||||
|
assert.Equal(t, "grayscale", c.DefaultTheme())
|
||||||
|
c.options.Sponsor = false
|
||||||
|
c.options.Test = false
|
||||||
|
assert.Equal(t, "default", c.DefaultTheme())
|
||||||
|
c.options.Sponsor = true
|
||||||
c.options.DefaultTheme = ""
|
c.options.DefaultTheme = ""
|
||||||
assert.Equal(t, "default", c.DefaultTheme())
|
assert.Equal(t, "default", c.DefaultTheme())
|
||||||
c.options.Sponsor = false
|
c.options.Sponsor = false
|
||||||
|
@ -77,3 +86,19 @@ func TestConfig_AppMode(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, "standalone", c.AppMode())
|
assert.Equal(t, "standalone", c.AppMode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfig_WallpaperUri(t *testing.T) {
|
||||||
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
assert.Equal(t, "", c.WallpaperUri())
|
||||||
|
c.options.WallpaperUri = "kashmir"
|
||||||
|
assert.Equal(t, "/static/img/wallpaper/kashmir.jpg", c.WallpaperUri())
|
||||||
|
c.options.WallpaperUri = "https://cdn.photoprism.app/wallpaper/welcome.jpg"
|
||||||
|
assert.Equal(t, "https://cdn.photoprism.app/wallpaper/welcome.jpg", c.WallpaperUri())
|
||||||
|
c.options.Test = false
|
||||||
|
assert.Equal(t, "", c.WallpaperUri())
|
||||||
|
c.options.Test = true
|
||||||
|
assert.Equal(t, "https://cdn.photoprism.app/wallpaper/welcome.jpg", c.WallpaperUri())
|
||||||
|
c.options.WallpaperUri = ""
|
||||||
|
assert.Equal(t, "", c.WallpaperUri())
|
||||||
|
}
|
|
@ -1,5 +1,10 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
|
// Sponsor checks if sponsor features should be enabled.
|
||||||
|
func Sponsor() bool {
|
||||||
|
return Env(EnvDemo, EnvSponsor, EnvTest)
|
||||||
|
}
|
||||||
|
|
||||||
// DisableWebDAV checks if the built-in WebDAV server should be disabled.
|
// DisableWebDAV checks if the built-in WebDAV server should be disabled.
|
||||||
func (c *Config) DisableWebDAV() bool {
|
func (c *Config) DisableWebDAV() bool {
|
||||||
if c.ReadOnly() || c.Demo() {
|
if c.ReadOnly() || c.Demo() {
|
||||||
|
|
|
@ -12,14 +12,15 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
||||||
cols = []string{"Value", "Name"}
|
cols = []string{"Value", "Name"}
|
||||||
|
|
||||||
rows = [][]string{
|
rows = [][]string{
|
||||||
|
// Authentication.
|
||||||
{"admin-password", strings.Repeat("*", utf8.RuneCountInString(c.AdminPassword()))},
|
{"admin-password", strings.Repeat("*", utf8.RuneCountInString(c.AdminPassword()))},
|
||||||
|
{"auth", fmt.Sprintf("%t", c.Auth())},
|
||||||
|
{"public", fmt.Sprintf("%t", c.Public())},
|
||||||
|
|
||||||
|
// Logging.
|
||||||
{"log-level", c.LogLevel().String()},
|
{"log-level", c.LogLevel().String()},
|
||||||
{"debug", fmt.Sprintf("%t", c.Debug())},
|
{"debug", fmt.Sprintf("%t", c.Debug())},
|
||||||
{"trace", fmt.Sprintf("%t", c.Trace())},
|
{"trace", fmt.Sprintf("%t", c.Trace())},
|
||||||
{"auth", fmt.Sprintf("%t", c.Auth())},
|
|
||||||
{"public", fmt.Sprintf("%t", c.Public())},
|
|
||||||
{"read-only", fmt.Sprintf("%t", c.ReadOnly())},
|
|
||||||
{"experimental", fmt.Sprintf("%t", c.Experimental())},
|
|
||||||
|
|
||||||
// Config.
|
// Config.
|
||||||
{"config-path", c.ConfigPath()},
|
{"config-path", c.ConfigPath()},
|
||||||
|
@ -55,6 +56,8 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
||||||
{"auto-import", fmt.Sprintf("%d", c.AutoImport()/time.Second)},
|
{"auto-import", fmt.Sprintf("%d", c.AutoImport()/time.Second)},
|
||||||
|
|
||||||
// Feature Flags.
|
// Feature Flags.
|
||||||
|
{"read-only", fmt.Sprintf("%t", c.ReadOnly())},
|
||||||
|
{"experimental", fmt.Sprintf("%t", c.Experimental())},
|
||||||
{"disable-backups", fmt.Sprintf("%t", c.DisableBackups())},
|
{"disable-backups", fmt.Sprintf("%t", c.DisableBackups())},
|
||||||
{"disable-settings", fmt.Sprintf("%t", c.DisableSettings())},
|
{"disable-settings", fmt.Sprintf("%t", c.DisableSettings())},
|
||||||
{"disable-places", fmt.Sprintf("%t", c.DisablePlaces())},
|
{"disable-places", fmt.Sprintf("%t", c.DisablePlaces())},
|
||||||
|
@ -79,13 +82,13 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
||||||
{"tensorflow-version", c.TensorFlowVersion()},
|
{"tensorflow-version", c.TensorFlowVersion()},
|
||||||
{"tensorflow-model-path", c.TensorFlowModelPath()},
|
{"tensorflow-model-path", c.TensorFlowModelPath()},
|
||||||
|
|
||||||
// UI Defaults.
|
// Customization.
|
||||||
{"default-locale", c.DefaultLocale()},
|
{"default-locale", c.DefaultLocale()},
|
||||||
|
{"default-theme", c.DefaultTheme()},
|
||||||
// Progressive Web App.
|
|
||||||
{"app-icon", c.AppIcon()},
|
{"app-icon", c.AppIcon()},
|
||||||
{"app-name", c.AppName()},
|
{"app-name", c.AppName()},
|
||||||
{"app-mode", c.AppMode()},
|
{"app-mode", c.AppMode()},
|
||||||
|
{"wallpaper-uri", c.WallpaperUri()},
|
||||||
|
|
||||||
// Site Infos.
|
// Site Infos.
|
||||||
{"cdn-url", c.CdnUrl("/")},
|
{"cdn-url", c.CdnUrl("/")},
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
_ = os.Setenv("PHOTOPRISM_TEST", "true")
|
||||||
log = logrus.StandardLogger()
|
log = logrus.StandardLogger()
|
||||||
log.SetLevel(logrus.TraceLevel)
|
log.SetLevel(logrus.TraceLevel)
|
||||||
|
|
||||||
|
|
28
internal/config/env.go
Normal file
28
internal/config/env.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Environment names.
|
||||||
|
const (
|
||||||
|
EnvDebug = "debug"
|
||||||
|
EnvTrace = "trace"
|
||||||
|
EnvDemo = "demo"
|
||||||
|
EnvSponsor = "sponsor"
|
||||||
|
EnvTest = "test"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Env checks the presence of environment and command-line flags.
|
||||||
|
func Env(vars ...string) bool {
|
||||||
|
for _, s := range vars {
|
||||||
|
if os.Getenv("PHOTOPRISM_"+strings.ToUpper(s)) == "true" || list.Contains(os.Args, "--"+s) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
16
internal/config/env_test.go
Normal file
16
internal/config/env_test.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnv(t *testing.T) {
|
||||||
|
t.Run("True", func(t *testing.T) {
|
||||||
|
assert.True(t, Env(EnvTest))
|
||||||
|
})
|
||||||
|
t.Run("False", func(t *testing.T) {
|
||||||
|
assert.False(t, Env("foo"))
|
||||||
|
})
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -74,6 +74,7 @@ type Options struct {
|
||||||
AppIcon string `yaml:"AppIcon" json:"AppIcon" flag:"app-icon"`
|
AppIcon string `yaml:"AppIcon" json:"AppIcon" flag:"app-icon"`
|
||||||
AppName string `yaml:"AppName" json:"AppName" flag:"app-name"`
|
AppName string `yaml:"AppName" json:"AppName" flag:"app-name"`
|
||||||
AppMode string `yaml:"AppMode" json:"AppMode" flag:"app-mode"`
|
AppMode string `yaml:"AppMode" json:"AppMode" flag:"app-mode"`
|
||||||
|
WallpaperUri string `yaml:"WallpaperUri" json:"WallpaperUri" flag:"wallpaper-uri"`
|
||||||
CdnUrl string `yaml:"CdnUrl" json:"CdnUrl" flag:"cdn-url"`
|
CdnUrl string `yaml:"CdnUrl" json:"CdnUrl" flag:"cdn-url"`
|
||||||
SiteUrl string `yaml:"SiteUrl" json:"SiteUrl" flag:"site-url"`
|
SiteUrl string `yaml:"SiteUrl" json:"SiteUrl" flag:"site-url"`
|
||||||
SiteAuthor string `yaml:"SiteAuthor" json:"SiteAuthor" flag:"site-author"`
|
SiteAuthor string `yaml:"SiteAuthor" json:"SiteAuthor" flag:"site-author"`
|
||||||
|
|
|
@ -200,6 +200,7 @@ func CliTestContext() *cli.Context {
|
||||||
globalSet.String("darktable-cli", config.DarktableBin, "doc")
|
globalSet.String("darktable-cli", config.DarktableBin, "doc")
|
||||||
globalSet.String("darktable-blacklist", config.DarktableBlacklist, "doc")
|
globalSet.String("darktable-blacklist", config.DarktableBlacklist, "doc")
|
||||||
globalSet.String("wakeup-interval", "1h34m9s", "doc")
|
globalSet.String("wakeup-interval", "1h34m9s", "doc")
|
||||||
|
globalSet.Bool("test", true, "doc")
|
||||||
globalSet.Bool("debug", false, "doc")
|
globalSet.Bool("debug", false, "doc")
|
||||||
globalSet.Bool("detect-nsfw", config.DetectNSFW, "doc")
|
globalSet.Bool("detect-nsfw", config.DetectNSFW, "doc")
|
||||||
globalSet.Int("auto-index", config.AutoIndex, "doc")
|
globalSet.Int("auto-index", config.AutoIndex, "doc")
|
||||||
|
@ -224,6 +225,7 @@ func CliTestContext() *cli.Context {
|
||||||
LogError(c.Set("darktable-blacklist", "raf,cr3"))
|
LogError(c.Set("darktable-blacklist", "raf,cr3"))
|
||||||
LogError(c.Set("wakeup-interval", "1h34m9s"))
|
LogError(c.Set("wakeup-interval", "1h34m9s"))
|
||||||
LogError(c.Set("detect-nsfw", "true"))
|
LogError(c.Set("detect-nsfw", "true"))
|
||||||
|
LogError(c.Set("test", "true"))
|
||||||
LogError(c.Set("auto-index", strconv.Itoa(config.AutoIndex)))
|
LogError(c.Set("auto-index", strconv.Itoa(config.AutoIndex)))
|
||||||
LogError(c.Set("auto-import", strconv.Itoa(config.AutoImport)))
|
LogError(c.Set("auto-import", strconv.Itoa(config.AutoImport)))
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestPhotosQueryPortrait(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.GreaterOrEqual(t, len(photos0), 40)
|
assert.GreaterOrEqual(t, len(photos0), 39)
|
||||||
|
|
||||||
t.Run("false > yes", func(t *testing.T) {
|
t.Run("false > yes", func(t *testing.T) {
|
||||||
var f form.SearchPhotos
|
var f form.SearchPhotos
|
||||||
|
|
22
pkg/clean/uri.go
Normal file
22
pkg/clean/uri.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package clean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Uri removes invalid character from an uri string.
|
||||||
|
func Uri(s string) string {
|
||||||
|
if s == "" || reject(s, 512) || strings.Contains(s, "..") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim whitespace.
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
|
||||||
|
if uri, err := url.Parse(s); err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return uri.String()
|
||||||
|
}
|
||||||
|
}
|
18
pkg/clean/uri_test.go
Normal file
18
pkg/clean/uri_test.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package clean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUri(t *testing.T) {
|
||||||
|
t.Run("Valid", func(t *testing.T) {
|
||||||
|
result := Uri("https://docs.photoprism.app/getting-started/config-options/#file-converters")
|
||||||
|
assert.Equal(t, "https://docs.photoprism.app/getting-started/config-options/#file-converters", result)
|
||||||
|
})
|
||||||
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
|
result := Uri("https://..docs.photoprism.app/gettin\\g-started/config-options/\tfile-converters")
|
||||||
|
assert.Equal(t, "", result)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue