Add YAML config loading with validation, env overrides for secrets, and a web dashboard exposing health, stats, and recent events.
77 lines
1.9 KiB
V
77 lines
1.9 KiB
V
module main
|
|
|
|
import net.http
|
|
import time
|
|
import sources
|
|
|
|
pub struct NtfyClient {
|
|
pub mut:
|
|
base_url string
|
|
auth_token string
|
|
logger Logger
|
|
}
|
|
|
|
pub fn new_ntfy_client(cfg ServerConfig, logger Logger) NtfyClient {
|
|
return NtfyClient{
|
|
base_url: cfg.url.trim_right('/')
|
|
auth_token: cfg.auth_token
|
|
logger: logger
|
|
}
|
|
}
|
|
|
|
fn join_tags(tags []string) string {
|
|
return tags.join(',')
|
|
}
|
|
|
|
// publish sends an event to the Ntfy server with exponential backoff retry (3 attempts).
|
|
pub fn (c NtfyClient) publish(event sources.Event) ! {
|
|
url := '${c.base_url}/${event.topic}'
|
|
mut header := http.Header{}
|
|
header.add_custom('Content-Type', 'text/plain')!
|
|
header.add_custom('X-Priority', event.priority.str())!
|
|
if event.tags.len > 0 {
|
|
header.add_custom('X-Tags', join_tags(event.tags))!
|
|
}
|
|
if c.auth_token != '' {
|
|
header.add(.authorization, 'Bearer ${c.auth_token}')
|
|
}
|
|
|
|
c.logger.debug('ntfy', 'publish',
|
|
'POST ${url} priority=${event.priority} tags=${join_tags(event.tags)}')
|
|
|
|
// Retry with exponential backoff: 3 attempts, 1s / 2s / 4s delays
|
|
mut last_err := error('unknown')
|
|
for attempt := 1; attempt <= 3; attempt++ {
|
|
resp := http.fetch(http.FetchConfig{
|
|
url: url
|
|
method: .post
|
|
header: header
|
|
data: event.message
|
|
}) or {
|
|
last_err = err
|
|
if attempt < 3 {
|
|
delay := time.second * (1 << (attempt - 1))
|
|
c.logger.warn('ntfy', 'publish',
|
|
'attempt ${attempt}/3 failed: ${err}, retrying in ${delay}')
|
|
time.sleep(delay)
|
|
}
|
|
continue
|
|
}
|
|
|
|
if resp.status_code >= 200 && resp.status_code < 300 {
|
|
c.logger.debug('ntfy', 'publish', 'success ${resp.status_code}')
|
|
return
|
|
}
|
|
|
|
last_err = error('ntfy returned ${resp.status_code}: ${resp.body}')
|
|
if attempt < 3 {
|
|
delay := time.second * (1 << (attempt - 1))
|
|
c.logger.warn('ntfy', 'publish',
|
|
'attempt ${attempt}/3 failed: ${last_err}, retrying in ${delay}')
|
|
time.sleep(delay)
|
|
}
|
|
}
|
|
|
|
return last_err
|
|
}
|