Files

87 lines
2.3 KiB
Go
Raw Permalink Normal View History

2026-09-18 12:03:35 +02:00
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
package webhooks
import (
"fmt"
"os"
"sourcedock.dev/petrbalvin/interpres/v2"
"sourcedock.dev/petrbalvin/volumen/internal/tomlfile"
)
// File is the admin-managed hook store: webhooks.toml beside the users
// file. The admin edits it through the settings forms, so a change
// applies without a restart; the file is written atomically with
// owner-only permissions, like the other state files.
type File struct {
Path string
}
// LoadFile reads the hook store. A missing file means no hooks, not an
// error; a malformed one is, because silently dropping the operator's
// endpoints would hide a real fault.
func LoadFile(path string) ([]Webhook, error) {
raw, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("read %s: %w", path, err)
}
data, err := interpres.ParseMap(raw)
if err != nil {
return nil, fmt.Errorf("parse %s: %w", path, err)
}
var out []Webhook
for _, entry := range tomlfile.Tables(data["webhooks"]) {
url := tomlfile.String(entry["url"])
if url == "" {
continue
}
out = append(out, Webhook{
URL: url,
Secret: tomlfile.String(entry["secret"]),
Events: stringList(entry["events"]),
Enabled: tomlfile.Bool(entry["enabled"], true),
})
}
return out, nil
}
// SaveFile replaces the hook store with the given hooks.
func SaveFile(path string, hooks []Webhook) error {
rows := make([]map[string]any, 0, len(hooks))
for _, hook := range hooks {
rows = append(rows, map[string]any{
"url": hook.URL,
"secret": hook.Secret,
"events": hook.Events,
"enabled": hook.Enabled,
})
}
if err := tomlfile.Write(path, "webhooks", rows); err != nil {
return fmt.Errorf("write %s: %w", path, err)
}
return nil
}
// stringList reads a string array that may arrive as either the []any
// the parser produces or a typed slice.
func stringList(value any) []string {
switch list := value.(type) {
case []string:
return list
case []any:
out := make([]string, 0, len(list))
for _, item := range list {
if s, ok := item.(string); ok && s != "" {
out = append(out, s)
}
}
return out
}
return nil
}