c091ad7aed
Stand up an internal ops-api service with operator account, auth, and audit log subsystems backed by a dedicated migrations_ops migration set. Wire Makefile targets and the migrate Dockerfile stage so the new schema ships alongside the existing tenant + monitoring databases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
67 lines
1.3 KiB
Go
67 lines
1.3 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/ops/domain"
|
|
)
|
|
|
|
type Actor struct {
|
|
OperatorID int64
|
|
Username string
|
|
DisplayName string
|
|
Role string
|
|
IP string
|
|
UserAgent string
|
|
RequestID string
|
|
}
|
|
|
|
func (a *Actor) audit(action, targetType string, targetID int64, metadata map[string]any) domain.AuditEvent {
|
|
id := a.OperatorID
|
|
var tt, ti string
|
|
tt = targetType
|
|
if targetID != 0 {
|
|
ti = formatInt64(targetID)
|
|
}
|
|
return domain.AuditEvent{
|
|
OperatorID: &id,
|
|
OperatorName: a.DisplayName,
|
|
Action: action,
|
|
TargetType: tt,
|
|
TargetID: ti,
|
|
Metadata: metadata,
|
|
IP: a.IP,
|
|
UserAgent: a.UserAgent,
|
|
RequestID: a.RequestID,
|
|
}
|
|
}
|
|
|
|
type actorContextKey struct{}
|
|
|
|
func WithActor(ctx context.Context, actor *Actor) context.Context {
|
|
return context.WithValue(ctx, actorContextKey{}, actor)
|
|
}
|
|
|
|
func ActorFromContext(ctx context.Context) *Actor {
|
|
if actor, ok := ctx.Value(actorContextKey{}).(*Actor); ok {
|
|
return actor
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func formatInt64(n int64) string {
|
|
if n == 0 {
|
|
return ""
|
|
}
|
|
const digits = "0123456789"
|
|
if n < 0 {
|
|
return "-" + formatInt64(-n)
|
|
}
|
|
buf := make([]byte, 0, 20)
|
|
for n > 0 {
|
|
buf = append([]byte{digits[n%10]}, buf...)
|
|
n /= 10
|
|
}
|
|
return string(buf)
|
|
}
|