diff --git a/internal/api/README.md b/internal/api/README.md index 956b6f7da..8fae68c4f 100644 --- a/internal/api/README.md +++ b/internal/api/README.md @@ -29,6 +29,34 @@ The API package exposes PhotoPrism’s HTTP endpoints via Gin handlers. Each fil - Derive client IPs through `api.ClientIP` and extract bearer tokens with `header.BearerToken` or the helper setters. Use constant-time comparison for tokens and secrets. - For downloads or proxy endpoints, validate URLs against allowed schemes (`http`, `https`) and reject private or loopback addresses unless explicitly required. +## Audit Logging + +- Emit security events via `event.Audit*` (`AuditInfo`, `AuditWarn`, `AuditErr`, `AuditDebug`) and always build the slice as **Who → What → Outcome**. + - **Who:** `ClientIP(c)` followed by the most specific actor context (`"session %s"`, `"client %s"`, `"user %s"`). + - **What:** Resource constant plus action segments (for example, `string(acl.ResourceCluster)`, `"node %s"`). Place extra context such as counts or error placeholders in separate segments before the outcome. + - **Outcome:** End with a single token like `event.Succeeded`, `event.Failed`, or `authn.Denied`; nothing comes after it. +- Prefer existing helpers (`ClientIP`, `clean.Log`, `clean.LogQuote`, `clean.Error`) instead of formatting values manually, and avoid inline `=` expressions. +- Example patterns: + ```go + event.AuditInfo([]string{ + ClientIP(c), + "session %s", + string(acl.ResourceCluster), + "node %s", + event.Deleted, + }, s.RefID, uuid) + + event.AuditErr([]string{ + clientIp, + "session %s", + string(acl.ResourceCluster), + "download theme", + "%s", + event.Failed, + }, refID, clean.Error(err)) + ``` +- See `specs/common/audit-logs.md` for the full conventions and additional examples that agents should follow. + ## Swagger Documentation - Annotate handlers with Swagger comments that include full `/api/v1/...` paths, request/response schemas, and security definitions. Only annotate routes that are externally accessible. diff --git a/internal/api/cluster_metrics.go b/internal/api/cluster_metrics.go index 240e4256a..ec9e6b906 100644 --- a/internal/api/cluster_metrics.go +++ b/internal/api/cluster_metrics.go @@ -50,11 +50,13 @@ func ClusterMetrics(router *gin.RouterGroup) { counts[role]++ } - c.JSON(http.StatusOK, cluster.MetricsResponse{ + resp := cluster.MetricsResponse{ UUID: conf.ClusterUUID(), ClusterCIDR: conf.ClusterCIDR(), Nodes: counts, Time: time.Now().UTC().Format(time.RFC3339), - }) + } + + c.JSON(http.StatusOK, resp) }) } diff --git a/internal/api/cluster_nodes.go b/internal/api/cluster_nodes.go index ff014d8d2..ab562de4a 100644 --- a/internal/api/cluster_nodes.go +++ b/internal/api/cluster_nodes.go @@ -108,7 +108,14 @@ func ClusterListNodes(router *gin.RouterGroup) { 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)) + event.AuditDebug([]string{ + ClientIP(c), + "session %s", + string(acl.ResourceCluster), + "list nodes", + "count %d offset %d returned %d", + event.Succeeded, + }, s.RefID, count, offset, len(resp)) c.JSON(http.StatusOK, resp) }) @@ -166,7 +173,13 @@ func ClusterGetNode(router *gin.RouterGroup) { resp := reg.BuildClusterNode(*n, opts) // Audit get access. - event.AuditInfo([]string{ClientIP(c), "session %s", string(acl.ResourceCluster), "nodes", "get", uuid, event.Succeeded}, s.RefID) + event.AuditInfo([]string{ + ClientIP(c), + "session %s", + string(acl.ResourceCluster), + "get node %s", + event.Succeeded, + }, s.RefID, uuid) c.JSON(http.StatusOK, resp) }) @@ -238,8 +251,9 @@ func ClusterUpdateNode(router *gin.RouterGroup) { if req.AdvertiseUrl != "" { n.AdvertiseUrl = req.AdvertiseUrl } - if s := normalizeSiteURL(req.SiteUrl); s != "" { - n.SiteUrl = s + + if u := normalizeSiteURL(req.SiteUrl); u != "" { + n.SiteUrl = u } n.UpdatedAt = time.Now().UTC().Format(time.RFC3339) @@ -249,7 +263,14 @@ func ClusterUpdateNode(router *gin.RouterGroup) { return } - event.AuditInfo([]string{ClientIP(c), string(acl.ResourceCluster), "nodes", "update", uuid, event.Succeeded}) + event.AuditInfo([]string{ + ClientIP(c), + "session %s", + string(acl.ResourceCluster), + "node %s", + event.Updated, + }, s.RefID, uuid) + c.JSON(http.StatusOK, cluster.StatusResponse{Status: "ok"}) }) } @@ -303,7 +324,14 @@ func ClusterDeleteNode(router *gin.RouterGroup) { return } - event.AuditInfo([]string{ClientIP(c), string(acl.ResourceCluster), "nodes", "delete", uuid, event.Succeeded}) + event.AuditWarn([]string{ + ClientIP(c), + "session %s", + string(acl.ResourceCluster), + "node %s", + event.Deleted, + }, s.RefID, uuid) + c.JSON(http.StatusOK, cluster.StatusResponse{Status: "ok"}) }) } diff --git a/internal/api/cluster_nodes_register.go b/internal/api/cluster_nodes_register.go index a06c31b0a..45fe9b196 100644 --- a/internal/api/cluster_nodes_register.go +++ b/internal/api/cluster_nodes_register.go @@ -53,7 +53,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { r := limiter.Auth.Request(clientIp) if r.Reject() || limiter.Auth.Reject(clientIp) { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "rate limit", event.Denied}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "rate limit exceeded", event.Denied}) limiter.AbortJSON(c) return } @@ -63,7 +63,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { token := header.BearerToken(c) if expected == "" || token == "" || subtle.ConstantTimeCompare([]byte(expected), []byte(token)) != 1 { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "auth", event.Denied}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid join token", event.Denied}) r.Success() // return reserved tokens; still unauthorized AbortUnauthorized(c) return @@ -73,7 +73,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { var req cluster.RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "form invalid", "%s"}, clean.Error(err)) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid form", "%s", event.Failed}, clean.Error(err)) AbortBadRequest(c) return } @@ -85,13 +85,13 @@ func ClusterNodesRegister(router *gin.RouterGroup) { // If an existing ClientID is provided, require the corresponding client secret for verification. if RegisterRequireClientSecret && req.ClientID != "" { if !rnd.IsUID(req.ClientID, entity.ClientUID) { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid client id"}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid client id", event.Failed}) AbortBadRequest(c) return } pw := entity.FindPassword(req.ClientID) if pw == nil || req.ClientSecret == "" || !pw.Valid(req.ClientSecret) { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid client secret"}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid client secret", event.Denied}) AbortUnauthorized(c) return } @@ -101,14 +101,14 @@ func ClusterNodesRegister(router *gin.RouterGroup) { // Enforce DNS label semantics for node names: lowercase [a-z0-9-], 1–32, start/end alnum. if name == "" || len(name) > 32 || name[0] == '-' || name[len(name)-1] == '-' { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid name"}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid name", event.Failed}) AbortBadRequest(c) return } for i := 0; i < len(name); i++ { b := name[i] if !(b == '-' || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9')) { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid name chars"}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid name chars", event.Failed}) AbortBadRequest(c) return } @@ -117,7 +117,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { // Validate advertise URL if provided (https required for non-local domains). if u := strings.TrimSpace(req.AdvertiseUrl); u != "" { if !validateAdvertiseURL(u) { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid advertise url"}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid advertise url", event.Failed}) AbortBadRequest(c) return } @@ -126,7 +126,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { // Validate site URL if provided (https required for non-local domains). if su := strings.TrimSpace(req.SiteUrl); su != "" { if !validateSiteURL(su) { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid site url"}) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid site url", event.Failed}) AbortBadRequest(c) return } @@ -139,7 +139,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { regy, err := reg.NewClientRegistryWithConfig(conf) if err != nil { - event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register node", "registry", event.Failed, "%s"}, clean.Error(err)) + event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register", "%s", event.Failed}, clean.Error(err)) AbortUnexpectedError(c) return } @@ -154,7 +154,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { // If caller attempts to change UUID by name without proving client secret, block with 409. if RegisterRequireClientSecret { if requestedUUID != "" && n.UUID != "" && requestedUUID != n.UUID && req.ClientID == "" { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s", "uuid change requires client secret", event.Denied}, clean.LogQuote(name)) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s uuid change requires client secret", event.Denied}, clean.Log(name)) c.JSON(http.StatusConflict, gin.H{"error": "client secret required to change node uuid"}) return } @@ -184,16 +184,16 @@ func ClusterNodesRegister(router *gin.RouterGroup) { if oldUUID != requestedUUID { n.UUID = requestedUUID // Emit audit event for UUID change. - event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "change uuid", "old %s", "new %s", event.Succeeded}, clean.LogQuote(name), clean.Log(oldUUID), clean.Log(requestedUUID)) + event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "change uuid old %s new %s", event.Updated}, clean.Log(name), clean.Log(oldUUID), clean.Log(requestedUUID)) } } else if n.UUID == "" { // Assign a fresh UUID if missing and none requested. n.UUID = rnd.UUIDv7() - event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "new uuid", "%s", event.Succeeded}, clean.LogQuote(name), clean.Log(n.UUID)) + event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "assign uuid %s", event.Created}, clean.Log(name), clean.Log(n.UUID)) } // Persist metadata changes so UpdatedAt advances. if putErr := regy.Put(n); putErr != nil { - event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.LogQuote(name), clean.Error(putErr)) + event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.Log(name), clean.Error(putErr)) AbortUnexpectedError(c) return } @@ -201,16 +201,16 @@ func ClusterNodesRegister(router *gin.RouterGroup) { var respSecret *cluster.RegisterSecrets if req.RotateSecret { if n, err = regy.RotateSecret(n.UUID); err != nil { - event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate secret", "%s", event.Failed}, clean.LogQuote(name), clean.Error(err)) + event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate secret", "%s", event.Failed}, clean.Log(name), clean.Error(err)) AbortUnexpectedError(c) return } respSecret = &cluster.RegisterSecrets{ClientSecret: n.ClientSecret, RotatedAt: n.RotatedAt} - event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate secret", event.Succeeded}, clean.LogQuote(name)) + event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s rotate secret", event.Succeeded}, clean.Log(name)) // Extra safety: ensure the updated secret is persisted even if subsequent steps fail. if putErr := regy.Put(n); putErr != nil { - event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist rotated secret", "%s", event.Failed}, clean.LogQuote(name), clean.Error(putErr)) + event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist rotated secret", "%s", event.Failed}, clean.Log(name), clean.Error(putErr)) AbortUnexpectedError(c) return } @@ -225,7 +225,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { creds, _, credsErr = provisioner.EnsureCredentials(c, conf, n.UUID, name, req.RotateDatabase) if credsErr != nil { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s", "ensure database", "%s", event.Failed}, clean.LogQuote(name), clean.Error(credsErr)) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s", "ensure database", "%s", event.Failed}, clean.Log(name), clean.Error(credsErr)) c.JSON(http.StatusConflict, gin.H{"error": credsErr.Error()}) return } @@ -235,11 +235,11 @@ func ClusterNodesRegister(router *gin.RouterGroup) { n.Database.RotatedAt = creds.RotatedAt n.Database.Driver = provisioner.DatabaseDriver if putErr := regy.Put(n); putErr != nil { - event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.LogQuote(name), clean.Error(putErr)) + event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.Log(name), clean.Error(putErr)) AbortUnexpectedError(c) return } - event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate db", event.Succeeded}, clean.LogQuote(name)) + event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s rotate database", event.Succeeded}, clean.Log(name)) } } @@ -273,7 +273,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { resp.Database.RotatedAt = creds.RotatedAt } c.Header(header.CacheControl, header.CacheControlNoStore) - event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "registration", event.Updated}, clean.LogQuote(name)) + event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", event.Updated}, clean.Log(name)) c.JSON(http.StatusOK, resp) return } @@ -315,7 +315,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { if shouldProvisionDB { if creds, _, err = provisioner.EnsureCredentials(c, conf, n.UUID, name, true); err != nil { - event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "ensure database", "%s", event.Failed}, clean.Error(err)) + event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "ensure database", "%s", event.Failed}, clean.Error(err)) c.JSON(http.StatusConflict, gin.H{"error": err.Error()}) return } @@ -329,7 +329,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { } if err = regy.Put(n); err != nil { - event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register node", "persist node", "%s", event.Failed}, clean.Error(err)) + event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register", "persist node", "%s", event.Failed}, clean.Error(err)) AbortUnexpectedError(c) return } @@ -355,7 +355,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { // When DB provisioning is skipped, leave Database fields zero-value. c.Header(header.CacheControl, header.CacheControlNoStore) - event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "registration", event.Created}, clean.LogQuote(name)) + event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", event.Created}, clean.Log(name)) c.JSON(http.StatusCreated, resp) }) } diff --git a/internal/api/cluster_summary.go b/internal/api/cluster_summary.go index 2f636a7aa..266989877 100644 --- a/internal/api/cluster_summary.go +++ b/internal/api/cluster_summary.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "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" @@ -51,14 +52,24 @@ func ClusterSummary(router *gin.RouterGroup) { themeVersion = v } - c.JSON(http.StatusOK, cluster.SummaryResponse{ + resp := cluster.SummaryResponse{ UUID: conf.ClusterUUID(), ClusterCIDR: conf.ClusterCIDR(), Nodes: len(nodes), Database: cluster.DatabaseInfo{Driver: conf.DatabaseDriverName(), Host: conf.DatabaseHost(), Port: conf.DatabasePort()}, Theme: themeVersion, Time: time.Now().UTC().Format(time.RFC3339), - }) + } + + event.AuditDebug([]string{ + ClientIP(c), + "session %s", + string(acl.ResourceCluster), + "get summary for cluster uuid %s", + event.Succeeded, + }, s.RefID, conf.ClusterUUID()) + + c.JSON(http.StatusOK, resp) }) } @@ -85,6 +96,13 @@ func ClusterHealth(router *gin.RouterGroup) { return } + event.AuditDebug([]string{ + ClientIP(c), + string(acl.ResourceCluster), + "health check", + event.Succeeded, + }) + c.JSON(http.StatusOK, NewHealthResponse("ok")) }) } diff --git a/internal/api/cluster_theme.go b/internal/api/cluster_theme.go index d852cb319..173d41b77 100644 --- a/internal/api/cluster_theme.go +++ b/internal/api/cluster_theme.go @@ -75,7 +75,7 @@ func ClusterGetTheme(router *gin.RouterGroup) { // Resolve symbolic links. if resolved, err := filepath.EvalSymlinks(themePath); err != nil { - event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "%s"}, refID, clean.Error(err)) + event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme resolve", "%s", event.Failed}, refID, clean.Error(err)) AbortNotFound(c) return } else { @@ -84,7 +84,7 @@ func ClusterGetTheme(router *gin.RouterGroup) { // Check if theme path exists. if !fs.PathExists(themePath) { - event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "theme path not found"}, refID) + event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme path", "not found"}, refID) AbortNotFound(c) return } @@ -93,17 +93,17 @@ func ClusterGetTheme(router *gin.RouterGroup) { // This aligns with bootstrap behavior, which only installs a theme when // app.js exists locally or can be fetched from the Portal. if !fs.FileExistsNotEmpty(filepath.Join(themePath, "app.js")) { - event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "app.js missing or empty"}, refID) + event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme app.js", "not found"}, refID) AbortNotFound(c) return } - event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "creating theme archive from %s"}, refID, clean.Log(themePath)) + event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme create archive", "%s", "started"}, refID, clean.Log(themePath)) if version, err := theme.DetectVersion(themePath); err == nil { updateNodeThemeVersion(conf, session, version, clientIp, refID) } else { - event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "version", "%s"}, refID, clean.Error(err)) + event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "detect theme version", "%s", event.Failed}, refID, clean.Error(err)) } // Add response headers. @@ -114,14 +114,14 @@ func ClusterGetTheme(router *gin.RouterGroup) { zipWriter := zip.NewWriter(c.Writer) defer func(w *zip.Writer) { if closeErr := w.Close(); closeErr != nil { - event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "failed to close", "%s"}, refID, clean.Error(closeErr)) + event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme close", "%s", event.Failed}, refID, clean.Error(closeErr)) } }(zipWriter) err := filepath.WalkDir(themePath, func(filePath string, info gofs.DirEntry, walkErr error) error { // Handle errors. if walkErr != nil { - event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "failed to traverse theme path", "%s"}, refID, clean.Error(walkErr)) + event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme traverse", "%s", event.Failed}, refID, clean.Error(walkErr)) // If the error occurs on a directory, skip descending to avoid cascading errors. if info != nil && info.IsDir() { @@ -157,11 +157,11 @@ func ClusterGetTheme(router *gin.RouterGroup) { // Get the relative file name to use as alias in the zip. alias := filepath.ToSlash(fs.RelName(filePath, themePath)) - event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "adding %s to archive"}, refID, clean.Log(alias)) + event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme add", "%s", event.Added}, refID, clean.Log(alias)) // Stream zipped file contents. if zipErr := fs.ZipFile(zipWriter, filePath, alias, false); zipErr != nil { - event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "failed to add %s", "%s"}, refID, clean.Log(alias), clean.Error(zipErr)) + event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme add %s", "%s", event.Failed}, refID, clean.Log(alias), clean.Error(zipErr)) } return nil @@ -169,9 +169,9 @@ func ClusterGetTheme(router *gin.RouterGroup) { // Log result. if err != nil { - event.AuditErr([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", event.Failed, "%s"}, refID, clean.Error(err)) + event.AuditErr([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme", "%s", event.Failed}, refID, clean.Error(err)) } else { - event.AuditInfo([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", event.Succeeded}, refID) + event.AuditInfo([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme", event.Succeeded}, refID) } }) } @@ -198,7 +198,7 @@ func updateNodeThemeVersion(conf *config.Config, session *entity.Session, versio regy, err := reg.NewClientRegistryWithConfig(conf) if err != nil { - event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "registry", "%s"}, refID, clean.Error(err)) + event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata registry", "%s", event.Failed}, refID, clean.Error(err)) return } @@ -217,7 +217,7 @@ func updateNodeThemeVersion(conf *config.Config, session *entity.Session, versio } if node == nil { - event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "node not found"}, refID) + event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata node", "skipped"}, refID) return } @@ -228,9 +228,9 @@ func updateNodeThemeVersion(conf *config.Config, session *entity.Session, versio node.Theme = normalized if err = regy.Put(node); err != nil { - event.AuditWarn([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "%s"}, refID, clean.Error(err)) + event.AuditWarn([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata", "%s", event.Failed}, refID, clean.Error(err)) return } - event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "updated"}, refID) + event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata", event.Updated}, refID) }