Documentation
¶
Overview ¶
Package notification implements the notification service for Arkeep. It is the single component responsible for persisting in-app notifications, publishing them to the WebSocket Hub, and delivering them via external channels (email, webhook). No other package should write to the notifications table or call hub.Publish on notification topics directly.
Index ¶
- Constants
- Variables
- type Config
- type NotificationEventsConfig
- type NotificationService
- func (s *NotificationService) NotifyAgentOffline(ctx context.Context, agentID uuid.UUID, agentName string) error
- func (s *NotificationService) NotifyAgentOnline(ctx context.Context, agentID uuid.UUID, agentName string) error
- func (s *NotificationService) NotifyJobFailed(ctx context.Context, jobID, policyID uuid.UUID, policyName, errMsg string) error
- func (s *NotificationService) NotifyJobSucceeded(ctx context.Context, jobID, policyID uuid.UUID, policyName string) error
- func (s *NotificationService) SMTPConfigured(ctx context.Context) bool
- func (s *NotificationService) SendEmail(ctx context.Context, to []string, subject, body string) error
- func (s *NotificationService) Start(ctx context.Context)
- type SMTPConfig
- type Service
- type WebhookConfig
Constants ¶
const ( KeySMTPHost = "smtp.host" KeySMTPPort = "smtp.port" KeySMTPUsername = "smtp.username" KeySMTPPassword = "smtp.password" // stored encrypted via EncryptedString KeySMTPFrom = "smtp.from" KeySMTPFromName = "smtp.from_name" // optional display name for the From header KeySMTPTLS = "smtp.tls" // "true" or "false" KeyWebhookURL = "webhook.url" KeyWebhookSecret = "webhook.secret" // HMAC secret, stored encrypted KeyWebhookEnabled = "webhook.enabled" // "true" or "false" // KeyNotificationRecipients is a comma-separated list of email addresses // that receive email notifications. When empty the service falls back to // all active admin users' email addresses. KeyNotificationRecipients = "notification.recipients" // Per-event toggle keys — "true" or "false", default true except agent_online. KeyEventJobSuccess = "notification.events.job_success" KeyEventJobFailure = "notification.events.job_failure" KeyEventAgentOffline = "notification.events.agent_offline" KeyEventAgentOnline = "notification.events.agent_online" )
Setting keys used by the notification service. All keys are namespaced to avoid collisions with future config namespaces.
const DefaultFromName = "Arkeep"
DefaultFromName is used as the email sender display name when smtp.from_name is not configured, so notifications read as "Arkeep <address>" out of the box.
Variables ¶
var ( // ErrSendFailed is returned when a notification could not be delivered // through one or more channels (email, webhook). It wraps the underlying // cause and is non-fatal — the in-app notification is still persisted even // if external delivery fails. ErrSendFailed = errors.New("notification: send failed") // ErrConfigNotFound is returned when a required configuration key is // missing from the settings table (e.g. SMTP not configured yet). ErrConfigNotFound = errors.New("notification: configuration not found") // ErrInvalidConfig is returned when settings exist but contain invalid or // incomplete values (e.g. SMTP host present but port missing). ErrInvalidConfig = errors.New("notification: invalid configuration") )
Sentinel errors returned by the notification service and its senders. Callers should use errors.Is for comparison.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
NotifRepo repositories.NotificationRepository
UserRepo repositories.UserRepository
SettingsRepo repositories.SettingsRepository
Hub *websocket.Hub
Logger *zap.Logger
}
Config holds the dependencies required to build a notification Service.
type NotificationEventsConfig ¶
type NotificationEventsConfig struct {
JobSuccess bool
JobFailure bool
AgentOffline bool
AgentOnline bool
}
NotificationEventsConfig holds the per-event enable/disable toggles for external deliveries (email + webhook). In-app notifications are unaffected. Default: all true except AgentOnline.
type NotificationService ¶
type NotificationService struct {
// contains filtered or unexported fields
}
NotificationService is the concrete implementation of Service. It is exported so that main.go can call Start(ctx) to launch the retrier.
func NewService ¶
func NewService(cfg Config) *NotificationService
NewService creates a new NotificationService. The email and webhook senders are wired internally — callers only need to provide the Config dependencies.
The returned *NotificationService satisfies the Service interface. To start the background delivery retrier, call Start(ctx) after creation.
func (*NotificationService) NotifyAgentOffline ¶
func (*NotificationService) NotifyAgentOnline ¶
func (*NotificationService) NotifyJobFailed ¶
func (*NotificationService) NotifyJobSucceeded ¶
func (*NotificationService) SMTPConfigured ¶
func (s *NotificationService) SMTPConfigured(ctx context.Context) bool
SMTPConfigured reports whether a usable SMTP configuration exists. The password reset flow uses this to decide between offering the email-based reset and instructing the user to contact an administrator. A configuration that exists but is incomplete (ErrInvalidConfig) counts as not configured.
func (*NotificationService) SendEmail ¶
func (s *NotificationService) SendEmail(ctx context.Context, to []string, subject, body string) error
SendEmail delivers a one-off email through the configured SMTP server. Unlike the notification fan-out, this is a direct send for transactional flows such as password reset. If SMTP is not configured the send is skipped silently (see emailSender.Send) — callers should gate on SMTPConfigured first.
func (*NotificationService) Start ¶
func (s *NotificationService) Start(ctx context.Context)
Start launches the background delivery retrier. It runs until ctx is cancelled (i.e. server shutdown). Call it as a goroutine:
go notifSvc.Start(ctx)
The retrier polls every 30 seconds for pending deliveries whose next_retry_at is in the past and attempts to resend them.
type SMTPConfig ¶
type SMTPConfig struct {
Host string
Port int
Username string
Password string // decrypted at load time by EncryptedString.Scan
From string
FromName string // display name for the From header; defaults to DefaultFromName
TLS bool // true = STARTTLS / implicit TLS
}
SMTPConfig holds the configuration needed to send emails via SMTP.
type Service ¶
type Service interface {
// NotifyJobSucceeded creates a success notification for the given job.
// policyName is included in the message body for human readability.
NotifyJobSucceeded(ctx context.Context, jobID, policyID uuid.UUID, policyName string) error
// NotifyJobFailed creates a failure notification for the given job.
// errMsg is the error string from the backup engine, included in the body.
NotifyJobFailed(ctx context.Context, jobID, policyID uuid.UUID, policyName, errMsg string) error
// NotifyAgentOffline creates a notification when an agent stops sending
// heartbeats and is marked offline by the agent manager.
NotifyAgentOffline(ctx context.Context, agentID uuid.UUID, agentName string) error
// NotifyAgentOnline creates a notification when an agent reconnects.
NotifyAgentOnline(ctx context.Context, agentID uuid.UUID, agentName string) error
}
Service is the single entry point for creating and delivering notifications. It persists in-app notifications to the database, publishes them to the WebSocket Hub, and fans out to external channels (email, webhook).
Callers (scheduler, gRPC handlers, etc.) should use the typed methods (NotifyJobSucceeded, NotifyJobFailed, NotifyAgentOffline) rather than constructing events manually, so that notification content stays consistent across the codebase.
type WebhookConfig ¶
type WebhookConfig struct {
URL string
Secret string // optional HMAC-SHA256 signing secret, decrypted at load time
Enabled bool
}
WebhookConfig holds the configuration for the outbound HTTP webhook channel.