mirror of
https://github.com/photoprism/photoprism.git
synced 2026-04-22 16:07:25 +08:00
Go: Apply go fix modernizations across backend packages
Run `go fix ./...` and keep mechanical modernization updates.
- Replace `interface{}` with `any` in signatures and local types
- Apply formatter/style cleanups from go1.26 tooling
- Keep `omitempty` behavior-preserving simplifications suggested by fix
- No functional feature changes intended
Validation:
- go test ./... -run '^$' -count=1 (Go 1.26.0)
- GOTOOLCHAIN=go1.24.10 go test ./... -run '^$' -count=1
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -44,7 +44,7 @@ const appDescription = "PhotoPrism® is an AI-Powered Photos App for the Decentr
|
||||
const appCopyright = "(c) 2018-2025 PhotoPrism UG. All rights reserved."
|
||||
|
||||
// Metadata contains build specific information.
|
||||
var Metadata = map[string]interface{}{
|
||||
var Metadata = map[string]any{
|
||||
"Name": appName,
|
||||
"About": appAbout,
|
||||
"Edition": appEdition,
|
||||
|
||||
@@ -157,12 +157,12 @@ func EmbeddingsMidpoint(embeddings Embeddings) (result Embedding, radius float64
|
||||
|
||||
normalizeEmbedding(emb)
|
||||
|
||||
for j := 0; j < dim; j++ {
|
||||
for j := range dim {
|
||||
result[j] += emb[j]
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < dim; i++ {
|
||||
for i := range dim {
|
||||
result[i] *= invCount
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ func EmbeddingsMidpoint(embeddings Embeddings) (result Embedding, radius float64
|
||||
for _, emb := range embeddings {
|
||||
var dist float64
|
||||
|
||||
for i := 0; i < dim; i++ {
|
||||
for i := range dim {
|
||||
diff := result[i] - emb[i]
|
||||
dist += diff * diff
|
||||
}
|
||||
|
||||
@@ -174,10 +174,7 @@ func NewONNXEngine(opts ONNXOptions) (DetectionEngine, error) {
|
||||
|
||||
threads := opts.Threads
|
||||
if threads == 0 {
|
||||
threads = runtime.NumCPU() / 2
|
||||
if threads < 1 {
|
||||
threads = 1
|
||||
}
|
||||
threads = max(runtime.NumCPU()/2, 1)
|
||||
}
|
||||
|
||||
if err := sessionOpts.SetIntraOpNumThreads(threads); err != nil {
|
||||
@@ -560,11 +557,11 @@ func (o *onnxEngine) anchorCenters(height, width, stride, anchors int) []float32
|
||||
|
||||
centers := make([]float32, height*width*anchors*2)
|
||||
idx := 0
|
||||
for y := 0; y < height; y++ {
|
||||
for y := range height {
|
||||
cy := float32(y * stride)
|
||||
for x := 0; x < width; x++ {
|
||||
for x := range width {
|
||||
cx := float32(x * stride)
|
||||
for a := 0; a < anchors; a++ {
|
||||
for range anchors {
|
||||
centers[idx] = cx
|
||||
centers[idx+1] = cy
|
||||
idx += 2
|
||||
@@ -599,7 +596,7 @@ func nonMaxSuppression(boxes []onnxDetection, threshold float32) []onnxDetection
|
||||
picked := make([]onnxDetection, 0, len(boxes))
|
||||
suppressed := make([]bool, len(boxes))
|
||||
|
||||
for i := 0; i < len(boxes); i++ {
|
||||
for i := range boxes {
|
||||
if suppressed[i] {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ type Face struct {
|
||||
Rows int `json:"rows,omitempty"`
|
||||
Cols int `json:"cols,omitempty"`
|
||||
Score int `json:"score,omitempty"`
|
||||
Area Area `json:"face,omitempty"`
|
||||
Area Area `json:"face"`
|
||||
Eyes Areas `json:"eyes,omitempty"`
|
||||
Landmarks Areas `json:"landmarks,omitempty"`
|
||||
Embeddings Embeddings `json:"embeddings,omitempty"`
|
||||
|
||||
@@ -187,12 +187,12 @@ func imageToTensor(img image.Image, resolution int) (tfTensor *tf.Tensor, err er
|
||||
|
||||
var tfImage [1][][][3]float32
|
||||
|
||||
for j := 0; j < resolution; j++ {
|
||||
for range resolution {
|
||||
tfImage[0] = append(tfImage[0], make([][3]float32, resolution))
|
||||
}
|
||||
|
||||
for i := 0; i < resolution; i++ {
|
||||
for j := 0; j < resolution; j++ {
|
||||
for i := range resolution {
|
||||
for j := range resolution {
|
||||
r, g, b, _ := img.At(i, j).RGBA()
|
||||
tfImage[0][j][i][0] = convertValue(r)
|
||||
tfImage[0][j][i][1] = convertValue(g)
|
||||
|
||||
@@ -112,8 +112,8 @@ func TestNet(t *testing.T) {
|
||||
// Dist Matrix
|
||||
correct := 0
|
||||
|
||||
for i := 0; i < len(embeddings); i++ {
|
||||
for j := 0; j < len(embeddings); j++ {
|
||||
for i := range embeddings {
|
||||
for j := range embeddings {
|
||||
if i >= j {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func (o ResizeOperation) MarshalYAML() (any, error) {
|
||||
}
|
||||
|
||||
// UnmarshalYAML decodes the resize operation from YAML input.
|
||||
func (o *ResizeOperation) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
func (o *ResizeOperation) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var s string
|
||||
if err := unmarshal(&s); err != nil {
|
||||
return err
|
||||
@@ -269,7 +269,7 @@ func (o ColorChannelOrder) MarshalYAML() (any, error) {
|
||||
}
|
||||
|
||||
// UnmarshalYAML decodes the channel order from YAML input.
|
||||
func (o *ColorChannelOrder) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
func (o *ColorChannelOrder) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var s string
|
||||
if err := unmarshal(&s); err != nil {
|
||||
return err
|
||||
|
||||
@@ -19,7 +19,7 @@ type ApiResponse struct {
|
||||
Code int `yaml:"Code,omitempty" json:"code,omitempty"`
|
||||
Error string `yaml:"Error,omitempty" json:"error,omitempty"`
|
||||
Model *Model `yaml:"Model,omitempty" json:"model,omitempty"`
|
||||
Result ApiResult `yaml:"Result,omitempty" json:"result,omitempty"`
|
||||
Result ApiResult `yaml:"Result,omitempty" json:"result"`
|
||||
}
|
||||
|
||||
// Err returns an error if the request has failed.
|
||||
|
||||
@@ -47,7 +47,7 @@ type Model struct {
|
||||
Resolution int `yaml:"Resolution,omitempty" json:"resolution,omitempty"`
|
||||
TensorFlow *tensorflow.ModelInfo `yaml:"TensorFlow,omitempty" json:"tensorflow,omitempty"`
|
||||
Options *ModelOptions `yaml:"Options,omitempty" json:"options,omitempty"`
|
||||
Service Service `yaml:"Service,omitempty" json:"service,omitempty"`
|
||||
Service Service `yaml:"Service,omitempty" json:"service"`
|
||||
Path string `yaml:"Path,omitempty" json:"-"`
|
||||
Disabled bool `yaml:"Disabled,omitempty" json:"disabled,omitempty"`
|
||||
classifyModel *classify.Model
|
||||
|
||||
@@ -40,7 +40,6 @@ func TestFilterModels(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := FilterModels(tc.models, tc.when, tc.allow)
|
||||
if len(got) != len(tc.expected) {
|
||||
|
||||
@@ -21,7 +21,6 @@ func TestParseRunType(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := ParseRunType(tc.in); got != tc.out {
|
||||
@@ -65,7 +64,6 @@ func TestModel_RunType(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := tc.model.RunType(); got != tc.want {
|
||||
|
||||
@@ -434,7 +434,6 @@ func TestModel_IsDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := tc.model.IsDefault(); got != tc.want {
|
||||
|
||||
@@ -33,7 +33,7 @@ func ParseModelTypes(s string) (types ModelTypes) {
|
||||
s = strings.ToLower(s)
|
||||
types = make(ModelTypes, 0, strings.Count(s, ","))
|
||||
|
||||
for _, t := range strings.Split(s, ",") {
|
||||
for t := range strings.SplitSeq(s, ",") {
|
||||
t = strings.TrimSpace(t)
|
||||
switch t {
|
||||
case ModelTypeLabels, ModelTypeNsfw, ModelTypeFace, ModelTypeCaption, ModelTypeGenerate:
|
||||
|
||||
@@ -12,7 +12,7 @@ type Response struct {
|
||||
Code int `yaml:"Code,omitempty" json:"code,omitempty"`
|
||||
Error string `yaml:"Error,omitempty" json:"error,omitempty"`
|
||||
Model string `yaml:"Model,omitempty" json:"model,omitempty"`
|
||||
CreatedAt time.Time `yaml:"CreatedAt,omitempty" json:"created_at,omitempty"`
|
||||
CreatedAt time.Time `yaml:"CreatedAt,omitempty" json:"created_at"`
|
||||
Response string `yaml:"Response,omitempty" json:"response,omitempty"`
|
||||
Thinking string `yaml:"Thinking,omitempty" json:"thinking,omitempty"`
|
||||
Done bool `yaml:"Done,omitempty" json:"done,omitempty"`
|
||||
@@ -23,7 +23,7 @@ type Response struct {
|
||||
PromptEvalDuration int `yaml:"PromptEvalDuration,omitempty" json:"prompt_eval_duration,omitempty"`
|
||||
EvalCount int `yaml:"EvalCount,omitempty" json:"eval_count,omitempty"`
|
||||
EvalDuration int64 `yaml:"EvalDuration,omitempty" json:"eval_duration,omitempty"`
|
||||
Result ResultPayload `yaml:"Result,omitempty" json:"result,omitempty"`
|
||||
Result ResultPayload `yaml:"Result,omitempty" json:"result"`
|
||||
}
|
||||
|
||||
// Err returns an error if the request has failed.
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
var brokenVideo []byte
|
||||
|
||||
// Abort writes a localized error response and stops further handler processing.
|
||||
func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
|
||||
func Abort(c *gin.Context, code int, id i18n.Message, params ...any) {
|
||||
resp := i18n.NewResponse(code, id, params...)
|
||||
|
||||
if code >= 400 {
|
||||
@@ -32,7 +32,7 @@ func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
|
||||
}
|
||||
|
||||
// Error aborts the request while attaching error details to the response payload.
|
||||
func Error(c *gin.Context, code int, err error, id i18n.Message, params ...interface{}) {
|
||||
func Error(c *gin.Context, code int, err error, id i18n.Message, params ...any) {
|
||||
resp := i18n.NewResponse(code, id, params...)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -95,11 +95,7 @@ func ClusterListNodes(router *gin.RouterGroup) {
|
||||
offset = len(items)
|
||||
}
|
||||
|
||||
end := offset + count
|
||||
|
||||
if end > len(items) {
|
||||
end = len(items)
|
||||
}
|
||||
end := min(offset+count, len(items))
|
||||
|
||||
page := items[offset:end]
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestOAuthToken_RateLimit_ClientCredentials(t *testing.T) {
|
||||
|
||||
// Invalid client secret repeatedly (from UnknownIP: no headers set)
|
||||
path := "/api/v1/oauth/token"
|
||||
for i := 0; i < 3; i++ {
|
||||
for range 3 {
|
||||
data := url.Values{
|
||||
"grant_type": {authn.GrantClientCredentials.String()},
|
||||
"client_id": {"cs5cpu17n6gj2qo5"},
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestCreateSession_RateLimitExceeded(t *testing.T) {
|
||||
limiter.Login = limiter.NewLimit(rate.Every(24*time.Hour), 3)
|
||||
limiter.Auth = limiter.NewLimit(rate.Every(24*time.Hour), 3)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
for range 3 {
|
||||
r := PerformRequestWithBody(app, http.MethodPost, "/api/v1/session", `{"username": "admin", "password": "wrong"}`)
|
||||
assert.Equal(t, http.StatusUnauthorized, r.Code)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func buildMultipart(files map[string][]byte) (body *bytes.Buffer, contentType st
|
||||
func buildMultipartTwo(name1 string, data1 []byte, name2 string, data2 []byte) (body *bytes.Buffer, contentType string, err error) {
|
||||
body = &bytes.Buffer{}
|
||||
mw := multipart.NewWriter(body)
|
||||
for _, it := range [][2]interface{}{{name1, data1}, {name2, data2}} {
|
||||
for _, it := range [][2]any{{name1, data1}, {name2, data2}} {
|
||||
fw, cerr := mw.CreateFormFile("files", it[0].(string))
|
||||
if cerr != nil {
|
||||
return nil, "", cerr
|
||||
@@ -342,7 +342,7 @@ func TestUploadUserFiles_Multipart_ZipPartialExtraction(t *testing.T) {
|
||||
|
||||
var zbuf bytes.Buffer
|
||||
zw := zip.NewWriter(&zbuf)
|
||||
for i := 0; i < 20; i++ { // ~20 * 63 KiB ≈ 1.2 MiB
|
||||
for i := range 20 { // ~20 * 63 KiB ≈ 1.2 MiB
|
||||
f, _ := zw.Create(fmt.Sprintf("pic%02d.jpg", i+1))
|
||||
_, _ = f.Write(data)
|
||||
}
|
||||
@@ -390,7 +390,7 @@ func TestUploadUserFiles_Multipart_ZipDeepNestingStress(t *testing.T) {
|
||||
|
||||
// Build a deeply nested path (20 levels)
|
||||
deep := ""
|
||||
for i := 0; i < 20; i++ {
|
||||
for i := range 20 {
|
||||
if i == 0 {
|
||||
deep = "deep"
|
||||
} else {
|
||||
|
||||
@@ -51,7 +51,7 @@ func AppendWebsocketTopics(topics ...string) {
|
||||
}
|
||||
|
||||
// wsSendMessage sends a message to the WebSocket client.
|
||||
func wsSendMessage(topic string, data interface{}, ws *websocket.Conn, writeMutex *sync.Mutex) {
|
||||
func wsSendMessage(topic string, data any, ws *websocket.Conn, writeMutex *sync.Mutex) {
|
||||
if topic == "" || ws == nil || writeMutex == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
@@ -172,13 +173,7 @@ func TestResource_Default_String_And_Compare(t *testing.T) {
|
||||
func TestResourceNames_ContainsCore(t *testing.T) {
|
||||
want := []Resource{ResourceDefault, ResourcePhotos, ResourceAlbums, ResourceWebDAV, ResourceApi}
|
||||
for _, w := range want {
|
||||
found := false
|
||||
for _, have := range ResourceNames {
|
||||
if have == w {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
found := slices.Contains(ResourceNames, w)
|
||||
assert.Truef(t, found, "resource %s not found in ResourceNames", w)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ type VerifierStatus struct {
|
||||
CacheETag string `json:"cacheEtag,omitempty"`
|
||||
KeyIDs []string `json:"keyIds,omitempty"`
|
||||
KeyCount int `json:"keyCount"`
|
||||
CacheFetchedAt time.Time `json:"cacheFetchedAt,omitempty"`
|
||||
CacheFetchedAt time.Time `json:"cacheFetchedAt"`
|
||||
CacheAgeSeconds int64 `json:"cacheAgeSeconds"`
|
||||
CacheTTLSeconds int `json:"cacheTtlSeconds"`
|
||||
CacheStale bool `json:"cacheStale"`
|
||||
@@ -137,7 +137,7 @@ func (v *Verifier) VerifyToken(ctx context.Context, tokenString string, expected
|
||||
)
|
||||
|
||||
claims := &Claims{}
|
||||
keyFunc := func(token *gojwt.Token) (interface{}, error) {
|
||||
keyFunc := func(token *gojwt.Token) (any, error) {
|
||||
kid, _ := token.Header["kid"].(string)
|
||||
|
||||
if kid == "" {
|
||||
@@ -171,7 +171,7 @@ func (v *Verifier) VerifyToken(ctx context.Context, tokenString string, expected
|
||||
|
||||
scopeSet := map[string]struct{}{}
|
||||
|
||||
for _, s := range strings.Fields(claims.Scope) {
|
||||
for s := range strings.FieldsSeq(claims.Scope) {
|
||||
scopeSet[s] = struct{}{}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ func VerifyTokenWithKeys(tokenString string, expected ExpectedClaims, keys []Pub
|
||||
|
||||
parser := gojwt.NewParser(options...)
|
||||
claims := &Claims{}
|
||||
keyFunc := func(token *gojwt.Token) (interface{}, error) {
|
||||
keyFunc := func(token *gojwt.Token) (any, error) {
|
||||
kid, _ := token.Header["kid"].(string)
|
||||
if kid == "" {
|
||||
return nil, errors.New("jwt: missing kid header")
|
||||
@@ -261,7 +261,7 @@ func VerifyTokenWithKeys(tokenString string, expected ExpectedClaims, keys []Pub
|
||||
|
||||
if len(expected.Scope) > 0 {
|
||||
scopeSet := map[string]struct{}{}
|
||||
for _, s := range strings.Fields(claims.Scope) {
|
||||
for s := range strings.FieldsSeq(claims.Scope) {
|
||||
scopeSet[s] = struct{}{}
|
||||
}
|
||||
for _, req := range expected.Scope {
|
||||
@@ -543,11 +543,7 @@ func backoffDuration(attempt int) time.Duration {
|
||||
attempt = 1
|
||||
}
|
||||
|
||||
base := jwksFetchBaseDelay << (attempt - 1)
|
||||
|
||||
if base > jwksFetchMaxDelay {
|
||||
base = jwksFetchMaxDelay
|
||||
}
|
||||
base := min(jwksFetchBaseDelay<<(attempt-1), jwksFetchMaxDelay)
|
||||
|
||||
jitterRange := base / 2
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ func TestIssuerClampTTL(t *testing.T) {
|
||||
|
||||
parsed := &Claims{}
|
||||
parser := gojwt.NewParser(gojwt.WithValidMethods([]string{gojwt.SigningMethodEdDSA.Alg()}), gojwt.WithoutClaimsValidation())
|
||||
_, err = parser.ParseWithClaims(token, parsed, func(token *gojwt.Token) (interface{}, error) {
|
||||
_, err = parser.ParseWithClaims(token, parsed, func(token *gojwt.Token) (any, error) {
|
||||
key, _ := mgr.ActiveKey()
|
||||
return key.PublicKey, nil
|
||||
})
|
||||
|
||||
@@ -60,10 +60,7 @@ func clusterNodesListAction(ctx *cli.Context) error {
|
||||
if count <= 0 || count > 1000 {
|
||||
count = 100
|
||||
}
|
||||
offset := ctx.Int("offset")
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
offset := max(ctx.Int("offset"), 0)
|
||||
if offset > len(items) {
|
||||
offset = len(items)
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func clusterNodesModAction(ctx *cli.Context) error {
|
||||
string(acl.ResourceCluster),
|
||||
"update node", "%s",
|
||||
}
|
||||
args := []interface{}{clean.Log(nodeID)}
|
||||
args := []any{clean.Log(nodeID)}
|
||||
|
||||
if changeSummary != "" {
|
||||
segments = append(segments, "%s")
|
||||
|
||||
@@ -194,7 +194,7 @@ func clusterNodesRotateAction(ctx *cli.Context) error {
|
||||
string(acl.ResourceCluster),
|
||||
"rotate node", "%s",
|
||||
}
|
||||
args := []interface{}{clean.Log(nodeID)}
|
||||
args := []any{clean.Log(nodeID)}
|
||||
if detail != "" {
|
||||
segments = append(segments, "%s")
|
||||
args = append(args, clean.Log(detail))
|
||||
|
||||
@@ -332,7 +332,7 @@ func (e *httpError) Error() string { return fmt.Sprintf("http %d: %s", e.Status,
|
||||
func postWithBackoff(url, token string, payload []byte, out any) error {
|
||||
// backoff: 500ms -> max ~8s, 6 attempts with jitter
|
||||
delay := 500 * time.Millisecond
|
||||
for attempt := 0; attempt < 6; attempt++ {
|
||||
for range 6 {
|
||||
req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(payload))
|
||||
header.SetAuthorization(req, token)
|
||||
req.Header.Set(header.ContentType, "application/json")
|
||||
|
||||
@@ -104,7 +104,7 @@ func NewTestContext(args []string) *cli.Context {
|
||||
app.HideHelpCommand = true
|
||||
app.Action = func(*cli.Context) error { return nil }
|
||||
app.EnableBashCompletion = false
|
||||
app.Metadata = map[string]interface{}{
|
||||
app.Metadata = map[string]any{
|
||||
"Name": "PhotoPrism",
|
||||
"About": "PhotoPrism®",
|
||||
"Edition": "ce",
|
||||
|
||||
@@ -74,10 +74,7 @@ func findAction(ctx *cli.Context) error {
|
||||
rows := make([][]string, 0, len(results))
|
||||
|
||||
for _, found := range results {
|
||||
size := found.FileSize
|
||||
if size < 0 {
|
||||
size = 0
|
||||
}
|
||||
size := max(found.FileSize, 0)
|
||||
v := []string{found.FileName, found.FileMime, humanize.Bytes(uint64(size)), found.FileHash} //nolint:gosec // size non-negative after check
|
||||
rows = append(rows, v)
|
||||
}
|
||||
|
||||
@@ -38,10 +38,7 @@ func showCommandsAction(ctx *cli.Context) error {
|
||||
includeHidden := ctx.Bool("all")
|
||||
wantJSON := ctx.Bool("json")
|
||||
nested := ctx.Bool("nested")
|
||||
baseHeading := ctx.Int("base-heading")
|
||||
if baseHeading < 1 {
|
||||
baseHeading = 1
|
||||
}
|
||||
baseHeading := max(ctx.Int("base-heading"), 1)
|
||||
|
||||
// Collect the app metadata to be included in the output.
|
||||
app := catalog.App{}
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestShowCommands_JSON_Flat(t *testing.T) {
|
||||
Name, FullName string
|
||||
Depth int
|
||||
} `json:"commands"`
|
||||
GlobalFlags []map[string]interface{} `json:"global_flags"`
|
||||
GlobalFlags []map[string]any `json:"global_flags"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
t.Fatalf("invalid json: %v", err)
|
||||
|
||||
@@ -58,7 +58,7 @@ func showConfigAction(ctx *cli.Context) error {
|
||||
rows, cols := rep.Report(conf)
|
||||
sections = append(sections, section{Title: rep.Title, Items: report.RowsToObjects(rows, cols)})
|
||||
}
|
||||
b, _ := json.Marshal(map[string]interface{}{"sections": sections})
|
||||
b, _ := json.Marshal(map[string]any{"sections": sections})
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -184,13 +184,6 @@ func TestShowConfigOptions_MarkdownSections(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func TestShowFileFormats_JSON(t *testing.T) {
|
||||
out, err := RunWithTestContext(ShowFileFormatsCommand, []string{"file-formats", "--json"})
|
||||
|
||||
|
||||
@@ -234,10 +234,10 @@ func videoListRow(found search.Photo) []string {
|
||||
}
|
||||
|
||||
// videoListJSONRow renders a search result row for JSON output with canonical column keys.
|
||||
func videoListJSONRow(found search.Photo) map[string]interface{} {
|
||||
func videoListJSONRow(found search.Photo) map[string]any {
|
||||
videoFile, _ := videoPrimaryFile(found)
|
||||
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"video": videoFile.FileName,
|
||||
"size": videoNonNegativeSize(videoFile.FileSize),
|
||||
"resolution": fmt.Sprintf("%dx%d", videoFile.FileWidth, videoFile.FileHeight),
|
||||
@@ -252,16 +252,16 @@ func videoListJSONRow(found search.Photo) map[string]interface{} {
|
||||
}
|
||||
|
||||
// videoListJSON marshals a list of JSON rows using the canonical keys for each column.
|
||||
func videoListJSON(rows []map[string]interface{}, cols []string) (string, error) {
|
||||
func videoListJSON(rows []map[string]any, cols []string) (string, error) {
|
||||
canon := make([]string, len(cols))
|
||||
for i, col := range cols {
|
||||
canon[i] = report.CanonKey(col)
|
||||
}
|
||||
|
||||
payload := make([]map[string]interface{}, 0, len(rows))
|
||||
payload := make([]map[string]any, 0, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := make(map[string]interface{}, len(canon))
|
||||
item := make(map[string]any, len(canon))
|
||||
for _, key := range canon {
|
||||
item[key] = row[key]
|
||||
}
|
||||
|
||||
@@ -68,10 +68,10 @@ func videoInfoAction(ctx *cli.Context) error {
|
||||
|
||||
// videoInfoEntry describes all metadata sections for a single video.
|
||||
type videoInfoEntry struct {
|
||||
Index map[string]interface{} `json:"index"`
|
||||
Exif interface{} `json:"exif,omitempty"`
|
||||
FFprobe interface{} `json:"ffprobe,omitempty"`
|
||||
Raw map[string]string `json:"raw,omitempty"`
|
||||
Index map[string]any `json:"index"`
|
||||
Exif any `json:"exif,omitempty"`
|
||||
FFprobe any `json:"ffprobe,omitempty"`
|
||||
Raw map[string]string `json:"raw,omitempty"`
|
||||
}
|
||||
|
||||
// videoInfoEntryFor collects indexed, ExifTool, and ffprobe metadata for a search result.
|
||||
@@ -123,8 +123,8 @@ func videoInfoEntryFor(conf *config.Config, found search.Photo, verbose bool) (v
|
||||
}
|
||||
|
||||
// videoIndexSummary builds a concise map of indexed fields for diagnostics.
|
||||
func videoIndexSummary(found search.Photo, file entity.File) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func videoIndexSummary(found search.Photo, file entity.File) map[string]any {
|
||||
return map[string]any{
|
||||
"file_name": file.FileName,
|
||||
"file_root": file.FileRoot,
|
||||
"file_uid": file.FileUID,
|
||||
@@ -152,7 +152,7 @@ func videoIndexSummary(found search.Photo, file entity.File) map[string]interfac
|
||||
}
|
||||
|
||||
// videoRunFFprobe executes ffprobe and returns parsed JSON plus raw output.
|
||||
func videoRunFFprobe(ffprobeBin, filePath string) (interface{}, string, error) {
|
||||
func videoRunFFprobe(ffprobeBin, filePath string) (any, string, error) {
|
||||
cmd := exec.Command(ffprobeBin, "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", filePath) //nolint:gosec // args are validated paths
|
||||
|
||||
var stdout bytes.Buffer
|
||||
@@ -169,7 +169,7 @@ func videoRunFFprobe(ffprobeBin, filePath string) (interface{}, string, error) {
|
||||
return nil, raw, nil
|
||||
}
|
||||
|
||||
var data interface{}
|
||||
var data any
|
||||
if err := json.Unmarshal([]byte(raw), &data); err != nil {
|
||||
return nil, raw, nil
|
||||
}
|
||||
@@ -185,7 +185,7 @@ func (v *videoInfoEntry) ensureRaw() {
|
||||
}
|
||||
|
||||
// videoPrettyJSON returns indented JSON for human-readable output.
|
||||
func videoPrettyJSON(value interface{}) string {
|
||||
func videoPrettyJSON(value any) string {
|
||||
data, err := json.MarshalIndent(value, "", " ")
|
||||
if err != nil {
|
||||
return ""
|
||||
|
||||
@@ -45,7 +45,7 @@ func videoListAction(ctx *cli.Context) error {
|
||||
cols := videoListColumns()
|
||||
|
||||
if format == report.JSON {
|
||||
rows := make([]map[string]interface{}, 0, len(results))
|
||||
rows := make([]map[string]any, 0, len(results))
|
||||
for _, found := range results {
|
||||
rows = append(rows, videoListJSONRow(found))
|
||||
}
|
||||
|
||||
@@ -27,20 +27,12 @@ func videoSearchResults(query string, count int, offset int) ([]search.Photo, er
|
||||
Order: sortby.Name,
|
||||
}
|
||||
|
||||
target := count + offset
|
||||
|
||||
if target < 0 {
|
||||
target = 0
|
||||
}
|
||||
target := max(count+offset, 0)
|
||||
|
||||
collected := make([]search.Photo, 0, target)
|
||||
index := make(map[string]int, target)
|
||||
searchOffset := 0
|
||||
batchSize := count
|
||||
|
||||
if batchSize < 200 {
|
||||
batchSize = 200
|
||||
}
|
||||
batchSize := max(count, 200)
|
||||
|
||||
needComplete := false
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// ApplyCliContext applies the values of the cli context based on the "flag" annotations.
|
||||
func ApplyCliContext(c interface{}, ctx *cli.Context) error {
|
||||
func ApplyCliContext(c any, ctx *cli.Context) error {
|
||||
v := reflect.ValueOf(c).Elem()
|
||||
|
||||
// Iterate through all config fields.
|
||||
|
||||
@@ -25,7 +25,6 @@ func TestApplyCliContext_Duration(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ func (f CliFlag) Skip(tags []string) bool {
|
||||
func (f CliFlag) Fields() reflect.Value {
|
||||
fields := reflect.ValueOf(f.Flag)
|
||||
|
||||
for fields.Kind() == reflect.Ptr {
|
||||
for fields.Kind() == reflect.Pointer {
|
||||
fields = reflect.Indirect(fields)
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ func (f CliFlags) SetHidden(hidden bool, names ...string) (result CliFlags) {
|
||||
if list.Contains(names, flag.Name()) {
|
||||
rv := reflect.ValueOf(flag.Flag)
|
||||
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
if rv.Kind() == reflect.Pointer {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
// Values is a shorthand alias for map[string]interface{}.
|
||||
type Values = map[string]interface{}
|
||||
type Values = map[string]any
|
||||
|
||||
func init() {
|
||||
TotalMem = memory.TotalMemory()
|
||||
@@ -747,12 +747,9 @@ func (c *Config) IndexWorkers() int {
|
||||
}
|
||||
|
||||
// NumCPU returns the number of logical CPU cores.
|
||||
cores := runtime.NumCPU()
|
||||
|
||||
// Limit to physical cores to avoid high load on HT capable CPUs.
|
||||
if cores > cpuid.CPU.PhysicalCores {
|
||||
cores = cpuid.CPU.PhysicalCores
|
||||
}
|
||||
cores := min(
|
||||
// Limit to physical cores to avoid high load on HT capable CPUs.
|
||||
runtime.NumCPU(), cpuid.CPU.PhysicalCores)
|
||||
|
||||
// Limit number of workers when using SQLite3 to avoid database locking issues.
|
||||
if c.DatabaseDriver() == SQLite3 && (cores >= 8 && c.options.IndexWorkers <= 0 || c.options.IndexWorkers > 4) {
|
||||
|
||||
@@ -126,10 +126,7 @@ func (c *Config) FaceEngineThreads() int {
|
||||
if c == nil {
|
||||
return 1
|
||||
} else if c.options.FaceEngineThreads <= 0 {
|
||||
threads := runtime.NumCPU() / 2
|
||||
if threads < 1 {
|
||||
threads = 1
|
||||
}
|
||||
threads := max(runtime.NumCPU()/2, 1)
|
||||
|
||||
c.options.FaceEngineThreads = threads
|
||||
|
||||
|
||||
@@ -162,10 +162,7 @@ func TestConfig_FaceEngineRunType(t *testing.T) {
|
||||
|
||||
func TestConfig_FaceEngineThreads(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
expected := runtime.NumCPU() / 2
|
||||
if expected < 1 {
|
||||
expected = 1
|
||||
}
|
||||
expected := max(runtime.NumCPU()/2, 1)
|
||||
assert.Equal(t, expected, c.FaceEngineThreads())
|
||||
|
||||
c.options.FaceEngineThreads = 8
|
||||
|
||||
@@ -199,7 +199,7 @@ func (c *Config) LegalUrl() string {
|
||||
func (c *Config) RobotsTxt() ([]byte, error) {
|
||||
if c.Demo() && c.Public() {
|
||||
// Allow public demo instances to be indexed.
|
||||
return []byte(fmt.Sprintf("User-agent: *\nDisallow: /\nAllow: %s/\nAllow: %s/\nAllow: .js\nAllow: .css", LibraryUri, StaticUri)), nil
|
||||
return fmt.Appendf(nil, "User-agent: *\nDisallow: /\nAllow: %s/\nAllow: %s/\nAllow: .js\nAllow: .css", LibraryUri, StaticUri), nil
|
||||
} else if c.Public() {
|
||||
// Do not allow other instances to be indexed when public mode is enabled.
|
||||
return robotsTxt, nil
|
||||
|
||||
@@ -111,22 +111,14 @@ func (c *Config) Usage() Usage {
|
||||
info.FilesUsedPct = 1
|
||||
}
|
||||
|
||||
info.FilesFreePct = 100 - info.FilesUsedPct
|
||||
|
||||
if info.FilesFreePct < 0 {
|
||||
info.FilesFreePct = 0
|
||||
}
|
||||
info.FilesFreePct = max(100-info.FilesUsedPct, 0)
|
||||
|
||||
info.UsersActive = query.CountUsers(true, true, nil, []string{"guest"})
|
||||
info.GuestsActive = query.CountUsers(true, true, []string{"guest"}, nil)
|
||||
|
||||
if info.UsersQuota = c.UsersQuota(); info.UsersQuota > 0 {
|
||||
info.UsersUsedPct = int(math.Floor(info.UsersUsedRatio() * 100))
|
||||
info.UsersFreePct = 100 - info.UsersUsedPct
|
||||
|
||||
if info.UsersFreePct < 0 {
|
||||
info.UsersFreePct = 0
|
||||
}
|
||||
info.UsersFreePct = max(100-info.UsersUsedPct, 0)
|
||||
} else {
|
||||
info.UsersUsedPct = 0
|
||||
info.UsersFreePct = 0
|
||||
|
||||
@@ -25,7 +25,7 @@ type Manifest struct {
|
||||
Scope string `json:"scope"`
|
||||
StartUrl string `json:"start_url,omitempty"`
|
||||
Shortcuts Urls `json:"shortcuts"`
|
||||
Serviceworker Serviceworker `json:"serviceworker,omitempty"`
|
||||
Serviceworker Serviceworker `json:"serviceworker"`
|
||||
Permissions list.List `json:"permissions"`
|
||||
OptionalPermissions list.List `json:"optional_permissions"`
|
||||
HostPermissions []string `json:"host_permissions"`
|
||||
|
||||
@@ -99,7 +99,7 @@ func (Album) TableName() string {
|
||||
}
|
||||
|
||||
// UpdateAlbum updates album attributes directly in the database by UID.
|
||||
func UpdateAlbum(albumUID string, values interface{}) (err error) {
|
||||
func UpdateAlbum(albumUID string, values any) (err error) {
|
||||
if rnd.InvalidUID(albumUID, AlbumUID) {
|
||||
return fmt.Errorf("album: invalid uid %s", clean.Log(albumUID))
|
||||
} else if err = Db().Model(Album{}).Where("album_uid = ?", albumUID).UpdateColumns(values).Error; err != nil {
|
||||
@@ -727,7 +727,7 @@ func (m *Album) SaveForm(f *form.Album) error {
|
||||
}
|
||||
|
||||
// Update sets a new value for a database column.
|
||||
func (m *Album) Update(attr string, value interface{}) error {
|
||||
func (m *Album) Update(attr string, value any) error {
|
||||
if m == nil {
|
||||
return errors.New("album must not be nil - you may have found a bug")
|
||||
} else if !m.HasID() {
|
||||
@@ -738,7 +738,7 @@ func (m *Album) Update(attr string, value interface{}) error {
|
||||
}
|
||||
|
||||
// Updates multiple columns in the database.
|
||||
func (m *Album) Updates(values interface{}) error {
|
||||
func (m *Album) Updates(values any) error {
|
||||
if m == nil {
|
||||
return errors.New("album must not be nil - you may have found a bug")
|
||||
} else if !m.HasID() {
|
||||
|
||||
@@ -361,7 +361,7 @@ func (m *Client) Disabled() bool {
|
||||
}
|
||||
|
||||
// Updates multiple properties in the database.
|
||||
func (m *Client) Updates(values interface{}) error {
|
||||
func (m *Client) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ func (m *Session) Delete() error {
|
||||
}
|
||||
|
||||
// Updates multiple properties in the database.
|
||||
func (m *Session) Updates(values interface{}) error {
|
||||
func (m *Session) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -86,13 +87,7 @@ func (data SessionData) HasShare(uid string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, share := range data.Shares {
|
||||
if share == uid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(data.Shares, uid)
|
||||
}
|
||||
|
||||
// SharedUIDs returns shared entity UIDs.
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestDeleteClientSessions(t *testing.T) {
|
||||
assert.Equal(t, 0, DeleteClientSessions(&Client{}, authn.MethodDefault, 0))
|
||||
|
||||
// Create 10 test client sessions.
|
||||
for i := 0; i < 10; i++ {
|
||||
for range 10 {
|
||||
sess := NewSession(3600, 0)
|
||||
sess.SetClient(client)
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ func (m *User) SaveRelated() *User {
|
||||
}
|
||||
|
||||
// Updates multiple properties in the database.
|
||||
func (m *User) Updates(values interface{}) error {
|
||||
func (m *User) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ func (m *UserDetails) Save() error {
|
||||
}
|
||||
|
||||
// Updates multiple properties in the database.
|
||||
func (m *UserDetails) Updates(values interface{}) error {
|
||||
func (m *UserDetails) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ func (m *UserSettings) Save() error {
|
||||
}
|
||||
|
||||
// Updates multiple properties in the database.
|
||||
func (m *UserSettings) Updates(values interface{}) error {
|
||||
func (m *UserSettings) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ func (m *UserShare) Save() error {
|
||||
}
|
||||
|
||||
// Updates changes multiple record values.
|
||||
func (m *UserShare) Updates(values interface{}) error {
|
||||
func (m *UserShare) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ func (m *Details) Save() error {
|
||||
}
|
||||
|
||||
// Update a column in the database.
|
||||
func (m *Details) Update(attr string, value interface{}) error {
|
||||
func (m *Details) Update(attr string, value any) error {
|
||||
if m == nil {
|
||||
return errors.New("photo details must not be nil - you may have found a bug")
|
||||
} else if m.PhotoID < 1 {
|
||||
@@ -75,7 +75,7 @@ func (m *Details) Update(attr string, value interface{}) error {
|
||||
}
|
||||
|
||||
// Updates multiple columns in the database.
|
||||
func (m *Details) Updates(values interface{}) error {
|
||||
func (m *Details) Updates(values any) error {
|
||||
if values == nil {
|
||||
return nil
|
||||
} else if m == nil {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// Count returns the number of records for a given a model and key values.
|
||||
func Count(m interface{}, keys []string, values []interface{}) int {
|
||||
func Count(m any, keys []string, values []any) int {
|
||||
if m == nil || len(keys) != len(values) {
|
||||
log.Debugf("entity: invalid parameters (count records)")
|
||||
return -1
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// Save tries to update an existing record and falls back to insert semantics, retrying on lock errors.
|
||||
func Save(m interface{}, keyNames ...string) (err error) {
|
||||
func Save(m any, keyNames ...string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("save: %s (panic)", r)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
type Tables map[string]interface{}
|
||||
type Tables map[string]any
|
||||
|
||||
// Entities contains database entities and their table names.
|
||||
var Entities = Tables{
|
||||
@@ -105,7 +105,7 @@ func (list Tables) Truncate(db *gorm.DB) {
|
||||
// Migrate migrates all database tables of registered entities.
|
||||
func (list Tables) Migrate(db *gorm.DB, opt migrate.Options) {
|
||||
var name string
|
||||
var entity interface{}
|
||||
var entity any
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// Update updates the values of an existing database record.
|
||||
func Update(m interface{}, keyNames ...string) (err error) {
|
||||
func Update(m any, keyNames ...string) (err error) {
|
||||
// Use an unscoped *gorm.DB connection, so that
|
||||
// soft-deleted database records can also be updated.
|
||||
db := UnscopedDb()
|
||||
|
||||
@@ -3,25 +3,20 @@ package entity
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Values is a shorthand alias for map[string]interface{}.
|
||||
type Values = map[string]interface{}
|
||||
type Values = map[string]any
|
||||
|
||||
// Map is retained for backward compatibility.
|
||||
// TODO: Remove when no longer needed.
|
||||
type Map = Values
|
||||
|
||||
// ModelValues extracts exported struct fields into a Values map, optionally omitting selected names.
|
||||
func ModelValues(m interface{}, omit ...string) (result Values, omitted []interface{}, err error) {
|
||||
func ModelValues(m any, omit ...string) (result Values, omitted []any, err error) {
|
||||
mustOmit := func(name string) bool {
|
||||
for _, s := range omit {
|
||||
if name == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(omit, name)
|
||||
}
|
||||
|
||||
r := reflect.ValueOf(m)
|
||||
@@ -39,11 +34,11 @@ func ModelValues(m interface{}, omit ...string) (result Values, omitted []interf
|
||||
t := values.Type()
|
||||
num := t.NumField()
|
||||
|
||||
omitted = make([]interface{}, 0, len(omit))
|
||||
omitted = make([]any, 0, len(omit))
|
||||
result = make(Values, num)
|
||||
|
||||
// Add exported fields to result.
|
||||
for i := 0; i < num; i++ {
|
||||
for i := range num {
|
||||
field := t.Field(i)
|
||||
|
||||
// Skip non-exported fields.
|
||||
|
||||
@@ -396,7 +396,7 @@ func (m *Face) Delete() error {
|
||||
}
|
||||
|
||||
// Update a face property in the database.
|
||||
func (m *Face) Update(attr string, value interface{}) error {
|
||||
func (m *Face) Update(attr string, value any) error {
|
||||
if m.ID == "" {
|
||||
return fmt.Errorf("empty id")
|
||||
}
|
||||
@@ -407,7 +407,7 @@ func (m *Face) Update(attr string, value interface{}) error {
|
||||
}
|
||||
|
||||
// Updates face properties in the database.
|
||||
func (m *Face) Updates(values interface{}) error {
|
||||
func (m *Face) Updates(values any) error {
|
||||
if m.ID == "" {
|
||||
return fmt.Errorf("empty id")
|
||||
}
|
||||
|
||||
@@ -516,12 +516,12 @@ func (m *File) UpdateVideoInfos() error {
|
||||
}
|
||||
|
||||
// Update updates a column in the database.
|
||||
func (m *File) Update(attr string, value interface{}) error {
|
||||
func (m *File) Update(attr string, value any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
|
||||
}
|
||||
|
||||
// Updates multiple columns in the database.
|
||||
func (m *File) Updates(values interface{}) error {
|
||||
func (m *File) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumns(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ func (m *File) MarshalJSON() ([]byte, error) {
|
||||
Software string `json:",omitempty"`
|
||||
Error string `json:",omitempty"`
|
||||
ModTime int64 `json:",omitempty"`
|
||||
CreatedAt time.Time `json:",omitempty"`
|
||||
CreatedIn int64 `json:",omitempty"`
|
||||
UpdatedAt time.Time `json:",omitempty"`
|
||||
UpdatedIn int64 `json:",omitempty"`
|
||||
DeletedAt *time.Time `json:",omitempty"`
|
||||
Markers *Markers `json:",omitempty"`
|
||||
CreatedAt time.Time
|
||||
CreatedIn int64 `json:",omitempty"`
|
||||
UpdatedAt time.Time
|
||||
UpdatedIn int64 `json:",omitempty"`
|
||||
DeletedAt *time.Time `json:",omitempty"`
|
||||
Markers *Markers `json:",omitempty"`
|
||||
}{
|
||||
UID: m.FileUID,
|
||||
PhotoUID: m.PhotoUID,
|
||||
|
||||
@@ -45,12 +45,12 @@ func NewFileShare(fileID, accountID uint, remoteName string) *FileShare {
|
||||
}
|
||||
|
||||
// Updates mutates multiple columns on the existing row.
|
||||
func (m *FileShare) Updates(values interface{}) error {
|
||||
func (m *FileShare) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumns(values).Error
|
||||
}
|
||||
|
||||
// Update mutates a single column on the existing row.
|
||||
func (m *FileShare) Update(attr string, value interface{}) error {
|
||||
func (m *FileShare) Update(attr string, value any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ type FileSync struct {
|
||||
RemoteName string `gorm:"primary_key;auto_increment:false;type:VARBINARY(255)" json:"RemoteName" yaml:"RemoteName,omitempty"`
|
||||
ServiceID uint `gorm:"primary_key;auto_increment:false" json:"ServiceID" yaml:"ServiceID,omitempty"`
|
||||
FileID uint `gorm:"index;" json:"FileID" yaml:"FileID,omitempty"`
|
||||
RemoteDate time.Time `json:"RemoteDate,omitempty" yaml:"RemoteDate,omitempty"`
|
||||
RemoteDate time.Time `json:"RemoteDate" yaml:"RemoteDate,omitempty"`
|
||||
RemoteSize int64 `json:"RemoteSize,omitempty" yaml:"RemoteSize,omitempty"`
|
||||
Status string `gorm:"type:VARBINARY(16);" json:"Status" yaml:"Status,omitempty"`
|
||||
Error string `gorm:"type:VARBINARY(512);" json:"Error,omitempty" yaml:"Error,omitempty"`
|
||||
@@ -46,12 +46,12 @@ func NewFileSync(accountID uint, remoteName string) *FileSync {
|
||||
}
|
||||
|
||||
// Updates mutates multiple columns on the existing row.
|
||||
func (m *FileSync) Updates(values interface{}) error {
|
||||
func (m *FileSync) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumns(values).Error
|
||||
}
|
||||
|
||||
// Update mutates a single column on the existing row.
|
||||
func (m *FileSync) Update(attr string, value interface{}) error {
|
||||
func (m *FileSync) Update(attr string, value any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ type Folder struct {
|
||||
FileCount int `gorm:"-" json:"FileCount" yaml:"-"`
|
||||
CreatedAt time.Time `json:"-" yaml:"-"`
|
||||
UpdatedAt time.Time `json:"-" yaml:"-"`
|
||||
ModifiedAt time.Time `json:"ModifiedAt,omitempty" yaml:"-"`
|
||||
ModifiedAt time.Time `json:"ModifiedAt" yaml:"-"`
|
||||
PublishedAt *time.Time `sql:"index" json:"PublishedAt,omitempty" yaml:"PublishedAt,omitempty"`
|
||||
DeletedAt *time.Time `sql:"index" json:"-" yaml:"DeletedAt,omitempty"`
|
||||
}
|
||||
@@ -230,7 +230,7 @@ func FirstOrCreateFolder(m *Folder) *Folder {
|
||||
}
|
||||
|
||||
// Updates selected properties in the database.
|
||||
func (m *Folder) Updates(values interface{}) error {
|
||||
func (m *Folder) Updates(values any) error {
|
||||
return Db().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ func NewKeyword(keyword string) *Keyword {
|
||||
|
||||
// Update modifies a single column on an already persisted keyword and relies on
|
||||
// the standard GORM callback to evict the cached instance afterwards.
|
||||
func (m *Keyword) Update(attr string, value interface{}) error {
|
||||
func (m *Keyword) Update(attr string, value any) error {
|
||||
if m == nil {
|
||||
return errors.New("keyword must not be nil - you may have found a bug")
|
||||
} else if !m.HasID() {
|
||||
@@ -50,7 +50,7 @@ func (m *Keyword) Update(attr string, value interface{}) error {
|
||||
|
||||
// Updates applies a set of column changes to an existing keyword while keeping
|
||||
// the cache consistent via the AfterUpdate hook.
|
||||
func (m *Keyword) Updates(values interface{}) error {
|
||||
func (m *Keyword) Updates(values any) error {
|
||||
if values == nil {
|
||||
return nil
|
||||
} else if m == nil {
|
||||
|
||||
@@ -201,7 +201,7 @@ func (m *Label) Skip() bool {
|
||||
}
|
||||
|
||||
// Update a label property in the database.
|
||||
func (m *Label) Update(attr string, value interface{}) error {
|
||||
func (m *Label) Update(attr string, value any) error {
|
||||
if m == nil {
|
||||
return errors.New("label must not be nil - you may have found a bug")
|
||||
} else if !m.HasID() {
|
||||
@@ -212,7 +212,7 @@ func (m *Label) Update(attr string, value interface{}) error {
|
||||
}
|
||||
|
||||
// Updates multiple columns in the database.
|
||||
func (m *Label) Updates(values interface{}) error {
|
||||
func (m *Label) Updates(values any) error {
|
||||
if values == nil {
|
||||
return nil
|
||||
} else if m == nil {
|
||||
|
||||
@@ -142,13 +142,13 @@ func (m *Marker) UpdateFile(file *File) (updated bool) {
|
||||
}
|
||||
|
||||
// Updates multiple columns in the database.
|
||||
func (m *Marker) Updates(values interface{}) error {
|
||||
func (m *Marker) Updates(values any) error {
|
||||
UpdateFaces.Store(true)
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
// Update updates a column in the database.
|
||||
func (m *Marker) Update(attr string, value interface{}) error {
|
||||
func (m *Marker) Update(attr string, value any) error {
|
||||
UpdateFaces.Store(true)
|
||||
return UnscopedDb().Model(m).Update(attr, value).Error
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
// This generates countries.go by running "go generate"
|
||||
package main
|
||||
|
||||
@@ -34,4 +34,4 @@ import (
|
||||
var log = event.Log
|
||||
|
||||
// Map is a shorthand alias for map[string]interface{}.
|
||||
type Map = map[string]interface{}
|
||||
type Map = map[string]any
|
||||
|
||||
@@ -105,7 +105,7 @@ func (m *Passcode) Delete() (err error) {
|
||||
}
|
||||
|
||||
// Updates multiple properties in the database.
|
||||
func (m *Passcode) Updates(values interface{}) error {
|
||||
func (m *Passcode) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -99,8 +100,8 @@ type Photo struct {
|
||||
Files []File `yaml:"-"`
|
||||
Labels []PhotoLabel `yaml:"-"`
|
||||
CreatedBy string `gorm:"type:VARBINARY(42);index" json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty"`
|
||||
CreatedAt time.Time `json:"CreatedAt,omitempty" yaml:"CreatedAt,omitempty"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt,omitempty" yaml:"UpdatedAt,omitempty"`
|
||||
CreatedAt time.Time `json:"CreatedAt" yaml:"CreatedAt,omitempty"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt" yaml:"UpdatedAt,omitempty"`
|
||||
EditedAt *time.Time `json:"EditedAt,omitempty" yaml:"EditedAt,omitempty"`
|
||||
PublishedAt *time.Time `sql:"index" json:"PublishedAt,omitempty" yaml:"PublishedAt,omitempty"`
|
||||
IndexedAt *time.Time `json:"IndexedAt,omitempty" yaml:"-"`
|
||||
@@ -332,7 +333,7 @@ func (m *Photo) Save() error {
|
||||
}
|
||||
|
||||
// Update a column in the database.
|
||||
func (m *Photo) Update(attr string, value interface{}) error {
|
||||
func (m *Photo) Update(attr string, value any) error {
|
||||
if m == nil {
|
||||
return errors.New("photo must not be nil - you may have found a bug")
|
||||
} else if !m.HasID() {
|
||||
@@ -343,7 +344,7 @@ func (m *Photo) Update(attr string, value interface{}) error {
|
||||
}
|
||||
|
||||
// Updates multiple columns in the database.
|
||||
func (m *Photo) Updates(values interface{}) error {
|
||||
func (m *Photo) Updates(values any) error {
|
||||
if values == nil {
|
||||
return nil
|
||||
} else if m == nil {
|
||||
@@ -411,13 +412,7 @@ func (m *Photo) ResetDuration() {
|
||||
func (m *Photo) HasMediaType(types ...media.Type) bool {
|
||||
mediaType := m.MediaType()
|
||||
|
||||
for _, t := range types {
|
||||
if mediaType == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(types, mediaType)
|
||||
}
|
||||
|
||||
// SetMediaType sets a new media type if its priority is higher than that of the current type.
|
||||
@@ -944,11 +939,7 @@ func (m *Photo) AddLabels(labels classify.Labels) {
|
||||
|
||||
template := NewPhotoLabel(m.ID, labelEntity.ID, classifyLabel.Uncertainty, labelSrc)
|
||||
template.Topicality = classifyLabel.Topicality
|
||||
score := 0
|
||||
|
||||
if classifyLabel.NSFWConfidence > 0 {
|
||||
score = classifyLabel.NSFWConfidence
|
||||
}
|
||||
score := max(classifyLabel.NSFWConfidence, 0)
|
||||
|
||||
if classifyLabel.NSFW && score == 0 {
|
||||
score = 100
|
||||
@@ -979,10 +970,7 @@ func (m *Photo) AddLabels(labels classify.Labels) {
|
||||
}
|
||||
|
||||
if classifyLabel.NSFWConfidence > 0 || classifyLabel.NSFW {
|
||||
nsfwScore := 0
|
||||
if classifyLabel.NSFWConfidence > 0 {
|
||||
nsfwScore = classifyLabel.NSFWConfidence
|
||||
}
|
||||
nsfwScore := max(classifyLabel.NSFWConfidence, 0)
|
||||
if classifyLabel.NSFW && nsfwScore == 0 {
|
||||
nsfwScore = 100
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func NewPhotoLabel(photoID, labelID uint, uncertainty int, source string) *Photo
|
||||
}
|
||||
|
||||
// Updates mutates multiple columns in the database and clears cached copies.
|
||||
func (m *PhotoLabel) Updates(values interface{}) error {
|
||||
func (m *PhotoLabel) Updates(values any) error {
|
||||
if m == nil {
|
||||
return errors.New("photo label must not be nil - you may have found a bug")
|
||||
} else if !m.HasID() {
|
||||
@@ -58,7 +58,7 @@ func (m *PhotoLabel) Updates(values interface{}) error {
|
||||
}
|
||||
|
||||
// Update mutates a single column in the database and clears cached copies.
|
||||
func (m *PhotoLabel) Update(attr string, value interface{}) error {
|
||||
func (m *PhotoLabel) Update(attr string, value any) error {
|
||||
if m == nil {
|
||||
return errors.New("photo label must not be nil - you may have found a bug")
|
||||
} else if !m.HasID() {
|
||||
|
||||
@@ -475,7 +475,6 @@ func TestPhoto_ShouldGenerateCaption(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range ctx {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := tc.photo.ShouldGenerateCaption(tc.source, tc.force)
|
||||
assert.Equal(t, tc.expect, result)
|
||||
|
||||
@@ -188,11 +188,7 @@ func PurgeOrphanFaces(faceIds []string, ignored bool) (affected int, err error)
|
||||
batchSize := BatchSize()
|
||||
|
||||
for i := 0; i < len(faceIds); i += batchSize {
|
||||
j := i + batchSize
|
||||
|
||||
if j > len(faceIds) {
|
||||
j = len(faceIds)
|
||||
}
|
||||
j := min(i+batchSize, len(faceIds))
|
||||
|
||||
// Next batch.
|
||||
ids := faceIds[i:j]
|
||||
|
||||
@@ -175,7 +175,7 @@ func TestFlagHiddenPhotos(t *testing.T) {
|
||||
t.Run("SuccessWith1000", func(t *testing.T) {
|
||||
var checkedTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
// Load 1000 photos that need to be hidden
|
||||
for i := 0; i < 1000; i++ {
|
||||
for range 1000 {
|
||||
newPhoto := entity.Photo{ //JPG, Geo from metadata, indexed
|
||||
//ID: 1000049,
|
||||
PhotoUID: rnd.GenerateUID(entity.PhotoUID),
|
||||
|
||||
@@ -34,7 +34,7 @@ type Album struct {
|
||||
LinkCount int `json:"LinkCount"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
DeletedAt time.Time `json:"DeletedAt,omitempty"`
|
||||
DeletedAt time.Time `json:"DeletedAt"`
|
||||
}
|
||||
|
||||
type AlbumResults []Album
|
||||
|
||||
@@ -184,7 +184,7 @@ func AnySlug(col, search, sep string) (where string) {
|
||||
var wheres []string
|
||||
var words []string
|
||||
|
||||
for _, w := range strings.Split(search, sep) {
|
||||
for w := range strings.SplitSeq(search, sep) {
|
||||
w = strings.TrimSpace(w)
|
||||
|
||||
words = append(words, txt.Slug(w))
|
||||
@@ -225,7 +225,7 @@ func AnyInt(col, numbers, sep string, min, max int) (where string) {
|
||||
var matches []int
|
||||
var wheres []string
|
||||
|
||||
for _, n := range strings.Split(numbers, sep) {
|
||||
for n := range strings.SplitSeq(numbers, sep) {
|
||||
i := txt.Int(n)
|
||||
|
||||
if i == 0 || i < min || i > max {
|
||||
@@ -249,19 +249,19 @@ func AnyInt(col, numbers, sep string, min, max int) (where string) {
|
||||
// OrLike prepares a parameterised OR/LIKE clause for a single column. Star (* )
|
||||
// wildcards are mapped to SQL percent wildcards before returning the query and
|
||||
// bind values.
|
||||
func OrLike(col, s string) (where string, values []interface{}) {
|
||||
func OrLike(col, s string) (where string, values []any) {
|
||||
if txt.Empty(col) || txt.Empty(s) {
|
||||
return "", []interface{}{}
|
||||
return "", []any{}
|
||||
}
|
||||
|
||||
s = strings.ReplaceAll(s, "*", "%")
|
||||
s = strings.ReplaceAll(s, "%%", "%")
|
||||
|
||||
terms := txt.UnTrimmedSplitWithEscape(s, txt.OrRune, txt.EscapeRune)
|
||||
values = make([]interface{}, len(terms))
|
||||
values = make([]any, len(terms))
|
||||
|
||||
if l := len(terms); l == 0 {
|
||||
return "", []interface{}{}
|
||||
return "", []any{}
|
||||
} else if l == 1 {
|
||||
values[0] = terms[0]
|
||||
} else {
|
||||
@@ -279,9 +279,9 @@ func OrLike(col, s string) (where string, values []interface{}) {
|
||||
// OrLikeCols behaves like OrLike but fans out the same search terms across
|
||||
// multiple columns, preserving the order of values so callers can feed them to
|
||||
// database/sql.
|
||||
func OrLikeCols(cols []string, s string) (where string, values []interface{}) {
|
||||
func OrLikeCols(cols []string, s string) (where string, values []any) {
|
||||
if len(cols) == 0 || txt.Empty(s) {
|
||||
return "", []interface{}{}
|
||||
return "", []any{}
|
||||
}
|
||||
|
||||
s = strings.ReplaceAll(s, "*", "%")
|
||||
@@ -290,10 +290,10 @@ func OrLikeCols(cols []string, s string) (where string, values []interface{}) {
|
||||
terms := txt.UnTrimmedSplitWithEscape(s, txt.OrRune, txt.EscapeRune)
|
||||
|
||||
if len(terms) == 0 {
|
||||
return "", []interface{}{}
|
||||
return "", []any{}
|
||||
}
|
||||
|
||||
values = make([]interface{}, len(terms)*len(cols))
|
||||
values = make([]any, len(terms)*len(cols))
|
||||
|
||||
for j := range terms {
|
||||
for i := range cols {
|
||||
|
||||
@@ -322,31 +322,31 @@ func TestOrLike(t *testing.T) {
|
||||
where, values := OrLike("k.keyword", "")
|
||||
|
||||
assert.Equal(t, "", where)
|
||||
assert.Equal(t, []interface{}{}, values)
|
||||
assert.Equal(t, []any{}, values)
|
||||
})
|
||||
t.Run("OneTerm", func(t *testing.T) {
|
||||
where, values := OrLike("k.keyword", "bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"bar"}, values)
|
||||
assert.Equal(t, []any{"bar"}, values)
|
||||
})
|
||||
t.Run("TwoTerms", func(t *testing.T) {
|
||||
where, values := OrLike("k.keyword", "foo*%|bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"foo%", "bar"}, values)
|
||||
assert.Equal(t, []any{"foo%", "bar"}, values)
|
||||
})
|
||||
t.Run("OneFilename", func(t *testing.T) {
|
||||
where, values := OrLike("files.file_name", " 2790/07/27900704_070228_D6D51B6C.jpg")
|
||||
|
||||
assert.Equal(t, "files.file_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
assert.Equal(t, []any{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
})
|
||||
t.Run("TwoFilenames", func(t *testing.T) {
|
||||
where, values := OrLike("files.file_name", "1990*|2790/07/27900704_070228_D6D51B6C.jpg")
|
||||
|
||||
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
assert.Equal(t, []any{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -355,55 +355,55 @@ func TestOrLikeCols(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "")
|
||||
|
||||
assert.Equal(t, "", where)
|
||||
assert.Equal(t, []interface{}{}, values)
|
||||
assert.Equal(t, []any{}, values)
|
||||
})
|
||||
t.Run("OneTerm", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ? OR p.photo_caption LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"bar", "bar"}, values)
|
||||
assert.Equal(t, []any{"bar", "bar"}, values)
|
||||
})
|
||||
t.Run("TwoTerms", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "foo*%|bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ? OR p.photo_caption LIKE ? OR p.photo_caption LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"foo%", "bar", "foo%", "bar"}, values)
|
||||
assert.Equal(t, []any{"foo%", "bar", "foo%", "bar"}, values)
|
||||
})
|
||||
t.Run("OneTermEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "\\|bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ? OR p.photo_caption LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"|bar", "|bar"}, values)
|
||||
assert.Equal(t, []any{"|bar", "|bar"}, values)
|
||||
})
|
||||
t.Run("TwoTermsEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "foo*%|\\|bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ? OR p.photo_caption LIKE ? OR p.photo_caption LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"foo%", "|bar", "foo%", "|bar"}, values)
|
||||
assert.Equal(t, []any{"foo%", "|bar", "foo%", "|bar"}, values)
|
||||
})
|
||||
t.Run("OneFilename", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"files.file_name"}, " 2790/07/27900704_070228_D6D51B6C.jpg")
|
||||
|
||||
assert.Equal(t, "files.file_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
assert.Equal(t, []any{" 2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
})
|
||||
t.Run("TwoFilenames", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"files.file_name", "photos.photo_name"}, "1990*|2790/07/27900704_070228_D6D51B6C.jpg")
|
||||
|
||||
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ? OR photos.photo_name LIKE ? OR photos.photo_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
assert.Equal(t, []any{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
})
|
||||
t.Run("OneFilenameEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"files.file_name"}, " 2790/07/27900704_070228_D6D\\|51B6C.jpg")
|
||||
|
||||
assert.Equal(t, "files.file_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{" 2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
|
||||
assert.Equal(t, []any{" 2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
|
||||
})
|
||||
t.Run("TwoFilenamesEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"files.file_name", "photos.photo_name"}, "1990*|2790/07/27900704_070228_D6D\\|51B6C.jpg")
|
||||
|
||||
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ? OR photos.photo_name LIKE ? OR photos.photo_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D|51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
|
||||
assert.Equal(t, []any{"1990%", "2790/07/27900704_070228_D6D|51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -21,5 +21,5 @@ type Label struct {
|
||||
ThumbSrc string `json:"ThumbSrc,omitempty"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
DeletedAt time.Time `json:"DeletedAt,omitempty"`
|
||||
DeletedAt time.Time `json:"DeletedAt"`
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ type Photo struct {
|
||||
Merged bool `json:"Merged" select:"-"`
|
||||
CreatedAt time.Time `json:"CreatedAt" select:"photos.created_at"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt" select:"photos.updated_at"`
|
||||
EditedAt time.Time `json:"EditedAt,omitempty" select:"photos.edited_at"`
|
||||
CheckedAt time.Time `json:"CheckedAt,omitempty" select:"photos.checked_at"`
|
||||
EditedAt time.Time `json:"EditedAt" select:"photos.edited_at"`
|
||||
CheckedAt time.Time `json:"CheckedAt" select:"photos.checked_at"`
|
||||
DeletedAt *time.Time `json:"DeletedAt,omitempty" select:"photos.deleted_at"`
|
||||
|
||||
// Additional information from the details table.
|
||||
|
||||
@@ -11,15 +11,15 @@ import (
|
||||
type Cols []string
|
||||
|
||||
// SelectString returns the columns for a search result struct as a string.
|
||||
func SelectString(f interface{}, tags []string) string {
|
||||
func SelectString(f any, tags []string) string {
|
||||
return strings.Join(SelectCols(f, tags), ", ")
|
||||
}
|
||||
|
||||
// SelectCols returns the columns for a search result struct.
|
||||
func SelectCols(f interface{}, tags []string) Cols {
|
||||
func SelectCols(f any, tags []string) Cols {
|
||||
v := reflect.ValueOf(f)
|
||||
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.Kind() == reflect.Pointer {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
|
||||
@@ -214,12 +214,12 @@ func (m *Service) Directories() (result fs.FileInfos, err error) {
|
||||
}
|
||||
|
||||
// Updates multiple columns in the database.
|
||||
func (m *Service) Updates(values interface{}) error {
|
||||
func (m *Service) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumns(values).Error
|
||||
}
|
||||
|
||||
// Update a column in the database.
|
||||
func (m *Service) Update(attr string, value interface{}) error {
|
||||
func (m *Service) Update(attr string, value any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -192,12 +192,12 @@ func (m *Subject) Restore() error {
|
||||
}
|
||||
|
||||
// Update updates an entity value in the database.
|
||||
func (m *Subject) Update(attr string, value interface{}) error {
|
||||
func (m *Subject) Update(attr string, value any) error {
|
||||
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
|
||||
}
|
||||
|
||||
// Updates multiple values in the database.
|
||||
func (m *Subject) Updates(values interface{}) error {
|
||||
func (m *Subject) Updates(values any) error {
|
||||
return UnscopedDb().Model(m).Updates(values).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ var AuditLog Logger
|
||||
var AuditPrefix = "audit: "
|
||||
|
||||
// Audit optionally reports security-relevant events.
|
||||
func Audit(level logrus.Level, ev []string, args ...interface{}) {
|
||||
func Audit(level logrus.Level, ev []string, args ...any) {
|
||||
// Skip if empty.
|
||||
if len(ev) == 0 {
|
||||
return
|
||||
@@ -41,21 +41,21 @@ func Audit(level logrus.Level, ev []string, args ...interface{}) {
|
||||
}
|
||||
|
||||
// AuditDebug records an audit entry at debug level.
|
||||
func AuditDebug(ev []string, args ...interface{}) {
|
||||
func AuditDebug(ev []string, args ...any) {
|
||||
Audit(logrus.DebugLevel, ev, args...)
|
||||
}
|
||||
|
||||
// AuditInfo records an audit entry at info level.
|
||||
func AuditInfo(ev []string, args ...interface{}) {
|
||||
func AuditInfo(ev []string, args ...any) {
|
||||
Audit(logrus.InfoLevel, ev, args...)
|
||||
}
|
||||
|
||||
// AuditWarn records an audit entry at warning level.
|
||||
func AuditWarn(ev []string, args ...interface{}) {
|
||||
func AuditWarn(ev []string, args ...any) {
|
||||
Audit(logrus.WarnLevel, ev, args...)
|
||||
}
|
||||
|
||||
// AuditErr records an audit entry at error level.
|
||||
func AuditErr(ev []string, args ...interface{}) {
|
||||
func AuditErr(ev []string, args ...any) {
|
||||
Audit(logrus.ErrorLevel, ev, args...)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ import (
|
||||
var MessageSep = " › "
|
||||
|
||||
// Format formats an audit log event.
|
||||
func Format(ev []string, args ...interface{}) string {
|
||||
func Format(ev []string, args ...any) string {
|
||||
return fmt.Sprintf(strings.Join(ev, MessageSep), args...)
|
||||
}
|
||||
|
||||
+31
-31
@@ -10,32 +10,32 @@ import (
|
||||
|
||||
// Logger is a logrus compatible logger interface.
|
||||
type Logger interface {
|
||||
WithField(key string, value interface{}) *logrus.Entry
|
||||
WithField(key string, value any) *logrus.Entry
|
||||
WithFields(fields logrus.Fields) *logrus.Entry
|
||||
WithError(err error) *logrus.Entry
|
||||
WithContext(ctx context.Context) *logrus.Entry
|
||||
WithTime(t time.Time) *logrus.Entry
|
||||
Logf(level logrus.Level, format string, args ...interface{})
|
||||
Tracef(format string, args ...interface{})
|
||||
Debugf(format string, args ...interface{})
|
||||
Infof(format string, args ...interface{})
|
||||
Printf(format string, args ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
Warningf(format string, args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
Panicf(format string, args ...interface{})
|
||||
Log(level logrus.Level, args ...interface{})
|
||||
Logf(level logrus.Level, format string, args ...any)
|
||||
Tracef(format string, args ...any)
|
||||
Debugf(format string, args ...any)
|
||||
Infof(format string, args ...any)
|
||||
Printf(format string, args ...any)
|
||||
Warnf(format string, args ...any)
|
||||
Warningf(format string, args ...any)
|
||||
Errorf(format string, args ...any)
|
||||
Fatalf(format string, args ...any)
|
||||
Panicf(format string, args ...any)
|
||||
Log(level logrus.Level, args ...any)
|
||||
LogFn(level logrus.Level, fn logrus.LogFunction)
|
||||
Trace(args ...interface{})
|
||||
Debug(args ...interface{})
|
||||
Info(args ...interface{})
|
||||
Print(args ...interface{})
|
||||
Warn(args ...interface{})
|
||||
Warning(args ...interface{})
|
||||
Error(args ...interface{})
|
||||
Fatal(args ...interface{})
|
||||
Panic(args ...interface{})
|
||||
Trace(args ...any)
|
||||
Debug(args ...any)
|
||||
Info(args ...any)
|
||||
Print(args ...any)
|
||||
Warn(args ...any)
|
||||
Warning(args ...any)
|
||||
Error(args ...any)
|
||||
Fatal(args ...any)
|
||||
Panic(args ...any)
|
||||
TraceFn(fn logrus.LogFunction)
|
||||
DebugFn(fn logrus.LogFunction)
|
||||
InfoFn(fn logrus.LogFunction)
|
||||
@@ -45,16 +45,16 @@ type Logger interface {
|
||||
ErrorFn(fn logrus.LogFunction)
|
||||
FatalFn(fn logrus.LogFunction)
|
||||
PanicFn(fn logrus.LogFunction)
|
||||
Logln(level logrus.Level, args ...interface{})
|
||||
Traceln(args ...interface{})
|
||||
Debugln(args ...interface{})
|
||||
Infoln(args ...interface{})
|
||||
Println(args ...interface{})
|
||||
Warnln(args ...interface{})
|
||||
Warningln(args ...interface{})
|
||||
Errorln(args ...interface{})
|
||||
Fatalln(args ...interface{})
|
||||
Panicln(args ...interface{})
|
||||
Logln(level logrus.Level, args ...any)
|
||||
Traceln(args ...any)
|
||||
Debugln(args ...any)
|
||||
Infoln(args ...any)
|
||||
Println(args ...any)
|
||||
Warnln(args ...any)
|
||||
Warningln(args ...any)
|
||||
Errorln(args ...any)
|
||||
Fatalln(args ...any)
|
||||
Panicln(args ...any)
|
||||
Exit(code int)
|
||||
SetNoLock()
|
||||
SetLevel(level logrus.Level)
|
||||
|
||||
@@ -39,21 +39,21 @@ func Warn(msg string) {
|
||||
}
|
||||
|
||||
// ErrorMsg publishes a localized error notification.
|
||||
func ErrorMsg(id i18n.Message, params ...interface{}) {
|
||||
func ErrorMsg(id i18n.Message, params ...any) {
|
||||
Error(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
// SuccessMsg publishes a localized success notification.
|
||||
func SuccessMsg(id i18n.Message, params ...interface{}) {
|
||||
func SuccessMsg(id i18n.Message, params ...any) {
|
||||
Success(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
// InfoMsg publishes a localized informational notification.
|
||||
func InfoMsg(id i18n.Message, params ...interface{}) {
|
||||
func InfoMsg(id i18n.Message, params ...any) {
|
||||
Info(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
// WarnMsg publishes a localized warning notification.
|
||||
func WarnMsg(id i18n.Message, params ...interface{}) {
|
||||
func WarnMsg(id i18n.Message, params ...any) {
|
||||
Warn(i18n.Msg(id, params...))
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const (
|
||||
)
|
||||
|
||||
// PublishEntities publishes updated entity data.
|
||||
func PublishEntities(channel, ev string, entities interface{}) {
|
||||
func PublishEntities(channel, ev string, entities any) {
|
||||
if channel == "" || ev == "" || entities == nil {
|
||||
return
|
||||
}
|
||||
@@ -29,7 +29,7 @@ func PublishEntities(channel, ev string, entities interface{}) {
|
||||
}
|
||||
|
||||
// PublishUserEntities publishes updated entity data for a user.
|
||||
func PublishUserEntities(channel, ev string, entities interface{}, userUid string) {
|
||||
func PublishUserEntities(channel, ev string, entities any, userUid string) {
|
||||
if userUid == "" {
|
||||
PublishEntities(channel, ev, entities)
|
||||
return
|
||||
@@ -46,26 +46,26 @@ func PublishUserEntities(channel, ev string, entities interface{}, userUid strin
|
||||
}
|
||||
|
||||
// EntitiesUpdated publishes an update notification for the given channel.
|
||||
func EntitiesUpdated(channel string, entities interface{}) {
|
||||
func EntitiesUpdated(channel string, entities any) {
|
||||
PublishEntities(channel, EntityUpdated, entities)
|
||||
}
|
||||
|
||||
// EntitiesCreated publishes a create notification for the given channel.
|
||||
func EntitiesCreated(channel string, entities interface{}) {
|
||||
func EntitiesCreated(channel string, entities any) {
|
||||
PublishEntities(channel, EntityCreated, entities)
|
||||
}
|
||||
|
||||
// EntitiesDeleted publishes a delete notification for the given channel.
|
||||
func EntitiesDeleted(channel string, entities interface{}) {
|
||||
func EntitiesDeleted(channel string, entities any) {
|
||||
PublishEntities(channel, EntityDeleted, entities)
|
||||
}
|
||||
|
||||
// EntitiesArchived publishes an archive notification for the given channel.
|
||||
func EntitiesArchived(channel string, entities interface{}) {
|
||||
func EntitiesArchived(channel string, entities any) {
|
||||
PublishEntities(channel, EntityArchived, entities)
|
||||
}
|
||||
|
||||
// EntitiesRestored publishes a restore notification for the given channel.
|
||||
func EntitiesRestored(channel string, entities interface{}) {
|
||||
func EntitiesRestored(channel string, entities any) {
|
||||
PublishEntities(channel, EntityRestored, entities)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var SystemLog Logger
|
||||
var SystemPrefix = "system: "
|
||||
|
||||
// System writes a system-level log entry and publishes it to the hub.
|
||||
func System(level logrus.Level, ev []string, args ...interface{}) {
|
||||
func System(level logrus.Level, ev []string, args ...any) {
|
||||
if len(ev) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -35,21 +35,21 @@ func System(level logrus.Level, ev []string, args ...interface{}) {
|
||||
}
|
||||
|
||||
// SystemDebug records a system debug message.
|
||||
func SystemDebug(ev []string, args ...interface{}) {
|
||||
func SystemDebug(ev []string, args ...any) {
|
||||
System(logrus.DebugLevel, ev, args...)
|
||||
}
|
||||
|
||||
// SystemInfo records a system info message.
|
||||
func SystemInfo(ev []string, args ...interface{}) {
|
||||
func SystemInfo(ev []string, args ...any) {
|
||||
System(logrus.InfoLevel, ev, args...)
|
||||
}
|
||||
|
||||
// SystemWarn records a system warning.
|
||||
func SystemWarn(ev []string, args ...interface{}) {
|
||||
func SystemWarn(ev []string, args ...any) {
|
||||
System(logrus.WarnLevel, ev, args...)
|
||||
}
|
||||
|
||||
// SystemError records a system error message.
|
||||
func SystemError(ev []string, args ...interface{}) {
|
||||
func SystemError(ev []string, args ...any) {
|
||||
System(logrus.ErrorLevel, ev, args...)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ type systemTestLogger struct {
|
||||
|
||||
type logEntry struct {
|
||||
level logrus.Level
|
||||
args []interface{}
|
||||
args []any
|
||||
}
|
||||
|
||||
func newSystemTestLogger() *systemTestLogger {
|
||||
@@ -29,11 +29,11 @@ func newSystemTestLogger() *systemTestLogger {
|
||||
return &systemTestLogger{Logger: logger}
|
||||
}
|
||||
|
||||
func (l *systemTestLogger) Log(level logrus.Level, args ...interface{}) {
|
||||
func (l *systemTestLogger) Log(level logrus.Level, args ...any) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
entryArgs := append([]interface{}(nil), args...)
|
||||
entryArgs := append([]any(nil), args...)
|
||||
l.entries = append(l.entries, logEntry{level: level, args: entryArgs})
|
||||
}
|
||||
|
||||
@@ -59,12 +59,12 @@ func TestSystemLoggingFunctions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level logrus.Level
|
||||
call func(ev []string, args ...interface{})
|
||||
call func(ev []string, args ...any)
|
||||
}{
|
||||
{name: "Info", level: logrus.InfoLevel, call: func(ev []string, args ...interface{}) { SystemInfo(ev, args...) }},
|
||||
{name: "Warn", level: logrus.WarnLevel, call: func(ev []string, args ...interface{}) { SystemWarn(ev, args...) }},
|
||||
{name: "Debug", level: logrus.DebugLevel, call: func(ev []string, args ...interface{}) { SystemDebug(ev, args...) }},
|
||||
{name: "Error", level: logrus.ErrorLevel, call: func(ev []string, args ...interface{}) { SystemError(ev, args...) }},
|
||||
{name: "Info", level: logrus.InfoLevel, call: func(ev []string, args ...any) { SystemInfo(ev, args...) }},
|
||||
{name: "Warn", level: logrus.WarnLevel, call: func(ev []string, args ...any) { SystemWarn(ev, args...) }},
|
||||
{name: "Debug", level: logrus.DebugLevel, call: func(ev []string, args ...any) { SystemDebug(ev, args...) }},
|
||||
{name: "Error", level: logrus.ErrorLevel, call: func(ev []string, args ...any) { SystemError(ev, args...) }},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -75,7 +75,7 @@ func TestSystemLoggingFunctions(t *testing.T) {
|
||||
t.Cleanup(func() { SystemLog = orig })
|
||||
|
||||
events := []string{"cleanup %s", "finished"}
|
||||
args := []interface{}{"cache"}
|
||||
args := []any{"cache"}
|
||||
expectedMessage := Format(events, args...)
|
||||
topic := "system.log." + tt.level.String()
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ type Album struct {
|
||||
}
|
||||
|
||||
// NewAlbum creates a new form struct based on the interface values.
|
||||
func NewAlbum(m interface{}) (*Album, error) {
|
||||
func NewAlbum(m any) (*Album, error) {
|
||||
frm := &Album{}
|
||||
err := deepcopier.Copy(m).To(frm)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ type Face struct {
|
||||
}
|
||||
|
||||
// NewFace copies values from an arbitrary model into a Face form.
|
||||
func NewFace(m interface{}) (f Face, err error) {
|
||||
func NewFace(m any) (f Face, err error) {
|
||||
err = deepcopier.Copy(m).To(&f)
|
||||
|
||||
return f, err
|
||||
|
||||
@@ -18,7 +18,7 @@ func (f Feedback) Empty() bool {
|
||||
}
|
||||
|
||||
// NewFeedback copies values from an arbitrary model into a Feedback form.
|
||||
func NewFeedback(m interface{}) (f Feedback, err error) {
|
||||
func NewFeedback(m any) (f Feedback, err error) {
|
||||
err = deepcopier.Copy(m).To(&f)
|
||||
|
||||
return f, err
|
||||
|
||||
@@ -17,7 +17,7 @@ func (f *File) Orientation() int {
|
||||
}
|
||||
|
||||
// NewFile copies values from an arbitrary model into a File form.
|
||||
func NewFile(m interface{}) (f File, err error) {
|
||||
func NewFile(m any) (f File, err error) {
|
||||
err = deepcopier.Copy(m).To(&f)
|
||||
|
||||
return f, err
|
||||
|
||||
@@ -21,7 +21,7 @@ type Folder struct {
|
||||
}
|
||||
|
||||
// NewFolder copies values from an arbitrary model into a Folder form.
|
||||
func NewFolder(m interface{}) (f Folder, err error) {
|
||||
func NewFolder(m any) (f Folder, err error) {
|
||||
err = deepcopier.Copy(m).To(&f)
|
||||
|
||||
return f, err
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
)
|
||||
|
||||
// Report returns form fields as table rows for reports.
|
||||
func Report(f interface{}) (rows [][]string, cols []string) {
|
||||
func Report(f any) (rows [][]string, cols []string) {
|
||||
cols = []string{"Filter", "Type", "Examples", "Notes"}
|
||||
|
||||
v := reflect.ValueOf(f)
|
||||
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.Kind() == reflect.Pointer {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user