mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Pkg: Add service/cluster package & rename media/http → service/http #98
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/ai/tensorflow"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// Model represents a TensorFlow classification model.
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clusters"
|
||||
"github.com/photoprism/photoprism/pkg/vector/alg"
|
||||
)
|
||||
|
||||
// Embedding represents a face embedding.
|
||||
@@ -54,7 +54,7 @@ func (m Embedding) Dist(other Embedding) float64 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return clusters.EuclideanDist(m, other)
|
||||
return alg.EuclideanDist(m, other)
|
||||
}
|
||||
|
||||
// Magnitude returns the face embedding vector length (magnitude).
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/montanaflynn/stats"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clusters"
|
||||
"github.com/photoprism/photoprism/pkg/vector/alg"
|
||||
)
|
||||
|
||||
// Embeddings represents a face embedding cluster.
|
||||
@@ -166,7 +166,7 @@ func EmbeddingsMidpoint(embeddings Embeddings) (result Embedding, radius float64
|
||||
|
||||
// Radius is the max embedding distance + 0.01 from result.
|
||||
for _, emb := range embeddings {
|
||||
if d := clusters.EuclideanDist(result, emb); d > radius {
|
||||
if d := alg.EuclideanDist(result, emb); d > radius {
|
||||
radius = d + 0.01
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// Model uses TensorFlow to label drawing, hentai, neutral, porn and sexy images.
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// PerformApiRequest performs a Vision API request and returns the result.
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
func TestNewApiRequest(t *testing.T) {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// ApiResponseOllama represents a Ollama API service response.
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
type Files = []string
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/ai/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/ai/tensorflow"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
var modelMutex = sync.Mutex{}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
func TestModel(t *testing.T) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package vision
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/ai/tensorflow"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// Default computer vision model configuration.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package vision
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// Service represents a remote computer vision service configuration.
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
//go:embed embed/video.mp4
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Auth checks if the user is authorized to access a resource with the given permission
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/auth/session"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package api
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// ClientIP returns the client IP address from the request context or a placeholder if it is unknown.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// AddCountHeader adds the actual result count to the response.
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Ensure assets path is set so TestMain in this package can initialize config.
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
type ThumbCache struct {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestAddVideoCacheHeader(t *testing.T) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// ClusterGetTheme returns custom theme files as zip, if available.
|
||||
@@ -44,13 +44,13 @@ func ClusterGetTheme(router *gin.RouterGroup) {
|
||||
conf := get.Config()
|
||||
|
||||
// Abort if this is not a portal server.
|
||||
if !conf.ClusterPortal() {
|
||||
if !conf.IsPortal() {
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
}
|
||||
|
||||
clientIp := ClientIP(c)
|
||||
themePath := conf.ClusterThemePath()
|
||||
themePath := conf.PortalThemePath()
|
||||
|
||||
// Resolve symbolic links.
|
||||
if resolved, err := filepath.EvalSymlinks(themePath); err != nil {
|
||||
|
||||
@@ -12,14 +12,15 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/cluster"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestClusterGetTheme(t *testing.T) {
|
||||
t.Run("FeatureDisabled", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
// Ensure portal feature flag is disabled.
|
||||
conf.Options().ClusterPortal = false
|
||||
conf.Options().NodeType = cluster.Instance
|
||||
ClusterGetTheme(router)
|
||||
|
||||
r := PerformRequest(app, http.MethodGet, "/api/v1/cluster/theme")
|
||||
@@ -29,7 +30,7 @@ func TestClusterGetTheme(t *testing.T) {
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
// Enable portal feature flag for this endpoint.
|
||||
conf.Options().ClusterPortal = true
|
||||
conf.Options().NodeType = cluster.Portal
|
||||
ClusterGetTheme(router)
|
||||
|
||||
missing := filepath.Join(os.TempDir(), "photoprism-test-missing-theme")
|
||||
@@ -47,7 +48,7 @@ func TestClusterGetTheme(t *testing.T) {
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
// Enable portal feature flag for this endpoint.
|
||||
conf.Options().ClusterPortal = true
|
||||
conf.Options().NodeType = cluster.Portal
|
||||
ClusterGetTheme(router)
|
||||
|
||||
tempTheme, err := os.MkdirTemp("", "pp-theme-*")
|
||||
@@ -102,7 +103,7 @@ func TestClusterGetTheme(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
app, router, conf := NewApiTest()
|
||||
// Enable portal feature flag for this endpoint.
|
||||
conf.Options().ClusterPortal = true
|
||||
conf.Options().NodeType = cluster.Portal
|
||||
ClusterGetTheme(router)
|
||||
|
||||
// Create an empty temporary theme directory (no includable files).
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
swagger "github.com/swaggo/gin-swagger"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
//go:embed swagger.json
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// GetMetrics provides a Prometheus-compatible metrics stream for monitoring.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// OAuthAuthorize should gather consent and authorization from resource owners when using the
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// OAuthRevoke takes an access token and deletes it. A client may only delete its own tokens.
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestOAuthRevoke(t *testing.T) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// OAuthToken creates a new access token for clients that authenticate with valid OAuth2 client credentials.
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestOAuthToken(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// OAuthUserinfo should return information about the authenticated user,
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// OIDCLogin redirects a browser to the login page of the configured OpenID Connect provider, if any.
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
"github.com/photoprism/photoprism/pkg/time/unix"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// CreateSession creates a new client session and returns it as JSON if authentication was successful.
|
||||
|
||||
@@ -3,7 +3,7 @@ package api
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// GetSession returns the session data as JSON if authentication was successful.
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/thumb/avatar"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// UploadUserAvatar updates the avatar image of the currently authenticated user.
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// CreateUserPasscode sets up a new two-factor authentication passcode.
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// GetVideo returns a video, optionally limited to a byte range for streaming.
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestGetVideo(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// PostVisionCaption returns a suitable caption for an image.
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// PostVisionFace returns the embeddings of a face.
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/vision"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
func TestPostVisionFace(t *testing.T) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// PostVisionLabels returns suitable labels for an image.
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/vision"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
func TestPostVisionLabels(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// PostVisionNsfw checks the specified images for inappropriate content.
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/vision"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
func TestPostVisionNsfw(t *testing.T) {
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// DownloadCommand configures the command name, flags, and action.
|
||||
|
||||
@@ -63,6 +63,11 @@ func (c *Config) AuthMode() string {
|
||||
}
|
||||
}
|
||||
|
||||
// AuthSecret returns the key for signing authentication tokens, if specified.
|
||||
func (c *Config) AuthSecret() string {
|
||||
return c.options.AuthSecret
|
||||
}
|
||||
|
||||
// Public checks if app runs in public mode and requires no authentication.
|
||||
func (c *Config) Public() bool {
|
||||
return c.AuthMode() == AuthModePublic
|
||||
|
||||
@@ -51,6 +51,15 @@ func TestAuthMode(t *testing.T) {
|
||||
c.options.Debug = false
|
||||
}
|
||||
|
||||
func TestAuthSecret(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
assert.Equal(t, "", c.AuthSecret())
|
||||
c.options.AuthSecret = "341e1657d37759410de1ae628b95dbaa"
|
||||
assert.Equal(t, "341e1657d37759410de1ae628b95dbaa", c.AuthSecret())
|
||||
c.options.AuthSecret = ""
|
||||
assert.Equal(t, "", c.AuthSecret())
|
||||
}
|
||||
|
||||
func TestConfig_AdminPassword(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestConfig_CdnUrl(t *testing.T) {
|
||||
|
||||
@@ -1,19 +1,41 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/service/cluster"
|
||||
)
|
||||
|
||||
// NodeName returns the human-readable instance name, if specified.
|
||||
// NodeName returns the unique name of this node within the cluster (lowercase letters and numbers only).
|
||||
func (c *Config) NodeName() string {
|
||||
return c.options.NodeName
|
||||
return clean.TypeLowerDash(c.options.NodeName)
|
||||
}
|
||||
|
||||
// NodeSecret returns the node's authentication secret, if specified.
|
||||
// NodeType returns the type of this node for cluster operation (portal, instance, service).
|
||||
func (c *Config) NodeType() string {
|
||||
switch c.options.NodeType {
|
||||
case cluster.Portal, cluster.Instance, cluster.Service:
|
||||
return c.options.NodeType
|
||||
default:
|
||||
return cluster.Instance
|
||||
}
|
||||
}
|
||||
|
||||
// NodeSecret returns the private node key for intra-cluster communication.
|
||||
func (c *Config) NodeSecret() string {
|
||||
if c.options.NodeSecret != "" {
|
||||
return c.options.NodeSecret
|
||||
} else if fileName := FlagFilePath("NODE_SECRET"); fileName == "" {
|
||||
return ""
|
||||
} else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 {
|
||||
log.Warnf("config: failed to read node secret from %s (%s)", fileName, err)
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
// PortalUrl returns the URL of the cluster portal server, if configured.
|
||||
@@ -21,35 +43,39 @@ func (c *Config) PortalUrl() string {
|
||||
return c.options.PortalUrl
|
||||
}
|
||||
|
||||
// PortalClient returns the portal client ID, if configured.
|
||||
func (c *Config) PortalClient() string {
|
||||
return c.options.PortalClient
|
||||
// PortalToken returns the token required to access the portal API endpoints.
|
||||
func (c *Config) PortalToken() string {
|
||||
if c.options.PortalToken != "" {
|
||||
return c.options.PortalToken
|
||||
} else if fileName := FlagFilePath("PORTAL_TOKEN"); fileName == "" {
|
||||
return ""
|
||||
} else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 {
|
||||
log.Warnf("config: failed to read portal token from %s (%s)", fileName, err)
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// PortalSecret returns the portal client secret, if configured.
|
||||
func (c *Config) PortalSecret() string {
|
||||
return c.options.PortalSecret
|
||||
}
|
||||
|
||||
// ClusterPortal returns true if this instance should act as a cluster portal.
|
||||
func (c *Config) ClusterPortal() bool {
|
||||
return c.options.ClusterPortal
|
||||
return c.IsPortal()
|
||||
}
|
||||
|
||||
// ClusterNode returns true if this instance should be configured as a cluster node.
|
||||
func (c *Config) ClusterNode() bool {
|
||||
return c.options.ClusterNode
|
||||
// IsPortal returns true if the configured node type is "portal".
|
||||
func (c *Config) IsPortal() bool {
|
||||
return c.NodeType() == cluster.Portal
|
||||
}
|
||||
|
||||
// ClusterConfigPath returns the path to portal config files.
|
||||
func (c *Config) ClusterConfigPath() string {
|
||||
// PortalConfigPath returns the path to the default configuration for cluster nodes.
|
||||
func (c *Config) PortalConfigPath() string {
|
||||
return filepath.Join(c.ConfigPath(), fs.ClusterDir)
|
||||
}
|
||||
|
||||
// ClusterThemePath returns the path to the shared theme files.
|
||||
func (c *Config) ClusterThemePath() string {
|
||||
// PortalThemePath returns the path to the theme files for cluster nodes to use.
|
||||
func (c *Config) PortalThemePath() string {
|
||||
// Prefer the cluster-specific theme directory if it exists.
|
||||
if dir := filepath.Join(c.ClusterConfigPath(), fs.ThemeDir); fs.PathExists(dir) {
|
||||
if dir := filepath.Join(c.PortalConfigPath(), fs.ThemeDir); fs.PathExists(dir) {
|
||||
return dir
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/service/cluster"
|
||||
)
|
||||
|
||||
func TestConfig_Cluster(t *testing.T) {
|
||||
@@ -16,13 +17,13 @@ func TestConfig_Cluster(t *testing.T) {
|
||||
|
||||
// Defaults
|
||||
assert.False(t, c.ClusterPortal())
|
||||
assert.False(t, c.ClusterNode())
|
||||
assert.False(t, c.IsPortal())
|
||||
|
||||
// Toggle values
|
||||
c.options.ClusterPortal = true
|
||||
c.options.ClusterNode = true
|
||||
c.Options().NodeType = string(cluster.Portal)
|
||||
assert.True(t, c.ClusterPortal())
|
||||
assert.True(t, c.ClusterNode())
|
||||
assert.True(t, c.IsPortal())
|
||||
c.Options().NodeType = ""
|
||||
})
|
||||
|
||||
t.Run("Paths", func(t *testing.T) {
|
||||
@@ -32,22 +33,22 @@ func TestConfig_Cluster(t *testing.T) {
|
||||
tempCfg := t.TempDir()
|
||||
c.options.ConfigPath = tempCfg
|
||||
|
||||
// ClusterConfigPath always points to a "cluster" subfolder under ConfigPath.
|
||||
// PortalConfigPath always points to a "cluster" subfolder under ConfigPath.
|
||||
expectedCluster := filepath.Join(c.ConfigPath(), fs.ClusterDir)
|
||||
assert.Equal(t, expectedCluster, c.ClusterConfigPath())
|
||||
assert.Equal(t, expectedCluster, c.PortalConfigPath())
|
||||
|
||||
// ClusterThemePath falls back to ThemePath if cluster dir does not exist.
|
||||
// PortalThemePath falls back to ThemePath if cluster dir does not exist.
|
||||
expectedTheme := filepath.Join(c.ConfigPath(), fs.ThemeDir)
|
||||
assert.Equal(t, expectedTheme, c.ClusterThemePath())
|
||||
assert.Equal(t, expectedTheme, c.PortalThemePath())
|
||||
|
||||
// When only the cluster directory exists (without a theme subfolder), it still falls back to ThemePath.
|
||||
assert.NoError(t, os.MkdirAll(expectedCluster, 0o755))
|
||||
assert.Equal(t, expectedTheme, c.ClusterThemePath())
|
||||
assert.Equal(t, expectedTheme, c.PortalThemePath())
|
||||
|
||||
// When the cluster theme directory exists, ClusterThemePath returns it.
|
||||
// When the cluster theme directory exists, PortalThemePath returns it.
|
||||
expectedClusterTheme := filepath.Join(expectedCluster, fs.ThemeDir)
|
||||
assert.NoError(t, os.MkdirAll(expectedClusterTheme, 0o755))
|
||||
assert.Equal(t, expectedClusterTheme, c.ClusterThemePath())
|
||||
assert.Equal(t, expectedClusterTheme, c.PortalThemePath())
|
||||
})
|
||||
|
||||
t.Run("PortalAndSecrets", func(t *testing.T) {
|
||||
@@ -55,19 +56,16 @@ func TestConfig_Cluster(t *testing.T) {
|
||||
|
||||
// Defaults
|
||||
assert.Equal(t, "", c.PortalUrl())
|
||||
assert.Equal(t, "", c.PortalClient())
|
||||
assert.Equal(t, "", c.PortalSecret())
|
||||
assert.Equal(t, "", c.PortalToken())
|
||||
assert.Equal(t, "", c.NodeSecret())
|
||||
|
||||
// Set and read back values
|
||||
c.options.PortalUrl = "https://portal.example.test"
|
||||
c.options.PortalClient = "client-id"
|
||||
c.options.PortalSecret = "client-secret"
|
||||
c.options.PortalToken = "portal-token"
|
||||
c.options.NodeSecret = "node-secret"
|
||||
|
||||
assert.Equal(t, "https://portal.example.test", c.PortalUrl())
|
||||
assert.Equal(t, "client-id", c.PortalClient())
|
||||
assert.Equal(t, "client-secret", c.PortalSecret())
|
||||
assert.Equal(t, "portal-token", c.PortalToken())
|
||||
assert.Equal(t, "node-secret", c.NodeSecret())
|
||||
})
|
||||
|
||||
@@ -79,12 +77,65 @@ func TestConfig_Cluster(t *testing.T) {
|
||||
// ThemePath should be absolute.
|
||||
assert.True(t, filepath.IsAbs(c.ThemePath()))
|
||||
|
||||
// ClusterThemePath should be absolute (fallback case).
|
||||
assert.True(t, filepath.IsAbs(c.ClusterThemePath()))
|
||||
// PortalThemePath should be absolute (fallback case).
|
||||
assert.True(t, filepath.IsAbs(c.PortalThemePath()))
|
||||
|
||||
// Create cluster theme directory and verify again.
|
||||
clusterTheme := filepath.Join(c.ClusterConfigPath(), fs.ThemeDir)
|
||||
clusterTheme := filepath.Join(c.PortalConfigPath(), fs.ThemeDir)
|
||||
assert.NoError(t, os.MkdirAll(clusterTheme, 0o755))
|
||||
assert.True(t, filepath.IsAbs(c.ClusterThemePath()))
|
||||
assert.True(t, filepath.IsAbs(c.PortalThemePath()))
|
||||
})
|
||||
|
||||
t.Run("NodeName", func(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
c.options.NodeName = " Client Credentials幸"
|
||||
assert.Equal(t, "client-credentials", c.NodeName())
|
||||
c.options.NodeName = ""
|
||||
assert.Equal(t, "", c.NodeName())
|
||||
})
|
||||
|
||||
t.Run("NodeTypeValues", func(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
// Default / unknown → node
|
||||
c.options.NodeType = ""
|
||||
assert.Equal(t, string(cluster.Instance), c.NodeType())
|
||||
c.options.NodeType = "unknown"
|
||||
assert.Equal(t, string(cluster.Instance), c.NodeType())
|
||||
|
||||
// Explicit values
|
||||
c.options.NodeType = string(cluster.Instance)
|
||||
assert.Equal(t, string(cluster.Instance), c.NodeType())
|
||||
c.options.NodeType = string(cluster.Portal)
|
||||
assert.Equal(t, string(cluster.Portal), c.NodeType())
|
||||
c.options.NodeType = string(cluster.Service)
|
||||
assert.Equal(t, string(cluster.Service), c.NodeType())
|
||||
})
|
||||
|
||||
t.Run("SecretsFromFiles", func(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
// Create temp secret/token files.
|
||||
dir := t.TempDir()
|
||||
nsFile := filepath.Join(dir, "node_secret")
|
||||
tkFile := filepath.Join(dir, "portal_token")
|
||||
assert.NoError(t, os.WriteFile(nsFile, []byte("s3cr3t"), 0o600))
|
||||
assert.NoError(t, os.WriteFile(tkFile, []byte("t0k3n"), 0o600))
|
||||
|
||||
// Clear inline values so file-based lookup is used.
|
||||
c.options.NodeSecret = ""
|
||||
c.options.PortalToken = ""
|
||||
|
||||
// Point env vars at the files and verify.
|
||||
t.Setenv("PHOTOPRISM_NODE_SECRET_FILE", nsFile)
|
||||
t.Setenv("PHOTOPRISM_PORTAL_TOKEN_FILE", tkFile)
|
||||
assert.Equal(t, "s3cr3t", c.NodeSecret())
|
||||
assert.Equal(t, "t0k3n", c.PortalToken())
|
||||
|
||||
// Empty / missing should yield empty strings.
|
||||
t.Setenv("PHOTOPRISM_NODE_SECRET_FILE", filepath.Join(dir, "missing"))
|
||||
t.Setenv("PHOTOPRISM_PORTAL_TOKEN_FILE", filepath.Join(dir, "missing"))
|
||||
assert.Equal(t, "", c.NodeSecret())
|
||||
assert.Equal(t, "", c.PortalToken())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/config/ttl"
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config/ttl"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
@@ -32,6 +32,12 @@ var Flags = CliFlags{
|
||||
Value: "password",
|
||||
EnvVars: EnvVars("AUTH_MODE"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "auth-secret",
|
||||
Usage: "secret `KEY` for signing authentication tokens",
|
||||
EnvVars: EnvVars("AUTH_SECRET"),
|
||||
Hidden: true,
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "public",
|
||||
Aliases: []string{"p"},
|
||||
@@ -593,11 +599,16 @@ var Flags = CliFlags{
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "site-url",
|
||||
Aliases: []string{"url"},
|
||||
Usage: "public site `URL`",
|
||||
Usage: "public site `URL` used to build links and determine HTTPS/TLS; must include scheme (http/https)",
|
||||
Value: "http://localhost:2342/",
|
||||
EnvVars: EnvVars("SITE_URL"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "internal-url",
|
||||
Usage: "internal server `URL` for service-to-service communication and local networking *optional*",
|
||||
Value: "",
|
||||
EnvVars: EnvVars("INTERNAL_URL"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "site-author",
|
||||
Usage: "site `OWNER`, copyright, or artist",
|
||||
@@ -631,12 +642,6 @@ var Flags = CliFlags{
|
||||
Usage: "sharing preview image `URL`",
|
||||
EnvVars: EnvVars("SITE_PREVIEW"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "internal-url",
|
||||
Usage: "internal site `URL` to be used for local networking *optional*",
|
||||
Value: "",
|
||||
EnvVars: EnvVars("INTERNAL_URL"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "cdn-url",
|
||||
Usage: "content delivery network `URL`",
|
||||
@@ -665,6 +670,35 @@ var Flags = CliFlags{
|
||||
EnvVars: EnvVars("CORS_METHODS"),
|
||||
Value: header.DefaultAccessControlAllowMethods,
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "node-name",
|
||||
Usage: "cluster node `NAME` (lowercase letters, digits, hyphens; 1–63 chars)",
|
||||
EnvVars: EnvVars("NODE_NAME"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "node-type",
|
||||
Usage: "cluster node `TYPE` (portal, instance, service)",
|
||||
EnvVars: EnvVars("NODE_TYPE"),
|
||||
Hidden: true,
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "node-secret",
|
||||
Usage: "private `KEY` to secure intra-cluster communication *optional*",
|
||||
EnvVars: EnvVars("NODE_SECRET"),
|
||||
Hidden: true,
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "portal-url",
|
||||
Usage: "base `URL` of the cluster portal e.g. https://portal.example.com",
|
||||
EnvVars: EnvVars("PORTAL_URL"),
|
||||
Hidden: true,
|
||||
}, Tags: []string{Pro}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "portal-token",
|
||||
Usage: "access TOKEN for nodes to register and synchronize with the portal",
|
||||
EnvVars: EnvVars("PORTAL_TOKEN"),
|
||||
Hidden: true,
|
||||
}, Tags: []string{Pro}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "https-proxy",
|
||||
Usage: "proxy server `URL` to be used for outgoing connections *optional*",
|
||||
@@ -1108,46 +1142,6 @@ var Flags = CliFlags{
|
||||
Value: face.MatchDist,
|
||||
EnvVars: EnvVars("FACE_MATCH_DIST"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "node-name",
|
||||
Usage: "unique `NAME` for this cluster node (lowercase, no spaces or special characters)",
|
||||
EnvVars: EnvVars("NODE_NAME"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "node-secret",
|
||||
Usage: "unique `SECRET` for authenticating this cluster node",
|
||||
EnvVars: EnvVars("NODE_SECRET"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "portal-url",
|
||||
Usage: "base `URL` of the cluster portal server e.g. https://portal.example.com",
|
||||
EnvVars: EnvVars("PORTAL_URL"),
|
||||
Hidden: true,
|
||||
}, Tags: []string{Pro}}, {
|
||||
/* Flag: &cli.StringFlag{
|
||||
Name: "portal-client",
|
||||
Usage: "OAuth2 client `ID` for joining a cluster *optional*",
|
||||
EnvVars: EnvVars("PORTAL_CLIENT"),
|
||||
Hidden: true,
|
||||
}, Tags: []string{Pro}}, {*/
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "portal-token",
|
||||
Usage: "access `TOKEN` for authenticating to the portal server (may be shared between nodes)",
|
||||
EnvVars: EnvVars("PORTAL_TOKEN"),
|
||||
Hidden: true,
|
||||
}, Tags: []string{Pro}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "cluster-node",
|
||||
Usage: "runs this instance as a cluster node and joins the configured portal",
|
||||
EnvVars: EnvVars("CLUSTER_NODE"),
|
||||
Hidden: true,
|
||||
}, Tags: []string{Pro}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "cluster-portal",
|
||||
Usage: "runs this instance as a cluster portal for orchestrating nodes",
|
||||
EnvVars: EnvVars("CLUSTER_PORTAL"),
|
||||
Hidden: true,
|
||||
}, Tags: []string{Pro}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "pid-filename",
|
||||
Usage: "process id `FILENAME` *daemon-mode only*",
|
||||
|
||||
@@ -25,6 +25,7 @@ type Options struct {
|
||||
Copyright string `json:"-"`
|
||||
PartnerID string `yaml:"-" json:"-" flag:"partner-id"`
|
||||
AuthMode string `yaml:"AuthMode" json:"-" flag:"auth-mode"`
|
||||
AuthSecret string `yaml:"AuthSecret" json:"-" flag:"auth-secret"`
|
||||
Public bool `yaml:"Public" json:"-" flag:"public"`
|
||||
NoHub bool `yaml:"-" json:"-" flag:"no-hub"`
|
||||
AdminUser string `yaml:"AdminUser" json:"-" flag:"admin-user"`
|
||||
@@ -130,18 +131,24 @@ type Options struct {
|
||||
LegalUrl string `yaml:"LegalUrl" json:"LegalUrl" flag:"legal-url"`
|
||||
WallpaperUri string `yaml:"WallpaperUri" json:"WallpaperUri" flag:"wallpaper-uri"`
|
||||
SiteUrl string `yaml:"SiteUrl" json:"SiteUrl" flag:"site-url"`
|
||||
InternalUrl string `yaml:"InternalUrl" json:"InternalUrl" flag:"internal-url"`
|
||||
SiteAuthor string `yaml:"SiteAuthor" json:"SiteAuthor" flag:"site-author"`
|
||||
SiteTitle string `yaml:"SiteTitle" json:"SiteTitle" flag:"site-title"`
|
||||
SiteCaption string `yaml:"SiteCaption" json:"SiteCaption" flag:"site-caption"`
|
||||
SiteDescription string `yaml:"SiteDescription" json:"SiteDescription" flag:"site-description"`
|
||||
SiteFavicon string `yaml:"SiteFavicon" json:"SiteFavicon" flag:"site-favicon"`
|
||||
SitePreview string `yaml:"SitePreview" json:"SitePreview" flag:"site-preview"`
|
||||
InternalUrl string `yaml:"InternalUrl" json:"InternalUrl" flag:"internal-url"`
|
||||
CdnUrl string `yaml:"CdnUrl" json:"CdnUrl" flag:"cdn-url"`
|
||||
CdnVideo bool `yaml:"CdnVideo" json:"CdnVideo" flag:"cdn-video"`
|
||||
CORSOrigin string `yaml:"CORSOrigin" json:"-" flag:"cors-origin"`
|
||||
CORSHeaders string `yaml:"CORSHeaders" json:"-" flag:"cors-headers"`
|
||||
CORSMethods string `yaml:"CORSMethods" json:"-" flag:"cors-methods"`
|
||||
NodeName string `yaml:"NodeName" json:"-" flag:"node-name"`
|
||||
NodeType string `yaml:"NodeType" json:"-" flag:"node-type"`
|
||||
NodeSecret string `yaml:"NodeSecret" json:"-" flag:"node-secret"`
|
||||
PortalUrl string `yaml:"PortalUrl" json:"-" flag:"portal-url"`
|
||||
PortalClient string `yaml:"PortalClient" json:"-" flag:"portal-client"`
|
||||
PortalToken string `yaml:"PortalToken" json:"-" flag:"portal-token"`
|
||||
HttpsProxy string `yaml:"HttpsProxy" json:"HttpsProxy" flag:"https-proxy"`
|
||||
HttpsProxyInsecure bool `yaml:"HttpsProxyInsecure" json:"HttpsProxyInsecure" flag:"https-proxy-insecure"`
|
||||
TrustedPlatform string `yaml:"TrustedPlatform" json:"-" flag:"trusted-platform"`
|
||||
@@ -218,13 +225,6 @@ type Options struct {
|
||||
FaceClusterCore int `yaml:"-" json:"-" flag:"face-cluster-core"`
|
||||
FaceClusterDist float64 `yaml:"-" json:"-" flag:"face-cluster-dist"`
|
||||
FaceMatchDist float64 `yaml:"-" json:"-" flag:"face-match-dist"`
|
||||
NodeName string `yaml:"NodeName" json:"-" flag:"node-name"`
|
||||
NodeSecret string `yaml:"NodeSecret" json:"-" flag:"node-secret"`
|
||||
PortalUrl string `yaml:"PortalUrl" json:"-" flag:"portal-url"`
|
||||
PortalClient string `yaml:"PortalClient" json:"-" flag:"portal-client"`
|
||||
PortalSecret string `yaml:"PortalSecret" json:"-" flag:"portal-secret"`
|
||||
ClusterNode bool `yaml:"ClusterNode" json:"-" flag:"cluster-node"`
|
||||
ClusterPortal bool `yaml:"ClusterPortal" json:"-" flag:"cluster-portal"`
|
||||
PIDFilename string `yaml:"PIDFilename" json:"-" flag:"pid-filename"`
|
||||
LogFilename string `yaml:"LogFilename" json:"-" flag:"log-filename"`
|
||||
DetachServer bool `yaml:"DetachServer" json:"-" flag:"detach-server"`
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Icons represents a list of app icons.
|
||||
|
||||
@@ -152,6 +152,7 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
||||
|
||||
// Site Infos.
|
||||
{"site-url", c.SiteUrl()},
|
||||
{"internal-url", c.InternalUrl()},
|
||||
{"site-https", fmt.Sprintf("%t", c.SiteHttps())},
|
||||
{"site-domain", c.SiteDomain()},
|
||||
{"site-author", c.SiteAuthor()},
|
||||
@@ -160,7 +161,15 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
||||
{"site-description", c.SiteDescription()},
|
||||
{"site-favicon", c.SiteFavicon()},
|
||||
{"site-preview", c.SitePreview()},
|
||||
{"internal-url", c.InternalUrl()},
|
||||
|
||||
// Cluster Configuration.
|
||||
{"node-name", c.NodeName()},
|
||||
{"node-type", c.NodeType()},
|
||||
{"node-secret", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.NodeSecret())))},
|
||||
{"portal-url", c.PortalUrl()},
|
||||
{"portal-token", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.PortalToken())))},
|
||||
{"portal-config-path", c.PortalConfigPath()},
|
||||
{"portal-theme-path", c.PortalThemePath()},
|
||||
|
||||
// CDN and Cross-Origin Resource Sharing (CORS).
|
||||
{"cdn-url", c.CdnUrl("/")},
|
||||
@@ -272,17 +281,6 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
||||
{"face-cluster-dist", fmt.Sprintf("%f", c.FaceClusterDist())},
|
||||
{"face-match-dist", fmt.Sprintf("%f", c.FaceMatchDist())},
|
||||
|
||||
// Cluster Configuration.
|
||||
{"node-name", c.NodeName()},
|
||||
{"node-secret", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.NodeSecret())))},
|
||||
{"portal-url", c.PortalUrl()},
|
||||
{"portal-client", c.PortalClient()},
|
||||
{"portal-secret", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.PortalSecret())))},
|
||||
{"cluster-node", fmt.Sprintf("%t", c.ClusterNode())},
|
||||
{"cluster-portal", fmt.Sprintf("%t", c.ClusterPortal())},
|
||||
{"cluster-config-path", c.ClusterConfigPath()},
|
||||
{"cluster-theme-path", c.ClusterThemePath()},
|
||||
|
||||
// Daemon Mode.
|
||||
{"pid-filename", c.PIDFilename()},
|
||||
{"log-filename", c.LogFilename()},
|
||||
|
||||
@@ -25,6 +25,7 @@ var OptionsReportSections = []ReportSection{
|
||||
{Start: "PHOTOPRISM_READONLY", Title: "Feature Flags"},
|
||||
{Start: "PHOTOPRISM_DEFAULT_LOCALE", Title: "Customization"},
|
||||
{Start: "PHOTOPRISM_SITE_URL", Title: "Site Information"},
|
||||
{Start: "PHOTOPRISM_NODE_NAME", Title: "Cluster Configuration"},
|
||||
{Start: "PHOTOPRISM_HTTPS_PROXY", Title: "Proxy Server"},
|
||||
{Start: "PHOTOPRISM_DISABLE_TLS", Title: "Web Server"},
|
||||
{Start: "PHOTOPRISM_DATABASE_DRIVER", Title: "Database Connection"},
|
||||
@@ -35,7 +36,6 @@ var OptionsReportSections = []ReportSection{
|
||||
{Start: "PHOTOPRISM_VISION_YAML", Title: "Computer Vision"},
|
||||
{Start: "PHOTOPRISM_FACE_SIZE", Title: "Face Recognition",
|
||||
Info: faceFlagsInfo},
|
||||
{Start: "PHOTOPRISM_NODE_NAME", Title: "Cluster Configuration"},
|
||||
{Start: "PHOTOPRISM_PID_FILENAME", Title: "Daemon Mode",
|
||||
Info: "If you start the server as a *daemon* in the background, you can additionally specify a filename for the log and the process ID:"},
|
||||
}
|
||||
@@ -52,6 +52,7 @@ var YamlReportSections = []ReportSection{
|
||||
{Start: "ReadOnly", Title: "Feature Flags"},
|
||||
{Start: "DefaultLocale", Title: "Customization"},
|
||||
{Start: "SiteUrl", Title: "Site Information"},
|
||||
{Start: "NodeName", Title: "Cluster Configuration"},
|
||||
{Start: "HttpsProxy", Title: "Proxy Server"},
|
||||
{Start: "DisableTLS", Title: "Web Server"},
|
||||
{Start: "DatabaseDriver", Title: "Database Connection"},
|
||||
@@ -60,7 +61,6 @@ var YamlReportSections = []ReportSection{
|
||||
{Start: "ThumbLibrary", Title: "Preview Images"},
|
||||
{Start: "JpegQuality", Title: "Image Quality"},
|
||||
{Start: "VisionYaml", Title: "Computer Vision"},
|
||||
{Start: "NodeName", Title: "Cluster Configuration"},
|
||||
{Start: "PIDFilename", Title: "Daemon Mode",
|
||||
Info: "If you start the server as a *daemon* in the background, you can additionally specify a filename for the log and the process ID:"},
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/list"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/time/unix"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
"github.com/photoprism/photoprism/pkg/txt/report"
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/time/unix"
|
||||
"github.com/photoprism/photoprism/pkg/txt/report"
|
||||
)
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/colors"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/projection"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestFile_RegenerateIndex(t *testing.T) {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestPhoto_Ids(t *testing.T) {
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/projection"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// ToImage converts a media file to a directly supported image file format.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/ai/face"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/entity/query"
|
||||
"github.com/photoprism/photoprism/pkg/clusters"
|
||||
"github.com/photoprism/photoprism/pkg/vector/alg"
|
||||
)
|
||||
|
||||
// Cluster clusters indexed face embeddings.
|
||||
@@ -37,10 +37,10 @@ func (w *Faces) Cluster(opt FacesOptions) (added entity.Faces, err error) {
|
||||
log.Debugf("faces: at least %d samples needed for clustering", opt.SampleThreshold())
|
||||
return added, nil
|
||||
} else {
|
||||
var c clusters.HardClusterer
|
||||
var c alg.HardClusterer
|
||||
|
||||
// See https://dl.photoprism.app/research/ for research on face clustering algorithms.
|
||||
if c, err = clusters.DBSCAN(face.ClusterCore, face.ClusterDist, w.conf.IndexWorkers(), clusters.EuclideanDist); err != nil {
|
||||
if c, err = alg.DBSCAN(face.ClusterCore, face.ClusterDist, w.conf.IndexWorkers(), alg.EuclideanDist); err != nil {
|
||||
return added, err
|
||||
} else if err = c.Learn(embeddings.Float64()); err != nil {
|
||||
return added, err
|
||||
|
||||
@@ -32,8 +32,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/meta"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/projection"
|
||||
"github.com/photoprism/photoprism/pkg/media/video"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/time/tz"
|
||||
)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestMediaFile_Ok(t *testing.T) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Api is a middleware that sets additional response headers when serving REST API requests.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/api"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// registerStaticRoutes adds routes for serving static content and templates.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/api"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// registerWebAppRoutes adds routes for the web user interface.
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/internal/api"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Security is a middleware that adds security-related headers to the server's response.
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/server/process"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Static is a middleware that adds static content-related headers to the server's response.
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/workers/auto"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Use auth cache to improve WebDAV performance. It has a standard expiration time of about 5 minutes.
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/server/limiter"
|
||||
"github.com/photoprism/photoprism/pkg/authn"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// WebDAVAuthSession returns the client session that belongs to the auth token provided, or returns nil if it was not found.
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestWebDAVAuth(t *testing.T) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/service/hub/places"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
type Status string
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// GetRequest fetches the cell ID data from the service URL.
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// SetUserImageURL sets a new user avatar URL.
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/disintegration/imaging"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/disintegration/imaging"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/disintegration/imaging"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package clean
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// ContentType normalizes media content type strings, see https://en.wikipedia.org/wiki/Media_type.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// TypeAnimated maps animated file types to their mime type.
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestDetectMimeType(t *testing.T) {
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/scheme"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/scheme"
|
||||
)
|
||||
|
||||
// DataUrl generates a data URL of the binary data from the specified io.Reader.
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// ContentType returns a normalized video content type strings based on the video file type and codec.
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestContentType(t *testing.T) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// Info represents video file information.
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
// ProbeFile returns information for the given filename.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
"github.com/photoprism/photoprism/pkg/service/http/header"
|
||||
)
|
||||
|
||||
func TestProbeFile(t *testing.T) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user