- Implemented the incoming webhook API to handle alerts from external applications. - Added configuration options for webhook listening address, port, and endpoints in config.go. - Created WebhookReceiverConfig and WebhookEndpointConfig structures to manage webhook settings. - Developed WebhookHandler to process incoming requests, validate tokens, and deliver alerts to specified channels. - Enhanced existing controller interfaces to support alert delivery. - Updated message rendering to respect Markdown settings for different channels. - Added tests for webhook functionality and ensured proper error handling.
209 lines
9.2 KiB
Go
209 lines
9.2 KiB
Go
package template
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"nukumizu-backend/global"
|
|
"nukumizu-backend/internal/node"
|
|
)
|
|
|
|
// Params holds all possible template parameters.
|
|
type Params struct {
|
|
Time string
|
|
ServerName string
|
|
ServerUUID string
|
|
UpStatus string
|
|
Event string
|
|
Message string
|
|
Command string
|
|
Result string
|
|
Subject string // Alert subject (see AlertParams)
|
|
Source string // Alert source (see AlertParams)
|
|
Content string // Alert content (see AlertParams)
|
|
OnlineServers string // Pre-formatted multi-line list
|
|
OfflineServers string // Pre-formatted multi-line list
|
|
SoftwareVersion string
|
|
SoftwareBuildVer int16
|
|
SoftwareCommitHash string
|
|
SoftwareBuildType string
|
|
SoftwareBuildTime string
|
|
SoftwareDeveloper string
|
|
SoftwareDescription string
|
|
}
|
|
|
|
// AlertTemplate is the body format of an alert submitted by an external
|
|
// application through the incoming webhook API.
|
|
const AlertTemplate = "{{ subject }}\n- Source: {{ source }}\n- Content:\n{{ content }}\n\n- Time: {{ time }}\nSent by Nukumizu Alert System"
|
|
|
|
// AlertParams holds the parameters of an alert submitted through the incoming
|
|
// webhook API.
|
|
type AlertParams struct {
|
|
Subject string // Short one-line title of the alert
|
|
Source string // Name of the webhook endpoint the alert was submitted to
|
|
Content string // Free-form alert body
|
|
Time string // Submission time
|
|
}
|
|
|
|
// RenderAlert renders the body of an alert for a channel. The alert source and
|
|
// content may be wrapped in Markdown — the source in inline code, the content
|
|
// in a fenced code block — when the target channel has markdown enabled
|
|
// (markdown); everything else, the timestamp included, stays plain text.
|
|
func RenderAlert(alert AlertParams, markdown bool) string {
|
|
params := Params{
|
|
Time: alert.Time,
|
|
Subject: alert.Subject,
|
|
Source: alert.Source,
|
|
Content: alert.Content,
|
|
}
|
|
if markdown {
|
|
params.Source = "`" + params.Source + "`"
|
|
params.Content = "```\n" + params.Content + "\n```"
|
|
}
|
|
return Render(AlertTemplate, params, false)
|
|
}
|
|
|
|
// BuildBotInitializationMsgParams creates template parameters for the bot initialization message.
|
|
func BuildBotInitializationMsgParams() Params {
|
|
return Params{
|
|
Time: time.Now().Format("2006-01-02T15:04:05.000000000-07:00"),
|
|
SoftwareVersion: global.SoftwareInfo.Version,
|
|
SoftwareBuildVer: global.SoftwareInfo.BuildVer,
|
|
SoftwareCommitHash: global.SoftwareInfo.CommitHash,
|
|
SoftwareBuildType: global.SoftwareInfo.BuildType,
|
|
SoftwareBuildTime: global.SoftwareInfo.BuildTime,
|
|
SoftwareDeveloper: global.SoftwareInfo.Developer,
|
|
SoftwareDescription: global.SoftwareInfo.Description,
|
|
}
|
|
}
|
|
|
|
// BuildParamsFromStatusChange creates template parameters from a status change event.
|
|
func BuildParamsFromStatusChange(change node.StatusChange) Params {
|
|
return Params{
|
|
Time: time.Now().Format("2006-01-02T15:04:05.000000000-07:00"),
|
|
ServerName: change.Name,
|
|
ServerUUID: change.UUID,
|
|
UpStatus: change.Event,
|
|
Event: change.Event,
|
|
Message: change.Message,
|
|
}
|
|
}
|
|
|
|
// BuildParamsFromServerList creates template parameters for the server list.
|
|
func BuildParamsFromServerList() Params {
|
|
tracker := node.GetTracker()
|
|
onlineServers := strings.Join(tracker.GetOnlineServers(), "\n")
|
|
offlineServers := strings.Join(tracker.GetOfflineServers(), "\n")
|
|
|
|
return Params{
|
|
Time: time.Now().Format("2006-01-02T15:04:05.000000000-07:00"),
|
|
OnlineServers: onlineServers,
|
|
OfflineServers: offlineServers,
|
|
}
|
|
}
|
|
|
|
// BuildParamsFromExecResult creates template parameters for a command execution result.
|
|
func BuildParamsFromExecResult(serverName, serverUUID, command, result string) Params {
|
|
return Params{
|
|
Time: time.Now().Format("2006-01-02T15:04:05.000000000-07:00"),
|
|
ServerName: serverName,
|
|
ServerUUID: serverUUID,
|
|
Command: command,
|
|
Result: result,
|
|
}
|
|
}
|
|
|
|
// Render substitutes {{ paramName }} placeholders in a template string. The
|
|
// markdown argument is the target channel's markdown setting: when true the
|
|
// values that are meant to be read verbatim (UUIDs, messages, commands, command
|
|
// results) are wrapped in Markdown code spans and blocks, otherwise every value
|
|
// is inserted as plain text. Whether a channel renders Markdown comes from the
|
|
// configuration alone — the renderer never infers it from the channel name.
|
|
//
|
|
// Supported placeholders:
|
|
// - {{ time }} — current server time
|
|
// - {{ serverName }} — server name
|
|
// - {{ serverUUID }} — server UUID
|
|
// - {{ upStatus }} — "Online" or "Offline"
|
|
// - {{ event }} — "Online" or "Offline"
|
|
// - {{ message }} — event descriptive message
|
|
// - {{ command }} — executed command
|
|
// - {{ result }} — command execution result
|
|
// - {{ subject }} — alert subject
|
|
// - {{ source }} — alert source
|
|
// - {{ content }} — alert content
|
|
// - {{ list.onlineServers }} — multi-line online server list
|
|
// - {{ list.offlineServers }} — multi-line offline server list
|
|
// - {{ softwareVersion }} — software version
|
|
// - {{ softwareBuildVer }} — software build version
|
|
// - {{ softwareCommitHash }} — software commit hash
|
|
// - {{ softwareBuildType }} — software build type (Debug/Release)
|
|
// - {{ softwareBuildTime }} — software build time
|
|
// - {{ softwareDeveloper }} — software developer
|
|
// - {{ softwareDescription }} — software description
|
|
func Render(tmpl string, params Params, markdown bool) string {
|
|
result := tmpl
|
|
|
|
if markdown {
|
|
// Channels that render Markdown get code blocks and inline code for the
|
|
// values that are read verbatim.
|
|
result = strings.ReplaceAll(result, "{{ time }}", "**"+params.Time+"**")
|
|
result = strings.ReplaceAll(result, "{{ serverName }}", "**"+params.ServerName+"**")
|
|
result = strings.ReplaceAll(result, "{{ serverUUID }}", "`"+params.ServerUUID+"`")
|
|
result = strings.ReplaceAll(result, "{{ upStatus }}", "**"+params.UpStatus+"**")
|
|
result = strings.ReplaceAll(result, "{{ event }}", "**"+params.Event+"**")
|
|
result = strings.ReplaceAll(result, "{{ message }}", "`"+params.Message+"`")
|
|
result = strings.ReplaceAll(result, "{{ command }}", "`"+params.Command+"`")
|
|
result = strings.ReplaceAll(result, "{{ result }}", "```bash\n"+params.Result+"\n```")
|
|
result = strings.ReplaceAll(result, "{{ list.onlineServers }}", params.OnlineServers)
|
|
result = strings.ReplaceAll(result, "{{ list.offlineServers }}", params.OfflineServers)
|
|
result = strings.ReplaceAll(result, "{{ softwareVersion }}", params.SoftwareVersion)
|
|
result = strings.ReplaceAll(result, "{{ softwareBuildVer }}", fmt.Sprintf("%d", params.SoftwareBuildVer))
|
|
result = strings.ReplaceAll(result, "{{ softwareCommitHash }}", params.SoftwareCommitHash)
|
|
result = strings.ReplaceAll(result, "{{ softwareBuildType }}", params.SoftwareBuildType)
|
|
result = strings.ReplaceAll(result, "{{ softwareBuildTime }}", params.SoftwareBuildTime)
|
|
result = strings.ReplaceAll(result, "{{ softwareDeveloper }}", params.SoftwareDeveloper)
|
|
result = strings.ReplaceAll(result, "{{ softwareDescription }}", params.SoftwareDescription)
|
|
} else {
|
|
result = strings.ReplaceAll(result, "{{ time }}", params.Time)
|
|
result = strings.ReplaceAll(result, "{{ serverName }}", params.ServerName)
|
|
result = strings.ReplaceAll(result, "{{ serverUUID }}", params.ServerUUID)
|
|
result = strings.ReplaceAll(result, "{{ upStatus }}", params.UpStatus)
|
|
result = strings.ReplaceAll(result, "{{ event }}", params.Event)
|
|
result = strings.ReplaceAll(result, "{{ message }}", params.Message)
|
|
result = strings.ReplaceAll(result, "{{ command }}", params.Command)
|
|
result = strings.ReplaceAll(result, "{{ result }}", params.Result)
|
|
result = strings.ReplaceAll(result, "{{ list.onlineServers }}", params.OnlineServers)
|
|
result = strings.ReplaceAll(result, "{{ list.offlineServers }}", params.OfflineServers)
|
|
result = strings.ReplaceAll(result, "{{ softwareVersion }}", params.SoftwareVersion)
|
|
result = strings.ReplaceAll(result, "{{ softwareBuildVer }}", fmt.Sprintf("%d", params.SoftwareBuildVer))
|
|
result = strings.ReplaceAll(result, "{{ softwareCommitHash }}", params.SoftwareCommitHash)
|
|
result = strings.ReplaceAll(result, "{{ softwareBuildType }}", params.SoftwareBuildType)
|
|
result = strings.ReplaceAll(result, "{{ softwareBuildTime }}", params.SoftwareBuildTime)
|
|
result = strings.ReplaceAll(result, "{{ softwareDeveloper }}", params.SoftwareDeveloper)
|
|
result = strings.ReplaceAll(result, "{{ softwareDescription }}", params.SoftwareDescription)
|
|
}
|
|
|
|
// Alert values carry their own formatting (see RenderAlert), so they are
|
|
// substituted identically in both branches.
|
|
result = strings.ReplaceAll(result, "{{ subject }}", params.Subject)
|
|
result = strings.ReplaceAll(result, "{{ source }}", params.Source)
|
|
result = strings.ReplaceAll(result, "{{ content }}", params.Content)
|
|
|
|
return result
|
|
}
|
|
|
|
// FormatServerListEntry formats a single server entry for list display.
|
|
func FormatServerListEntry(name, uuid string) string {
|
|
if name == "" {
|
|
return fmt.Sprintf("- `%s`", uuid)
|
|
}
|
|
return fmt.Sprintf("- %s (`%s`)", name, uuid)
|
|
}
|
|
|
|
// JoinServerListEntries joins formatted server entries with newlines.
|
|
func JoinServerListEntries(entries []string) string {
|
|
return strings.Join(entries, "\n")
|
|
}
|