126 lines
3.2 KiB
Go
126 lines
3.2 KiB
Go
package settings
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// LoadActionWidgets reads and parses node-action-widgets.json from dir.
|
|
func LoadActionWidgets(dir string) (ActionWidgetStore, error) {
|
|
path := filepath.Join(dir, NodeActionWidgetsFileName)
|
|
payload, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return ActionWidgetStore{}, err
|
|
}
|
|
|
|
var store ActionWidgetStore
|
|
if err := json.Unmarshal(payload, &store); err != nil {
|
|
return ActionWidgetStore{}, fmt.Errorf("parse action widgets: %w", err)
|
|
}
|
|
if store.Widgets == nil {
|
|
store.Widgets = []ActionWidgetSnapshot{}
|
|
}
|
|
return store, nil
|
|
}
|
|
|
|
// LoadActionWidgetsOrEmpty returns an empty store when the file is missing.
|
|
func LoadActionWidgetsOrEmpty(dir string) (ActionWidgetStore, error) {
|
|
store, err := LoadActionWidgets(dir)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return ActionWidgetStore{Widgets: []ActionWidgetSnapshot{}}, nil
|
|
}
|
|
return ActionWidgetStore{}, err
|
|
}
|
|
return store, nil
|
|
}
|
|
|
|
// SaveActionWidgets writes node-action-widgets.json to dir with mode 0640.
|
|
func SaveActionWidgets(dir string, store ActionWidgetStore) error {
|
|
if err := ensureConfigDir(dir); err != nil {
|
|
return err
|
|
}
|
|
if store.Widgets == nil {
|
|
store.Widgets = []ActionWidgetSnapshot{}
|
|
}
|
|
|
|
payload, err := json.MarshalIndent(store, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("encode action widgets: %w", err)
|
|
}
|
|
payload = append(payload, '\n')
|
|
|
|
path := filepath.Join(dir, NodeActionWidgetsFileName)
|
|
return os.WriteFile(path, payload, 0o640)
|
|
}
|
|
|
|
// WidgetsForNode returns snapshots belonging to nodeID.
|
|
func WidgetsForNode(store ActionWidgetStore, nodeID string) []ActionWidgetSnapshot {
|
|
out := make([]ActionWidgetSnapshot, 0)
|
|
for _, widget := range store.Widgets {
|
|
if widget.NodeID == nodeID {
|
|
out = append(out, widget)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// UpsertActionWidgetSnapshot replaces or appends a snapshot keyed by node_id + item_id.
|
|
func UpsertActionWidgetSnapshot(dir string, snapshot ActionWidgetSnapshot) error {
|
|
if strings.TrimSpace(snapshot.NodeID) == "" {
|
|
return fmt.Errorf("node id is required")
|
|
}
|
|
if strings.TrimSpace(snapshot.ItemID) == "" {
|
|
return fmt.Errorf("item id is required")
|
|
}
|
|
if snapshot.UpdatedAt.IsZero() {
|
|
snapshot.UpdatedAt = time.Now().UTC()
|
|
}
|
|
|
|
store, err := LoadActionWidgetsOrEmpty(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
found := false
|
|
for index := range store.Widgets {
|
|
if store.Widgets[index].NodeID == snapshot.NodeID &&
|
|
store.Widgets[index].ItemID == snapshot.ItemID {
|
|
store.Widgets[index] = snapshot
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
store.Widgets = append(store.Widgets, snapshot)
|
|
}
|
|
return SaveActionWidgets(dir, store)
|
|
}
|
|
|
|
// DeleteActionWidgetsForItem removes the snapshot for a node action item, if any.
|
|
func DeleteActionWidgetsForItem(dir string, nodeID string, itemID string) error {
|
|
store, err := LoadActionWidgetsOrEmpty(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
filtered := make([]ActionWidgetSnapshot, 0, len(store.Widgets))
|
|
changed := false
|
|
for _, widget := range store.Widgets {
|
|
if widget.NodeID == nodeID && widget.ItemID == itemID {
|
|
changed = true
|
|
continue
|
|
}
|
|
filtered = append(filtered, widget)
|
|
}
|
|
if !changed {
|
|
return nil
|
|
}
|
|
store.Widgets = filtered
|
|
return SaveActionWidgets(dir, store)
|
|
}
|