Fixes 'invalid input syntax for type uuid' error when writing ticket
workflow audit logs. The audit Event.ID field was using fmt.Sprintf
with nanoseconds ('wf-%d') which doesn't match PostgreSQL's uuid type.
Also adds uuid import to ticket_workflow.go.
Verified: full chain webhook→assign→resolve→close produces 3 audit
logs correctly, no more 'invalid uuid' errors in logs.
78 lines
1.8 KiB
Go
78 lines
1.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/bridge/ai-customer-service/internal/domain/error/cserrors"
|
|
)
|
|
|
|
const (
|
|
HeaderActorID = "X-CS-Actor-ID"
|
|
HeaderActorRole = "X-CS-Actor-Role"
|
|
)
|
|
|
|
type Actor struct {
|
|
ID string
|
|
Role string
|
|
}
|
|
|
|
type actorContextKey struct{}
|
|
|
|
func WithActor(ctx context.Context, id, role string) context.Context {
|
|
return context.WithValue(ctx, actorContextKey{}, Actor{
|
|
ID: strings.TrimSpace(id),
|
|
Role: normalizeRole(role),
|
|
})
|
|
}
|
|
|
|
func ActorFromContext(ctx context.Context) (Actor, bool) {
|
|
actor, ok := ctx.Value(actorContextKey{}).(Actor)
|
|
if !ok {
|
|
return Actor{}, false
|
|
}
|
|
if strings.TrimSpace(actor.ID) == "" || strings.TrimSpace(actor.Role) == "" {
|
|
return Actor{}, false
|
|
}
|
|
return actor, true
|
|
}
|
|
|
|
func RequireRoles(next http.Handler, allowedRoles ...string) http.Handler {
|
|
allowed := make(map[string]struct{}, len(allowedRoles))
|
|
for _, role := range allowedRoles {
|
|
if normalized := normalizeRole(role); normalized != "" {
|
|
allowed[normalized] = struct{}{}
|
|
}
|
|
}
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
actorID := strings.TrimSpace(r.Header.Get(HeaderActorID))
|
|
role := normalizeRole(r.Header.Get(HeaderActorRole))
|
|
if actorID == "" || role == "" {
|
|
writeAccessDenied(w)
|
|
return
|
|
}
|
|
if _, ok := allowed[role]; !ok {
|
|
writeAccessDenied(w)
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r.WithContext(WithActor(r.Context(), actorID, role)))
|
|
})
|
|
}
|
|
|
|
func normalizeRole(role string) string {
|
|
return strings.ToLower(strings.TrimSpace(role))
|
|
}
|
|
|
|
func writeAccessDenied(w http.ResponseWriter) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusForbidden)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"error": map[string]any{
|
|
"code": cserrors.CS_AUTH_4001,
|
|
"message": cserrors.ErrorMsg(cserrors.CS_AUTH_4001),
|
|
},
|
|
})
|
|
}
|