API: Move "service/cluster" package from "pkg" to "internal" #98

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-09-15 13:40:56 +02:00
parent ea8d413deb
commit a4cd1ac1fd
14 changed files with 79 additions and 81 deletions

View File

@@ -9,9 +9,9 @@ import (
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/photoprism/get"
"github.com/photoprism/photoprism/internal/service/cluster"
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/service/cluster"
"github.com/photoprism/photoprism/pkg/txt"
)
@@ -103,9 +103,9 @@ func ClusterListNodes(router *gin.RouterGroup) {
page := items[offset:end]
// Build response with session-based redaction.
opts := ClusterNodeOptionsForSession(s)
opts := reg.NodeOptsForSession(s)
resp := BuildClusterNodes(page, opts)
resp := reg.BuildClusterNodes(page, opts)
// Audit list access.
event.AuditInfo([]string{ClientIP(c), "session %s", string(acl.ResourceCluster), "nodes", "list", event.Succeeded, "count=%d", "offset=%d", "returned=%d"}, s.RefID, count, offset, len(resp))
@@ -162,8 +162,8 @@ func ClusterGetNode(router *gin.RouterGroup) {
}
// Build response with session-based redaction.
opts := ClusterNodeOptionsForSession(s)
resp := BuildClusterNode(*n, opts)
opts := reg.NodeOptsForSession(s)
resp := reg.BuildClusterNode(*n, opts)
// Audit get access.
event.AuditInfo([]string{ClientIP(c), "session %s", string(acl.ResourceCluster), "nodes", "get", n.ID, event.Succeeded}, s.RefID)

View File

@@ -10,11 +10,11 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/photoprism/get"
"github.com/photoprism/photoprism/internal/server/limiter"
"github.com/photoprism/photoprism/internal/service/cluster"
"github.com/photoprism/photoprism/internal/service/cluster/provisioner"
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/service/cluster"
"github.com/photoprism/photoprism/pkg/service/http/header"
)
@@ -134,9 +134,9 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
}
// Build response with struct types.
opts := ClusterNodeOptionsForSession(nil) // registration is token-based, not session; default redaction is fine
opts := reg.NodeOptsForSession(nil) // registration is token-based, not session; default redaction is fine
resp := cluster.RegisterResponse{
Node: BuildClusterNode(*n, opts),
Node: reg.BuildClusterNode(*n, opts),
DB: cluster.RegisterDB{Host: conf.DatabaseHost(), Port: conf.DatabasePort(), Name: n.DB.Name, User: n.DB.User},
Secrets: respSecret,
AlreadyRegistered: true,
@@ -184,7 +184,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
}
resp := cluster.RegisterResponse{
Node: BuildClusterNode(*n, ClusterNodeOptionsForSession(nil)),
Node: reg.BuildClusterNode(*n, reg.NodeOptsForSession(nil)),
Secrets: &cluster.RegisterSecrets{NodeSecret: n.Secret, NodeSecretLastRotatedAt: n.SecretRot},
DB: cluster.RegisterDB{Host: conf.DatabaseHost(), Port: conf.DatabasePort(), Name: creds.Name, User: creds.User, Password: creds.Password, DSN: creds.DSN, DBLastRotatedAt: creds.LastRotatedAt},
AlreadyRegistered: false,

View File

@@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/photoprism/photoprism/internal/service/cluster"
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
"github.com/photoprism/photoprism/pkg/service/cluster"
)
func TestClusterNodesRegister(t *testing.T) {

View File

@@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/photoprism/photoprism/internal/service/cluster"
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
"github.com/photoprism/photoprism/pkg/service/cluster"
)
func TestClusterEndpoints(t *testing.T) {

View File

@@ -8,12 +8,11 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/service/cluster"
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/service/cluster"
"github.com/photoprism/photoprism/pkg/service/http/header"
)

View File

