mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-23 09:31:02 +01:00
333 lines
6.7 KiB
Markdown
333 lines
6.7 KiB
Markdown
# internal/notif
|
|
|
|
The notif package provides a notification dispatching system for GoDoxy, supporting multiple providers (Webhook, Gotify, Ntfy) with retry logic and exponential backoff.
|
|
|
|
## Overview
|
|
|
|
The notif package implements a notification dispatcher that delivers messages to multiple providers with automatic retry handling, provider management, and logging support.
|
|
|
|
### Key Features
|
|
|
|
- Multiple notification providers (Webhook, Gotify, Ntfy)
|
|
- Provider registration and management
|
|
- Retry logic with exponential backoff
|
|
- Message queuing with configurable buffer
|
|
- Selective provider targeting
|
|
- Colored message support
|
|
|
|
## Architecture
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[LogMessage] --> B[Dispatcher]
|
|
B --> C{Provider Filter}
|
|
C -->|Matches| D[Send to Provider]
|
|
C -->|No Match| E[Skip]
|
|
|
|
D --> F{Send Success?}
|
|
F -->|Yes| G[Log Success]
|
|
F -->|No| H[Retry Queue]
|
|
|
|
H --> I{Retry Limit?}
|
|
I -->|No| J[Schedule Retry]
|
|
J --> B
|
|
I -->|Yes| K[Log Failure]
|
|
|
|
subgraph Providers
|
|
L[Webhook]
|
|
M[Gotify]
|
|
N[Ntfy]
|
|
end
|
|
|
|
D --> L
|
|
D --> M
|
|
D --> N
|
|
```
|
|
|
|
## Core Components
|
|
|
|
### Dispatcher
|
|
|
|
```go
|
|
type Dispatcher struct {
|
|
task *task.Task
|
|
providers *xsync.Map[Provider, struct{}]
|
|
logCh chan *LogMessage
|
|
retryMsg *xsync.Map[*RetryMessage, struct{}]
|
|
retryTicker *time.Ticker
|
|
}
|
|
```
|
|
|
|
### LogMessage
|
|
|
|
```go
|
|
type LogMessage struct {
|
|
Level zerolog.Level
|
|
Title string
|
|
Body LogBody
|
|
Color Color
|
|
To []string // Provider names to target
|
|
}
|
|
|
|
type LogBody []string
|
|
```
|
|
|
|
### Provider Interface
|
|
|
|
```go
|
|
type Provider interface {
|
|
GetName() string
|
|
Send(ctx context.Context, msg *LogMessage) error
|
|
}
|
|
```
|
|
|
|
## Providers
|
|
|
|
### Webhook
|
|
|
|
```go
|
|
type Webhook struct {
|
|
URL string `json:"url"`
|
|
Method string `json:"method"` // default: POST
|
|
}
|
|
```
|
|
|
|
### Gotify
|
|
|
|
```go
|
|
type GotifyClient struct {
|
|
URL string `json:"url"`
|
|
Token string `json:"token"`
|
|
}
|
|
```
|
|
|
|
### Ntfy
|
|
|
|
```go
|
|
type Ntfy struct {
|
|
URL string `json:"url"`
|
|
Topic string `json:"topic"`
|
|
}
|
|
```
|
|
|
|
## Public API
|
|
|
|
### Dispatcher Management
|
|
|
|
```go
|
|
// StartNotifDispatcher initializes the notification dispatcher.
|
|
func StartNotifDispatcher(parent task.Parent) *Dispatcher
|
|
```
|
|
|
|
### Notification
|
|
|
|
```go
|
|
// Notify sends a log message to all providers.
|
|
func Notify(msg *LogMessage)
|
|
```
|
|
|
|
### Dispatcher Methods
|
|
|
|
```go
|
|
// RegisterProvider registers a notification provider.
|
|
func (disp *Dispatcher) RegisterProvider(cfg *NotificationConfig)
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Basic Notification
|
|
|
|
```go
|
|
notif.Notify(¬if.LogMessage{
|
|
Level: zerolog.InfoLevel,
|
|
Title: "Container Started",
|
|
Body: notif.ListBody{
|
|
"Container: myapp",
|
|
"Status: Running",
|
|
},
|
|
})
|
|
```
|
|
|
|
### Provider-Specific Notification
|
|
|
|
```go
|
|
notif.Notify(¬if.LogMessage{
|
|
Level: zerolog.WarnLevel,
|
|
Title: "High Memory Usage",
|
|
Body: notif.MessageBody("Memory: 85%"),
|
|
Color: notif.ColorRed,
|
|
To: []string{"gotify"}, // Only send to provider with name "gotify"
|
|
})
|
|
```
|
|
|
|
### Registering Providers
|
|
|
|
```go
|
|
dispatcher := notif.StartNotifDispatcher(parent)
|
|
|
|
dispatcher.RegisterProvider(¬if.NotificationConfig{
|
|
ProviderName: notif.ProviderWebhook,
|
|
Provider: ¬if.Webhook{
|
|
URL: "https://hooks.example.com/webhook",
|
|
},
|
|
})
|
|
|
|
dispatcher.RegisterProvider(¬if.NotificationConfig{
|
|
ProviderName: notif.ProviderGotify,
|
|
Provider: ¬if.GotifyClient{
|
|
URL: "https://gotify.example.com",
|
|
Token: "secret",
|
|
},
|
|
})
|
|
```
|
|
|
|
## Retry Logic
|
|
|
|
### Configuration
|
|
|
|
```go
|
|
const (
|
|
retryInterval = time.Second
|
|
maxBackoffDelay = 5 * time.Minute
|
|
backoffMultiplier = 2.0
|
|
)
|
|
```
|
|
|
|
### Backoff Calculation
|
|
|
|
```go
|
|
func calculateBackoffDelay(trials int) time.Duration {
|
|
if trials == 0 {
|
|
return retryInterval
|
|
}
|
|
|
|
delay := min(float64(retryInterval)*math.Pow(backoffMultiplier, float64(trials)),
|
|
float64(maxBackoffDelay))
|
|
|
|
// Add 20% jitter
|
|
jitter := delay * 0.2 * (rand.Float64() - 0.5)
|
|
return time.Duration(delay + jitter)
|
|
}
|
|
```
|
|
|
|
### Retry Schedule
|
|
|
|
| Trial | Delay | Jitter | Total (approx) |
|
|
| ----- | ------ | --------- | -------------- |
|
|
| 0 | 1s | +/- 100ms | 0.9-1.1s |
|
|
| 1 | 2s | +/- 200ms | 1.8-2.2s |
|
|
| 2 | 4s | +/- 400ms | 3.6-4.4s |
|
|
| 3 | 8s | +/- 800ms | 7.2-8.8s |
|
|
| 4 | 16s | +/- 1.6s | 14.4-17.6s |
|
|
| 5 | 32s | +/- 3.2s | 28.8-35.2s |
|
|
| ... | max 5m | +/- 30s | 4.5-5.5m |
|
|
|
|
## Data Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant App
|
|
participant Dispatcher
|
|
participant Queue
|
|
participant Provider
|
|
participant RetryScheduler
|
|
|
|
App->>Dispatcher: Notify(LogMessage)
|
|
Dispatcher->>Dispatcher: Buffer Message
|
|
Dispatcher->>Dispatcher: Dispatch Async
|
|
|
|
par Parallel Provider Delivery
|
|
Dispatcher->>Provider: Send()
|
|
alt Success
|
|
Provider-->>Dispatcher: nil
|
|
Dispatcher->>Dispatcher: Log Success
|
|
else Failure
|
|
Provider-->>Dispatcher: Error
|
|
Dispatcher->>RetryScheduler: Schedule Retry
|
|
end
|
|
end
|
|
|
|
loop Retry Ticker
|
|
RetryScheduler->>Dispatcher: Process Retries
|
|
Dispatcher->>Provider: Retry Send
|
|
end
|
|
```
|
|
|
|
## Message Colors
|
|
|
|
```go
|
|
type Color uint
|
|
|
|
const (
|
|
ColorDefault Color = iota
|
|
ColorRed
|
|
ColorGreen
|
|
ColorBlue
|
|
ColorYellow
|
|
ColorPurple
|
|
)
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### YAML Configuration
|
|
|
|
```yaml
|
|
providers:
|
|
notification:
|
|
- provider: webhook
|
|
url: https://hooks.example.com/webhook
|
|
method: POST
|
|
|
|
- provider: gotify
|
|
url: https://gotify.example.com
|
|
token: your-token
|
|
|
|
- provider: ntfy
|
|
url: https://ntfy.example.com
|
|
topic: godoxy
|
|
```
|
|
|
|
## Integration Points
|
|
|
|
The notif package integrates with:
|
|
|
|
- **ACL**: Blocked access notifications
|
|
- **Route**: Route status changes
|
|
- **Idlewatcher**: Container idle/alive notifications
|
|
- **Health**: Health check alerts
|
|
- **Autocert**: Certificate expiration warnings
|
|
|
|
## Error Handling
|
|
|
|
### Provider Errors
|
|
|
|
```go
|
|
var (
|
|
ErrMissingNotifProvider = errors.New("missing notification provider")
|
|
ErrInvalidNotifProviderType = errors.New("invalid notification provider type")
|
|
ErrUnknownNotifProvider = errors.New("unknown notification provider")
|
|
)
|
|
```
|
|
|
|
### Retry Limits
|
|
|
|
Retry limits depend on message level:
|
|
|
|
```go
|
|
var maxRetries = map[zerolog.Level]int{
|
|
zerolog.ErrorLevel: 3,
|
|
zerolog.WarnLevel: 2,
|
|
zerolog.InfoLevel: 1,
|
|
zerolog.DebugLevel: 0,
|
|
}
|
|
```
|
|
|
|
## Performance Considerations
|
|
|
|
- Buffered channel with 100 message capacity
|
|
- Non-blocking sends to provider
|
|
- Batched retry processing
|
|
- Provider filtering reduces unnecessary calls
|
|
- Exponential backoff prevents thundering herd
|