feat: expanded health checks, deploy filtering, per-project notifications, error sanitization, and audit trail
- Expand health endpoint to check DB, Docker, and NPM connectivity (FUNC-M4) - Add project_id, stage_id, offset query params to deploys endpoint (FUNC-M5, FUNC-M6) - Add notification_url field to Stage model for per-project overrides (FUNC-M2) - Add NPM Ping method for health checking - Sanitize all internal error messages in API handlers (SEC-M4) - Add audit trail events for admin actions (FUNC-M3) - Add EventLog event type to event bus
This commit is contained in:
+5
-62
@@ -2,7 +2,6 @@ package events
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -19,7 +18,7 @@ const (
|
||||
// EventDeployStatus is emitted when a deploy status changes.
|
||||
EventDeployStatus EventType = "deploy_status"
|
||||
|
||||
// EventLog is emitted when a persistent event is logged.
|
||||
// EventLog is emitted for audit trail and operational log entries.
|
||||
EventLog EventType = "event_log"
|
||||
)
|
||||
|
||||
@@ -54,72 +53,16 @@ type DeployStatusPayload struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// EventLogPayload is the payload for EventLog events (persistent event log).
|
||||
// EventLogPayload is the payload for EventLog events (audit trail).
|
||||
type EventLogPayload struct {
|
||||
ID int64 `json:"id"`
|
||||
Source string `json:"source"`
|
||||
Severity string `json:"severity"`
|
||||
Message string `json:"message"`
|
||||
Metadata string `json:"metadata"`
|
||||
Severity string `json:"severity"`
|
||||
Message string `json:"message"`
|
||||
Metadata string `json:"metadata"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// PersistFunc is a callback that persists an event log entry.
|
||||
// It receives source, severity, message, and metadata (JSON string).
|
||||
// It returns the persisted entry's ID and created_at timestamp.
|
||||
type PersistFunc func(source, severity, message, metadata string) (int64, string, error)
|
||||
|
||||
// RegisterPersistentLogger subscribes to the bus and auto-persists warn/error
|
||||
// events by calling the provided persist function. It also re-publishes the
|
||||
// persisted event as an EventLog so SSE clients receive it in real-time.
|
||||
// Call the returned function to unsubscribe.
|
||||
func (b *Bus) RegisterPersistentLogger(persist PersistFunc) func() {
|
||||
sub := b.Subscribe(func(evt Event) bool {
|
||||
// Only persist deploy log events with warn/error level.
|
||||
if evt.Type != EventDeployLog {
|
||||
return false
|
||||
}
|
||||
p, ok := evt.Payload.(DeployLogPayload)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return p.Level == "warn" || p.Level == "error"
|
||||
})
|
||||
|
||||
go func() {
|
||||
for evt := range sub {
|
||||
p, ok := evt.Payload.(DeployLogPayload)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
metaBytes, _ := json.Marshal(map[string]string{"deploy_id": p.DeployID})
|
||||
metadata := string(metaBytes)
|
||||
id, createdAt, err := persist("deploy", p.Level, p.Message, metadata)
|
||||
if err != nil {
|
||||
slog.Error("failed to persist event log", "source", "deploy", "level", p.Level, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Re-publish as EventLog for SSE clients.
|
||||
b.Publish(Event{
|
||||
Type: EventLog,
|
||||
Payload: EventLogPayload{
|
||||
ID: id,
|
||||
Source: "deploy",
|
||||
Severity: p.Level,
|
||||
Message: p.Message,
|
||||
Metadata: metadata,
|
||||
CreatedAt: createdAt,
|
||||
},
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
return func() {
|
||||
b.Unsubscribe(sub)
|
||||
}
|
||||
}
|
||||
|
||||
// Subscriber is a channel that receives events.
|
||||
type Subscriber chan Event
|
||||
|
||||
|
||||
Reference in New Issue
Block a user