verstak/internal/core/activity/activity.go

117 lines
3.1 KiB
Go

package activity
import (
"time"
"verstak/internal/core/storage"
"verstak/internal/core/util"
)
// Event types.
const (
TypeNoteCreated = "note_created"
TypeNoteUpdated = "note_updated"
TypeFileAdded = "file_added"
TypeFileDeleted = "file_deleted"
TypeFileRenamed = "file_renamed"
TypeFileCopied = "file_copied"
TypeFileMoved = "file_moved"
TypeFolderAdded = "folder_added"
TypeFolderDeleted = "folder_deleted"
TypeFolderRenamed = "folder_renamed"
TypeNodeCreated = "node_created"
TypeNodeUpdated = "node_updated"
)
// Event represents an activity event.
type Event struct {
ID string `json:"id"`
NodeID string `json:"node_id"`
ParentID string `json:"parent_id,omitempty"`
EventType string `json:"event_type"`
Title string `json:"title"`
Metadata string `json:"metadata"`
CreatedAt string `json:"created_at"`
}
// Service records and queries activity events.
type Service struct {
db *storage.DB
}
func NewService(db *storage.DB) *Service {
return &Service{db: db}
}
// Record inserts a new activity event.
func (s *Service) Record(nodeID, parentID, eventType, title, metadata string) error {
id := util.UUID7()
now := time.Now().UTC().Format(time.RFC3339)
if metadata == "" {
metadata = "{}"
}
if parentID != "" {
_, err := s.db.Exec(
`INSERT INTO activity_events(id,node_id,parent_id,event_type,title,metadata,created_at)
VALUES(?,?,?,?,?,?,?)`,
id, nodeID, parentID, eventType, title, metadata, now)
return err
}
_, err := s.db.Exec(
`INSERT INTO activity_events(id,node_id,event_type,title,metadata,created_at)
VALUES(?,?,?,?,?,?)`,
id, nodeID, eventType, title, metadata, now)
return err
}
// todayBoundaries returns the current local day in UTC.
// TodayBoundaries returns the current local day in UTC as RFC3339 strings.
func TodayBoundaries() (string, string) {
now := time.Now()
y, m, d := now.Date()
start := time.Date(y, m, d, 0, 0, 0, 0, now.Location())
end := start.Add(24 * time.Hour)
return start.UTC().Format(time.RFC3339), end.UTC().Format(time.RFC3339)
}
// ListTodayEvents returns all activity events from today.
func (s *Service) ListTodayEvents() ([]Event, error) {
start, end := TodayBoundaries()
rows, err := s.db.Query(
`SELECT id,node_id,COALESCE(parent_id,''),event_type,title,metadata,created_at
FROM activity_events
WHERE created_at >= ? AND created_at < ?
ORDER BY created_at DESC`, start, end)
if err != nil {
return nil, err
}
defer rows.Close()
var events []Event
for rows.Next() {
var e Event
if err := rows.Scan(&e.ID, &e.NodeID, &e.ParentID, &e.EventType, &e.Title, &e.Metadata, &e.CreatedAt); err != nil {
return nil, err
}
events = append(events, e)
}
return events, rows.Err()
}
// ListTodayEventsByParent returns activity events grouped by parent node.
func (s *Service) ListTodayEventsByParent() (map[string][]Event, error) {
events, err := s.ListTodayEvents()
if err != nil {
return nil, err
}
grouped := make(map[string][]Event, 8)
for _, e := range events {
pid := e.ParentID
if pid == "" {
pid = e.NodeID
}
grouped[pid] = append(grouped[pid], e)
}
return grouped, nil
}