@@ -1,64 +0,0 @@
package api
import (
entitypkg "github.com/photoprism/photoprism/internal/entity"
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
"github.com/photoprism/photoprism/pkg/service/cluster"
)
// ClusterNodeOptions controls which optional fields get included in responses.
type ClusterNodeOptions struct {
IncludeInternalURL bool
IncludeDBMeta bool
}
// ClusterNodeOptionsForSession returns the default exposure policy for a session.
// Admin users see internalUrl and DB metadata; others get a redacted view.
func ClusterNodeOptionsForSession(s *entitypkg.Session) ClusterNodeOptions {
if s != nil && s.User() != nil && s.User().IsAdmin() {
return ClusterNodeOptions{IncludeInternalURL: true, IncludeDBMeta: true}
}
return ClusterNodeOptions{}
}
// BuildClusterNode builds a ClusterNode DTO from a registry.Node with redaction according to opts.
func BuildClusterNode(n reg.Node, opts ClusterNodeOptions) cluster.Node {
out := cluster.Node{
ID: n.ID,
Name: n.Name,
Type: n.Type,
Labels: n.Labels,
CreatedAt: n.CreatedAt,
UpdatedAt: n.UpdatedAt,
}
if opts.IncludeInternalURL && n.Internal != "" {
out.InternalURL = n.Internal
}
if opts.IncludeDBMeta {
out.DB = &cluster.NodeDB{
Name: n.DB.Name,
User: n.DB.User,
DBLastRotatedAt: n.DB.RotAt,
}
}
return out
}
// BuildClusterNodes builds DTOs for a slice of registry.Node.
func BuildClusterNodes(list []reg.Node, opts ClusterNodeOptions) []cluster.Node {
if len(list) == 0 {
return []cluster.Node{}
}
out := make([]cluster.Node, 0, len(list))
for _, n := range list {
out = append(out, BuildClusterNode(n, opts))
}
return out
}

View File

@@ -8,8 +8,8 @@ import (
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/photoprism/get"
"github.com/photoprism/photoprism/internal/service/cluster"
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
"github.com/photoprism/photoprism/pkg/service/cluster"
"github.com/photoprism/photoprism/pkg/service/http/header"
)

View File

@@ -11,8 +11,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/photoprism/photoprism/internal/service/cluster"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/service/cluster"
"github.com/photoprism/photoprism/pkg/service/http/header"
)

View File

@@ -6,10 +6,10 @@ import (
"gopkg.in/yaml.v2"
"github.com/photoprism/photoprism/internal/service/cluster"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/service/cluster"
)
// NodeName returns the unique name of this node within the cluster (lowercase letters and numbers only).

View File

@@ -8,9 +8,9 @@ import (
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
"github.com/photoprism/photoprism/internal/service/cluster"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/service/cluster"
)
func TestConfig_Cluster(t *testing.T) {

View File

@@ -0,0 +1,63 @@
package registry
import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/service/cluster"
)
// NodeOpts controls which optional fields get included in responses.
type NodeOpts struct {
IncludeInternalURL bool
IncludeDBMeta bool
}
// NodeOptsForSession returns the default exposure policy for a session.
// Admin users see internalUrl and DB metadata; others get a redacted view.
func NodeOptsForSession(s *entity.Session) NodeOpts {
if s != nil && s.User() != nil && s.User().IsAdmin() {
return NodeOpts{IncludeInternalURL: true, IncludeDBMeta: true}
}
return NodeOpts{}
}
// BuildClusterNode builds a cluster.Node DTO from a registry.Node with redaction according to opts.
func BuildClusterNode(n Node, opts NodeOpts) cluster.Node {
out := cluster.Node{
ID: n.ID,
Name: n.Name,
Type: n.Type,
Labels: n.Labels,
CreatedAt: n.CreatedAt,
UpdatedAt: n.UpdatedAt,
}
if opts.IncludeInternalURL && n.Internal != "" {
out.InternalURL = n.Internal
}
if opts.IncludeDBMeta {
out.DB = &cluster.NodeDB{
Name: n.DB.Name,
User: n.DB.User,
DBLastRotatedAt: n.DB.RotAt,
}
}
return out
}
// BuildClusterNodes creates a cluster node slice from the given registry node slice.
func BuildClusterNodes(list []Node, opts NodeOpts) []cluster.Node {
if len(list) == 0 {
return []cluster.Node{}
}
out := make([]cluster.Node, 0, len(list))
for _, n := range list {
out = append(out, BuildClusterNode(n, opts))
}
return out
}