mirror of
https://github.com/parkan/go-hauk.git
synced 2026-05-08 16:47:46 +02:00
add data models
This commit is contained in:
25
model/point.go
Normal file
25
model/point.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package model
|
||||
|
||||
type Point struct {
|
||||
IV string `json:"iv,omitempty"`
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
Time float64 `json:"time"`
|
||||
Provider int `json:"prv"`
|
||||
Accuracy *float64 `json:"acc,omitempty"`
|
||||
Speed *float64 `json:"spd,omitempty"`
|
||||
}
|
||||
|
||||
func (p Point) ToArray(encrypted bool) []any {
|
||||
if encrypted {
|
||||
return []any{p.IV, p.Lat, p.Lon, p.Time, p.Provider, p.Accuracy, p.Speed}
|
||||
}
|
||||
return []any{p.Lat, p.Lon, p.Time, p.Provider, p.Accuracy, p.Speed}
|
||||
}
|
||||
|
||||
func (p Point) TimeIndex(encrypted bool) int {
|
||||
if encrypted {
|
||||
return 3
|
||||
}
|
||||
return 2
|
||||
}
|
||||
128
model/session.go
Normal file
128
model/session.go
Normal file
@@ -0,0 +1,128 @@
|
||||
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
|
||||
}
|
||||
timeIdx := 2
|
||||
if s.data.Encrypted {
|
||||
timeIdx = 3
|
||||
}
|
||||
var pts [][]any
|
||||
for _, p := range s.data.Points {
|
||||
if len(p) > timeIdx {
|
||||
if t, ok := p[timeIdx].(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
|
||||
}
|
||||
203
model/share.go
Normal file
203
model/share.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/parkan/go-hauk/store"
|
||||
)
|
||||
|
||||
const (
|
||||
PrefixLocdata = "locdata-"
|
||||
PrefixGroupID = "groupid-"
|
||||
|
||||
ShareTypeAlone = 0
|
||||
ShareTypeGroup = 1
|
||||
|
||||
GroupPinMin = 100000
|
||||
GroupPinMax = 999999
|
||||
)
|
||||
|
||||
type SoloShareData struct {
|
||||
Type int `json:"type"`
|
||||
Expire int64 `json:"expire"`
|
||||
Host string `json:"host"`
|
||||
Adoptable bool `json:"adoptable"`
|
||||
}
|
||||
|
||||
type GroupShareData struct {
|
||||
Type int `json:"type"`
|
||||
Expire int64 `json:"expire"`
|
||||
Hosts map[string]string `json:"hosts"`
|
||||
GroupPin int `json:"groupPin"`
|
||||
}
|
||||
|
||||
type SoloShare struct {
|
||||
store store.Store
|
||||
id string
|
||||
data SoloShareData
|
||||
publicURL string
|
||||
}
|
||||
|
||||
type GroupShare struct {
|
||||
store store.Store
|
||||
id string
|
||||
data GroupShareData
|
||||
publicURL string
|
||||
}
|
||||
|
||||
func NewSoloShare(s store.Store, publicURL string, linkGen func() (string, error)) (*SoloShare, error) {
|
||||
id, err := linkGen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SoloShare{
|
||||
store: s,
|
||||
id: id,
|
||||
publicURL: publicURL,
|
||||
data: SoloShareData{
|
||||
Type: ShareTypeAlone,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func LoadSoloShare(ctx context.Context, s store.Store, id, publicURL string) (*SoloShare, error) {
|
||||
share := &SoloShare{store: s, id: id, publicURL: publicURL}
|
||||
err := s.Get(ctx, PrefixLocdata+id, &share.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return share, nil
|
||||
}
|
||||
|
||||
func (s *SoloShare) ID() string { return s.id }
|
||||
func (s *SoloShare) Type() int { return s.data.Type }
|
||||
func (s *SoloShare) Expire() time.Time { return time.Unix(s.data.Expire, 0) }
|
||||
func (s *SoloShare) Host() string { return s.data.Host }
|
||||
func (s *SoloShare) Adoptable() bool { return s.data.Adoptable }
|
||||
func (s *SoloShare) ViewLink() string { return s.publicURL + "?" + s.id }
|
||||
|
||||
func (s *SoloShare) SetExpire(t time.Time) { s.data.Expire = t.Unix() }
|
||||
func (s *SoloShare) SetHost(sid string) { s.data.Host = sid }
|
||||
func (s *SoloShare) SetAdoptable(a bool) { s.data.Adoptable = a }
|
||||
func (s *SoloShare) SetID(id string) { s.id = id }
|
||||
|
||||
func (s *SoloShare) Save(ctx context.Context) error {
|
||||
return s.store.Set(ctx, PrefixLocdata+s.id, s.data, s.Expire())
|
||||
}
|
||||
|
||||
func (s *SoloShare) Delete(ctx context.Context) error {
|
||||
return s.store.Delete(ctx, PrefixLocdata+s.id)
|
||||
}
|
||||
|
||||
func NewGroupShare(s store.Store, publicURL string, linkGen func() (string, error)) (*GroupShare, error) {
|
||||
id, err := linkGen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pin := GroupPinMin + rand.Intn(GroupPinMax-GroupPinMin+1)
|
||||
return &GroupShare{
|
||||
store: s,
|
||||
id: id,
|
||||
publicURL: publicURL,
|
||||
data: GroupShareData{
|
||||
Type: ShareTypeGroup,
|
||||
Hosts: make(map[string]string),
|
||||
GroupPin: pin,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func LoadGroupShare(ctx context.Context, s store.Store, id, publicURL string) (*GroupShare, error) {
|
||||
share := &GroupShare{store: s, id: id, publicURL: publicURL}
|
||||
err := s.Get(ctx, PrefixLocdata+id, &share.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return share, nil
|
||||
}
|
||||
|
||||
func LoadGroupShareByPin(ctx context.Context, s store.Store, pin int, publicURL string) (*GroupShare, error) {
|
||||
var shareID string
|
||||
err := s.Get(ctx, PrefixGroupID+strconv.Itoa(pin), &shareID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return LoadGroupShare(ctx, s, shareID, publicURL)
|
||||
}
|
||||
|
||||
func (g *GroupShare) ID() string { return g.id }
|
||||
func (g *GroupShare) Type() int { return g.data.Type }
|
||||
func (g *GroupShare) Expire() time.Time { return time.Unix(g.data.Expire, 0) }
|
||||
func (g *GroupShare) Hosts() map[string]string { return g.data.Hosts }
|
||||
func (g *GroupShare) Pin() int { return g.data.GroupPin }
|
||||
func (g *GroupShare) ViewLink() string { return g.publicURL + "?" + g.id }
|
||||
|
||||
func (g *GroupShare) SetExpire(t time.Time) { g.data.Expire = t.Unix() }
|
||||
func (g *GroupShare) SetID(id string) { g.id = id }
|
||||
|
||||
func (g *GroupShare) AddHost(nick, sessionID string) {
|
||||
g.data.Hosts[nick] = sessionID
|
||||
}
|
||||
|
||||
func (g *GroupShare) RemoveHost(sessionID string) {
|
||||
for nick, sid := range g.data.Hosts {
|
||||
if sid == sessionID {
|
||||
delete(g.data.Hosts, nick)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupShare) Save(ctx context.Context) error {
|
||||
if err := g.store.Set(ctx, PrefixLocdata+g.id, g.data, g.Expire()); err != nil {
|
||||
return err
|
||||
}
|
||||
return g.store.Set(ctx, PrefixGroupID+strconv.Itoa(g.data.GroupPin), g.id, g.Expire())
|
||||
}
|
||||
|
||||
func (g *GroupShare) Delete(ctx context.Context) error {
|
||||
g.store.Delete(ctx, PrefixGroupID+strconv.Itoa(g.data.GroupPin))
|
||||
return g.store.Delete(ctx, PrefixLocdata+g.id)
|
||||
}
|
||||
|
||||
func (g *GroupShare) GetAllPoints(ctx context.Context, since float64, maxPts int) (map[string][][]any, error) {
|
||||
points := make(map[string][][]any)
|
||||
for nick, sid := range g.data.Hosts {
|
||||
sess, err := LoadSession(ctx, g.store, sid, maxPts)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
points[nick] = sess.GetPoints(since)
|
||||
}
|
||||
return points, nil
|
||||
}
|
||||
|
||||
func (g *GroupShare) GetAutoInterval(ctx context.Context, maxPts int) float64 {
|
||||
min := float64(0)
|
||||
for _, sid := range g.data.Hosts {
|
||||
sess, err := LoadSession(ctx, g.store, sid, maxPts)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if min == 0 || sess.Interval() < min {
|
||||
min = sess.Interval()
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
|
||||
type ShareType struct {
|
||||
Type int `json:"type"`
|
||||
}
|
||||
|
||||
func LoadShareType(ctx context.Context, s store.Store, id string) (int, error) {
|
||||
var st ShareType
|
||||
err := s.Get(ctx, PrefixLocdata+id, &st)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return st.Type, nil
|
||||
}
|
||||
Reference in New Issue
Block a user