Files
go-hauk/model/session.go
2025-12-23 13:25:56 +01:00

129 lines
3.1 KiB
Go

package model
import (
"context"
"crypto/rand"
"encoding/hex"
"time"
"github.com/parkan/go-hauk/store"
)
const (
PrefixSession = "session-"
SessionIDSize = 32
)
type SessionData struct {
Expire int64 `json:"expire"`
Interval float64 `json:"interval"`
Targets []string `json:"targets"`
Points [][]any `json:"points"`
Encrypted bool `json:"encrypted"`
Salt string `json:"salt,omitempty"`
}
type Session struct {
store store.Store
id string
data SessionData
maxPts int
}
func NewSession(s store.Store, maxPts int) (*Session, error) {
id, err := generateSessionID()
if err != nil {
return nil, err
}
return &Session{
store: s,
id: id,
maxPts: maxPts,
data: SessionData{
Targets: []string{},
Points: [][]any{},
},
}, nil
}
func LoadSession(ctx context.Context, s store.Store, id string, maxPts int) (*Session, error) {
sess := &Session{store: s, id: id, maxPts: maxPts}
err := s.Get(ctx, PrefixSession+id, &sess.data)
if err != nil {
return nil, err
}
return sess, nil
}
func (s *Session) ID() string { return s.id }
func (s *Session) Expire() time.Time { return time.Unix(s.data.Expire, 0) }
func (s *Session) Interval() float64 { return s.data.Interval }
func (s *Session) Targets() []string { return s.data.Targets }
func (s *Session) Points() [][]any { return s.data.Points }
func (s *Session) Encrypted() bool { return s.data.Encrypted }
func (s *Session) Salt() string { return s.data.Salt }
func (s *Session) HasExpired() bool { return time.Now().Unix() >= s.data.Expire }
func (s *Session) SetExpire(t time.Time) { s.data.Expire = t.Unix() }
func (s *Session) SetInterval(i float64) { s.data.Interval = i }
func (s *Session) SetEncrypted(e bool, salt string) {
s.data.Encrypted = e
s.data.Salt = salt
}
func (s *Session) AddTarget(shareID string) {
s.data.Targets = append(s.data.Targets, shareID)
}
func (s *Session) RemoveTarget(shareID string) {
for i, t := range s.data.Targets {
if t == shareID {
s.data.Targets = append(s.data.Targets[:i], s.data.Targets[i+1:]...)
return
}
}
}
func (s *Session) AddPoint(p []any) {
s.data.Points = append(s.data.Points, p)
if len(s.data.Points) > s.maxPts {
s.data.Points = s.data.Points[len(s.data.Points)-s.maxPts:]
}
}
func (s *Session) GetPoints(since float64) [][]any {
if since <= 0 {
return s.data.Points
}
// encrypted sessions have opaque timestamps - can't filter server-side
if s.data.Encrypted {
return s.data.Points
}
var pts [][]any
for _, p := range s.data.Points {
if len(p) > 2 {
if t, ok := p[2].(float64); ok && t > since {
pts = append(pts, p)
}
}
}
return pts
}
func (s *Session) Save(ctx context.Context) error {
return s.store.Set(ctx, PrefixSession+s.id, s.data, s.Expire())
}
func (s *Session) Delete(ctx context.Context) error {
return s.store.Delete(ctx, PrefixSession+s.id)
}
func generateSessionID() (string, error) {
b := make([]byte, SessionIDSize)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}