feat: add Slack target type for incoming webhook notifications (#47)
All checks were successful
check / check (push) Successful in 4s

## Summary

Adds a new `slack` target type that sends webhook events as formatted messages to any Slack-compatible incoming webhook URL (Slack, Mattermost, and other compatible services).

closes #44

## What it does

When a webhook event is received, the Slack target:

1. Formats a human-readable message with event metadata (HTTP method, content type, timestamp, body size)
2. Pretty-prints the payload in a code block — JSON payloads get indented formatting, non-JSON payloads are shown as raw text
3. Truncates large payloads at 3500 characters to keep Slack messages reasonable
4. POSTs the message as a `{"text": "..."}` JSON payload to the configured webhook URL

## Changes

- **`internal/database/model_target.go`** — Add `TargetTypeSlack` constant
- **`internal/delivery/engine.go`** — Add `SlackTargetConfig` struct, `deliverSlack` method, `FormatSlackMessage` function (exported), `parseSlackConfig` helper. Route slack targets in `processDelivery` switch.
- **`internal/handlers/source_management.go`** — Handle `slack` type in `HandleTargetCreate`, building `webhook_url` config from the URL form field
- **`templates/source_detail.html`** — Add "Slack" option to target type dropdown with URL field and helper text
- **`README.md`** — Document the new target type, update roadmap

## Tests

- `TestParseSlackConfig_Valid` / `_Empty` / `_MissingWebhookURL` — Config parsing
- `TestFormatSlackMessage_JSONBody` / `_NonJSONBody` / `_EmptyBody` / `_LargeJSONTruncated` — Message formatting
- `TestDeliverSlack_Success` / `_Failure` / `_InvalidConfig` — End-to-end delivery
- `TestProcessDelivery_RoutesToSlack` — Routing from processDelivery switch

All existing tests continue to pass. `docker build .` (which runs `make check`) passes clean.

Co-authored-by: user <user@Mac.lan guest wan>
Reviewed-on: #47
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #47.
This commit is contained in:
2026-03-17 12:30:50 +01:00
committed by Jeffrey Paul
parent 1fbcf96581
commit 8d702a16c6
6 changed files with 465 additions and 6 deletions

View File

@@ -519,14 +519,14 @@ func (h *Handlers) HandleTargetCreate() http.HandlerFunc {
// Validate target type
switch targetType {
case database.TargetTypeHTTP, database.TargetTypeDatabase, database.TargetTypeLog:
case database.TargetTypeHTTP, database.TargetTypeDatabase, database.TargetTypeLog, database.TargetTypeSlack:
// valid
default:
http.Error(w, "Invalid target type", http.StatusBadRequest)
return
}
// Build config JSON for HTTP targets
// Build config JSON based on target type
var configJSON string
if targetType == database.TargetTypeHTTP {
if url == "" {
@@ -542,6 +542,20 @@ func (h *Handlers) HandleTargetCreate() http.HandlerFunc {
return
}
configJSON = string(configBytes)
} else if targetType == database.TargetTypeSlack {
if url == "" {
http.Error(w, "Webhook URL is required for Slack targets", http.StatusBadRequest)
return
}
cfg := map[string]interface{}{
"webhook_url": url,
}
configBytes, err := json.Marshal(cfg)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
configJSON = string(configBytes)
}
maxRetries := 0 // default: fire-and-forget (no retries)