feat(admin): add security and activity management features
This commit is contained in:
151
lib/alerts/alerts.go
Normal file
151
lib/alerts/alerts.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package alerts
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Status string
|
||||
|
||||
const (
|
||||
StatusOpen Status = "open"
|
||||
StatusAcked Status = "acked"
|
||||
StatusClosed Status = "closed"
|
||||
)
|
||||
|
||||
type Alert struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Severity string `json:"severity"`
|
||||
Status Status `json:"status"`
|
||||
Group string `json:"group"`
|
||||
Code string `json:"code"`
|
||||
Trace string `json:"trace"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Meta map[string]string `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
type Store struct {
|
||||
path string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewStore(path string) *Store {
|
||||
return &Store{path: path}
|
||||
}
|
||||
|
||||
func (s *Store) Add(alert Alert) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
alertsList, err := s.readLocked()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
if alert.ID == "" {
|
||||
alert.ID = strconv.FormatInt(now.UnixNano(), 10)
|
||||
}
|
||||
if alert.Status == "" {
|
||||
alert.Status = StatusOpen
|
||||
}
|
||||
if alert.CreatedAt.IsZero() {
|
||||
alert.CreatedAt = now
|
||||
}
|
||||
alert.UpdatedAt = now
|
||||
alertsList = append(alertsList, alert)
|
||||
return s.writeLocked(alertsList)
|
||||
}
|
||||
|
||||
func (s *Store) List(limit int) ([]Alert, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
alertsList, err := s.readLocked()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(alertsList, func(i, j int) bool {
|
||||
return alertsList[i].CreatedAt.After(alertsList[j].CreatedAt)
|
||||
})
|
||||
if limit > 0 && len(alertsList) > limit {
|
||||
return alertsList[:limit], nil
|
||||
}
|
||||
return alertsList, nil
|
||||
}
|
||||
|
||||
func (s *Store) SetStatus(ids []string, status Status) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
alertsList, err := s.readLocked()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target := map[string]bool{}
|
||||
for _, id := range ids {
|
||||
target[id] = true
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
for i := range alertsList {
|
||||
if target[alertsList[i].ID] {
|
||||
alertsList[i].Status = status
|
||||
alertsList[i].UpdatedAt = now
|
||||
}
|
||||
}
|
||||
return s.writeLocked(alertsList)
|
||||
}
|
||||
|
||||
func (s *Store) Delete(ids []string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
alertsList, err := s.readLocked()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target := map[string]bool{}
|
||||
for _, id := range ids {
|
||||
target[id] = true
|
||||
}
|
||||
kept := make([]Alert, 0, len(alertsList))
|
||||
for _, alert := range alertsList {
|
||||
if !target[alert.ID] {
|
||||
kept = append(kept, alert)
|
||||
}
|
||||
}
|
||||
return s.writeLocked(kept)
|
||||
}
|
||||
|
||||
func (s *Store) readLocked() ([]Alert, error) {
|
||||
data, err := os.ReadFile(s.path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return []Alert{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return []Alert{}, nil
|
||||
}
|
||||
var alertsList []Alert
|
||||
if err := json.Unmarshal(data, &alertsList); err != nil {
|
||||
return []Alert{}, nil
|
||||
}
|
||||
return alertsList, nil
|
||||
}
|
||||
|
||||
func (s *Store) writeLocked(alertsList []Alert) error {
|
||||
if err := os.MkdirAll(filepath.Dir(s.path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.MarshalIndent(alertsList, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(s.path, data, 0644)
|
||||
}
|
||||
Reference in New Issue
Block a user