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