Files
3x-ui-agent/internal/web/service/probe/notify_state.go
T

61 lines
1.9 KiB
Go

package probe
import (
"database/sql"
"errors"
)
// Item 7 B6: the notification engine's durable state lives in
// probe_setting, next to the roll-up watermark — the offline marks
// (one key per agent, "notify_down:<id>", the hub's down_since column
// in KV form so DeleteAgentData can strike exactly one agent's mark),
// the traffic steps already announced ("traffic_alerted", hub's key
// of the same name) and the expiry digest's last-sent date
// ("notify_expiry_sent"). The store stays the only writer of
// probe.db; the notify package reads and writes through here.
// StateGet reads one probe_setting value from the default store.
// ok is false when the key was never written; err is non-nil when no
// store is available, which callers treat as "skip this section"
// (the hub skips the traffic steps on a failed read rather than
// recording crossings it did not announce).
func StateGet(key string) (string, bool, error) {
st := Default()
if st == nil {
return "", false, errors.New("probe store unavailable")
}
return st.getState(key)
}
// StateSet writes one probe_setting value on the default store. An
// empty value deletes the key, so "cleared" and "never set" are the
// same state, as with the hub's down_since = 0.
func StateSet(key, value string) error {
st := Default()
if st == nil {
return errors.New("probe store unavailable")
}
return st.setState(key, value)
}
func (s *Store) getState(key string) (string, bool, error) {
var v string
err := s.db.QueryRow(`SELECT value FROM probe_setting WHERE key = ?`, key).Scan(&v)
if err == sql.ErrNoRows {
return "", false, nil
}
if err != nil {
return "", false, err
}
return v, true, nil
}
func (s *Store) setState(key, value string) error {
if value == "" {
_, err := s.db.Exec(`DELETE FROM probe_setting WHERE key = ?`, key)
return err
}
_, err := s.db.Exec(`INSERT OR REPLACE INTO probe_setting (key, value) VALUES (?, ?)`, key, value)
return err
}