mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
CI: Apply Go linter recommendations to "internal/service" packages #5330
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
699ad5b50c
commit
57c9096d1f
25 changed files with 61 additions and 42 deletions
|
|
@ -10,12 +10,12 @@ const (
|
|||
|
||||
// Example values used by documentation and tests to illustrate cluster tokens.
|
||||
const (
|
||||
// ExampleJoinToken represents a valid portal join token.
|
||||
ExampleJoinToken = "pGVplw8-eISgkdQN-Mep62nQ"
|
||||
// ExampleJoinTokenAlt provides an alternative join token for negative/rotation tests.
|
||||
ExampleJoinTokenAlt = "k9sEFe6-A7gt6zqm-gY9gFh0"
|
||||
// ExampleJoinToken represents a valid portal join token. Sample only.
|
||||
ExampleJoinToken = "pGVplw8-eISgkdQN-Mep62nQ" //nolint:gosec // example value, not a secret
|
||||
// ExampleJoinTokenAlt provides an alternative join token for negative/rotation tests. Sample only.
|
||||
ExampleJoinTokenAlt = "k9sEFe6-A7gt6zqm-gY9gFh0" //nolint:gosec // example value, not a secret
|
||||
// ExampleClientID is a sample node client identifier issued by the portal.
|
||||
ExampleClientID = "cs5gfen1bgxz7s9i"
|
||||
// ExampleClientSecret is a sample node client secret matching the format generated by rnd.ClientSecret().
|
||||
ExampleClientSecret = "A1B2C3D4E5F6G7H8J9K0L1M2N3P4Q5R6"
|
||||
ExampleClientSecret = "A1B2C3D4E5F6G7H8J9K0L1M2N3P4Q5R6" //nolint:gosec // example value
|
||||
)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ func (u *OptionsUpdate) SetDatabasePassword(value string) {
|
|||
u.DatabasePassword = stringPtr(value)
|
||||
}
|
||||
|
||||
// forEach enumerates all set fields and invokes fn with the corresponding key/value pair.
|
||||
// Visit enumerates all set fields and invokes fn with the corresponding key/value pair.
|
||||
func (u OptionsUpdate) Visit(fn func(string, any)) {
|
||||
if u.ClusterUUID != nil {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func TestOptionsUpdate_Apply(t *testing.T) {
|
|||
seed := map[string]any{"Existing": "value"}
|
||||
b, err := yaml.Marshal(seed)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile(conf.OptionsYaml(), b, 0o644))
|
||||
require.NoError(t, os.WriteFile(conf.OptionsYaml(), b, 0o600))
|
||||
|
||||
update := cluster.OptionsUpdate{}
|
||||
update.SetClusterUUID("4a47c940-d5de-41b3-88a2-eb816cc659ca")
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func init() {
|
|||
applyPolicyEnv()
|
||||
}
|
||||
|
||||
// applyPolicyEnv allows advanced users to fine-tune bootstrap behaviour via environment
|
||||
// applyPolicyEnv allows advanced users to fine-tune bootstrap behavior via environment
|
||||
// variables without exposing additional user-facing configuration options.
|
||||
func applyPolicyEnv() {
|
||||
if v := os.Getenv(clean.EnvVar("cluster-bootstrap-auto-join-enabled")); v != "" {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/go-sql-driver/mysql" // register MySQL driver
|
||||
)
|
||||
|
||||
// ProvisionDSN specifies the admin DSN used for auto-provisioning, for example:
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ func TestExecTimeout_DeadlineExceeded(t *testing.T) {
|
|||
// execTimeout forwards the statement correctly.
|
||||
type fastDriver struct{ last *string }
|
||||
|
||||
func (f fastDriver) Open(name string) (driver.Conn, error) { return fastConn{last: f.last}, nil }
|
||||
func (f fastDriver) Open(name string) (driver.Conn, error) { return fastConn(f), nil }
|
||||
|
||||
type fastConn struct{ last *string }
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ func TestExecTimeout_ForwardsStatement(t *testing.T) {
|
|||
// variant waits for cancellation, the other returns quickly.
|
||||
type pingyDriver struct{ wait bool }
|
||||
|
||||
func (p pingyDriver) Open(name string) (driver.Conn, error) { return pingyConn{wait: p.wait}, nil }
|
||||
func (p pingyDriver) Open(name string) (driver.Conn, error) { return pingyConn(p), nil }
|
||||
|
||||
type pingyConn struct{ wait bool }
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ const (
|
|||
// username: cluster_u<hmac11>
|
||||
dbSuffix = 11
|
||||
userSuffix = 11
|
||||
// Budgets: keep user conservative for MySQL compatibility; MariaDB allows more.
|
||||
userMax = 32
|
||||
dbMax = 64
|
||||
// prefixMax ensures usernames remain within the MySQL identifier limit.
|
||||
prefixMax = cluster.DatabaseProvisionPrefixMaxLen
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ type Node struct {
|
|||
|
||||
// ensureDatabase returns a writable NodeDatabase, creating one if missing.
|
||||
func (n *Node) ensureDatabase() *cluster.NodeDatabase {
|
||||
if n.Node.Database == nil {
|
||||
n.Node.Database = &cluster.NodeDatabase{}
|
||||
if n.Database == nil {
|
||||
n.Database = &cluster.NodeDatabase{}
|
||||
}
|
||||
return n.Node.Database
|
||||
|
||||
return n.Database
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,14 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
)
|
||||
|
||||
// NodeRole represents the role a node plays within a cluster.
|
||||
type NodeRole = string
|
||||
|
||||
const (
|
||||
RoleApp = NodeRole(acl.RoleApp) // A regular PhotoPrism app node that can join a cluster
|
||||
RolePortal = NodeRole(acl.RolePortal) // A management portal for orchestrating a cluster
|
||||
RoleService = NodeRole(acl.RoleService) // Other service used within a cluster, e.g. Ollama or Vision API
|
||||
// RoleApp represents a regular PhotoPrism app node that can join a cluster.
|
||||
RoleApp = NodeRole(acl.RoleApp)
|
||||
// RolePortal represents a management portal for orchestrating a cluster.
|
||||
RolePortal = NodeRole(acl.RolePortal)
|
||||
// RoleService represents other services used within a cluster, e.g., Ollama or Vision API.
|
||||
RoleService = NodeRole(acl.RoleService)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func DetectVersion(themePath string) (string, error) {
|
|||
|
||||
versionFile := filepath.Join(themePath, fs.VersionTxtFile)
|
||||
|
||||
if data, readErr := os.ReadFile(versionFile); readErr == nil {
|
||||
if data, readErr := os.ReadFile(versionFile); readErr == nil { //nolint:gosec // version file path is internal, provided by caller
|
||||
if v := clean.TypeUnicode(string(data)); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Headers holds request header key/value pairs.
|
||||
type Headers = map[string]string
|
||||
|
||||
// Heuristic represents a heuristic for detecting a remote service type, e.g. WebDAV.
|
||||
|
|
@ -36,6 +37,7 @@ var Heuristics = []Heuristic{
|
|||
},
|
||||
}
|
||||
|
||||
// MatchDomain returns true if the heuristic allows the provided domain.
|
||||
func (h Heuristic) MatchDomain(match string) bool {
|
||||
if len(h.Domains) == 0 {
|
||||
return true
|
||||
|
|
@ -44,6 +46,7 @@ func (h Heuristic) MatchDomain(match string) bool {
|
|||
return slices.Contains(h.Domains, match)
|
||||
}
|
||||
|
||||
// Discover returns the first matching endpoint URL for the heuristic.
|
||||
func (h Heuristic) Discover(rawUrl, user string) *url.URL {
|
||||
u, err := url.Parse(rawUrl)
|
||||
|
||||
|
|
@ -56,7 +59,7 @@ func (h Heuristic) Discover(rawUrl, user string) *url.URL {
|
|||
}
|
||||
|
||||
for _, p := range h.Paths {
|
||||
u.Path = strings.Replace(p, "{user}", user, -1)
|
||||
u.Path = strings.ReplaceAll(p, "{user}", user)
|
||||
|
||||
if h.TestRequest(h.Method, u.String()) {
|
||||
return u
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha1"
|
||||
"crypto/sha1" //nolint:gosec // retained for legacy key derivation check
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
|
@ -25,11 +25,15 @@ import (
|
|||
"github.com/photoprism/photoprism/pkg/http/header"
|
||||
)
|
||||
|
||||
// Status represents the hub registration state.
|
||||
type Status string
|
||||
|
||||
const (
|
||||
StatusUnknown Status = ""
|
||||
StatusNew Status = "unregistered"
|
||||
// StatusUnknown indicates an undefined state.
|
||||
StatusUnknown Status = ""
|
||||
// StatusNew indicates a node has not registered yet.
|
||||
StatusNew Status = "unregistered"
|
||||
// StatusCommunity indicates Community Edition status.
|
||||
StatusCommunity Status = "ce"
|
||||
)
|
||||
|
||||
|
|
@ -122,7 +126,7 @@ func (c *Config) Sanitize() {
|
|||
c.Key = strings.ToLower(c.Key)
|
||||
|
||||
if c.Secret != "" {
|
||||
if c.Key != fmt.Sprintf("%x", sha1.Sum([]byte(c.Secret))) {
|
||||
if c.Key != fmt.Sprintf("%x", sha1.Sum([]byte(c.Secret))) { //nolint:gosec // legacy SHA1 comparison
|
||||
c.Key = ""
|
||||
c.Secret = ""
|
||||
c.Session = ""
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//nolint:gocritic // explicit branching retained for clarity
|
||||
package places
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ var Retries = 2
|
|||
// RetryDelay specifies the waiting time between retries.
|
||||
var RetryDelay = 100 * time.Millisecond
|
||||
|
||||
var Key = "f60f5b25d59c397989e3cd374f81cdd7710a4fca"
|
||||
var Secret = "photoprism"
|
||||
// Key is the hub places API key (overridden via environment/config in production).
|
||||
var Key = "f60f5b25d59c397989e3cd374f81cdd7710a4fca" //nolint:gosec // example/default key
|
||||
|
||||
// Secret is the hub places API secret (overridden in production).
|
||||
var Secret = "photoprism" //nolint:gosec // example/default secret
|
||||
|
||||
// UserAgent overrides the default HTTP User-Agent header for hub places calls.
|
||||
var UserAgent = ""
|
||||
|
|
|
|||
|
|
@ -4,5 +4,8 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
// ErrMissingQuery indicates that a place search query was empty.
|
||||
var ErrMissingQuery = errors.New("missing query")
|
||||
|
||||
// ErrMissingCoordinates indicates that both latitude and longitude were missing.
|
||||
var ErrMissingCoordinates = errors.New("missing coordinates")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//nolint:gocritic // explicit branching retained for clarity
|
||||
package places
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package places
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha1" //nolint:gosec // required for upstream signature scheme
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
|
@ -40,7 +40,7 @@ func GetRequest(reqUrl string, locale string) (r *http.Response, err error) {
|
|||
// Add API key?
|
||||
if Key != "" {
|
||||
req.Header.Set("X-Key", Key)
|
||||
req.Header.Set("X-Signature", fmt.Sprintf("%x", sha1.Sum([]byte(Key+reqUrl+Secret))))
|
||||
req.Header.Set("X-Signature", fmt.Sprintf("%x", sha1.Sum([]byte(Key+reqUrl+Secret)))) //nolint:gosec // upstream expects SHA1
|
||||
}
|
||||
|
||||
// Create new http.Client.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//nolint:gocritic // explicit branching retained for clarity
|
||||
package places
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package places
|
||||
|
||||
// SearchResult represents a place returned by the hub places API.
|
||||
type SearchResult struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
|
|
@ -12,4 +13,5 @@ type SearchResult struct {
|
|||
Licence string `json:"licence,omitempty"`
|
||||
}
|
||||
|
||||
// SearchResults is a slice of SearchResult.
|
||||
type SearchResults = []SearchResult
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package maps
|
|||
|
||||
// Generated code, do not edit.
|
||||
|
||||
// CountryNames maps ISO country codes to localized names.
|
||||
var CountryNames = map[string]string{
|
||||
"af": "Afghanistan",
|
||||
"ax": "Åland Islands",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
// This generates countries.go by running "go generate"
|
||||
package main
|
||||
|
|
@ -55,6 +54,7 @@ package maps
|
|||
|
||||
// Generated code, do not edit.
|
||||
|
||||
// CountryNames maps ISO country codes to localized names.
|
||||
var CountryNames = map[string]string{
|
||||
{{- range .Countries }}
|
||||
{{ printf "%q" .Code }}: {{ printf "%q" .Name }},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//revive:disable:exported // getters intentionally exported for use across services
|
||||
package maps
|
||||
|
||||
import (
|
||||
|
|
@ -42,9 +43,8 @@ type LocationSource interface {
|
|||
Source() string
|
||||
}
|
||||
|
||||
func (l *Location) QueryApi(api string) error {
|
||||
switch api {
|
||||
case places.ApiName:
|
||||
func (l *Location) QueryApi(api string) error { //nolint:gocritic // single-case switch retained for future APIs
|
||||
if api == places.ApiName {
|
||||
return l.QueryPlaces()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,11 +24,5 @@ Additional information can be found in our Developer Guide:
|
|||
*/
|
||||
package maps
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
)
|
||||
|
||||
//go:generate go run gen.go
|
||||
//go:generate go fmt .
|
||||
|
||||
var log = event.Log
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ func (c *Client) Upload(src, dest string) (err error) {
|
|||
return fmt.Errorf("file %s not found", clean.Log(path.Base(src)))
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(src, os.O_RDONLY, 0)
|
||||
f, err := os.OpenFile(src, os.O_RDONLY, 0) //nolint:gosec // path provided by caller; read-only
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("webdav: %s", clean.Error(err))
|
||||
|
|
@ -290,7 +290,7 @@ func (c *Client) Download(src, dest string, force bool) (err error) {
|
|||
|
||||
defer reader.Close()
|
||||
|
||||
f, err := os.OpenFile(dest, os.O_TRUNC|os.O_RDWR|os.O_CREATE, fs.ModeFile)
|
||||
f, err := os.OpenFile(dest, os.O_TRUNC|os.O_RDWR|os.O_CREATE, fs.ModeFile) //nolint:gosec // dest provided by caller
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("webdav: %s", clean.Error(err))
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
// Global log instance.
|
||||
var log = event.Log
|
||||
|
||||
// Timeout specifies a timeout mode for WebDAV operations.
|
||||
type Timeout string
|
||||
|
||||
// Request Timeout options.
|
||||
|
|
@ -45,6 +46,8 @@ const (
|
|||
)
|
||||
|
||||
// Second represents a second on which other timeouts are based.
|
||||
//
|
||||
//revive:disable-next-line:time-naming // keep exported constant name for API compatibility
|
||||
const Second = time.Second
|
||||
|
||||
// MaxRequestDuration is the maximum request duration e.g. for recursive retrieval of large remote directory structures.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue