94 lines
2.5 KiB
Go
94 lines
2.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
|
|
"wucher/internal/shared/pkg/userctx"
|
|
"wucher/internal/shared/pkg/uuidv7"
|
|
responsedto "wucher/internal/transport/http/dto/response"
|
|
)
|
|
|
|
func userRef(id []byte, name string) *responsedto.UserRef {
|
|
trimmed := strings.TrimSpace(name)
|
|
if len(id) == 0 && trimmed == "" {
|
|
return nil
|
|
}
|
|
if trimmed == "" && len(id) > 0 {
|
|
trimmed = userctx.GetDisplayName(context.TODO(), id)
|
|
}
|
|
return &responsedto.UserRef{
|
|
UUID: idString(id),
|
|
Name: trimmed,
|
|
}
|
|
}
|
|
|
|
// updatedAuditOrCreated returns the "last modified" timestamp (RFC3339) and actor for an
|
|
// audited row, falling back to the creation timestamp/actor when the row was never
|
|
// updated after creation (updated_by unset — e.g. seed/legacy rows). This guarantees an
|
|
// updated_at/updated_by is always presentable instead of silently omitted.
|
|
func updatedAuditOrCreated(updatedAt, createdAt time.Time, updatedBy []byte, updatedByName string, createdBy []byte, createdByName string) (string, *responsedto.UserRef) {
|
|
if len(updatedBy) == 0 {
|
|
updatedBy, updatedByName = createdBy, createdByName
|
|
}
|
|
if updatedAt.IsZero() {
|
|
updatedAt = createdAt
|
|
}
|
|
at := ""
|
|
if !updatedAt.IsZero() {
|
|
at = updatedAt.UTC().Format(time.RFC3339)
|
|
}
|
|
return at, userRef(updatedBy, updatedByName)
|
|
}
|
|
|
|
func actorUserID(c *fiber.Ctx) []byte {
|
|
raw := c.Locals("user_id")
|
|
userID, ok := raw.([]byte)
|
|
if !ok || len(userID) == 0 {
|
|
return nil
|
|
}
|
|
// Return a copy to avoid accidental mutation of local context values.
|
|
return append([]byte(nil), userID...)
|
|
}
|
|
|
|
// uuidStringOrEmpty resolves a user ID to their last name using the global userctx getter
|
|
// if set; otherwise falls back to the UUID string representation.
|
|
func uuidStringOrEmpty(id []byte) string {
|
|
if len(id) == 0 {
|
|
return ""
|
|
}
|
|
if name := userctx.GetDisplayName(context.TODO(), id); name != "" {
|
|
return name
|
|
}
|
|
// Fallback to UUID string when no getter is available or user not found.
|
|
val, err := uuidv7.BytesToString(id)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return val
|
|
}
|
|
|
|
// idString converts a resource ID byte slice to a UUID string.
|
|
// Unlike uuidStringOrEmpty, this does not attempt user name resolution.
|
|
// Use this for primary keys and resource IDs, not for audit/user reference fields.
|
|
func idString(id []byte) string {
|
|
if len(id) == 0 {
|
|
return ""
|
|
}
|
|
val, err := uuidv7.BytesToString(id)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return val
|
|
}
|
|
|
|
func timeStringOrEmpty(t *time.Time) string {
|
|
if t == nil || t.IsZero() {
|
|
return ""
|
|
}
|
|
return t.UTC().Format(time.RFC3339)
|
|
}
|