mirror of
https://github.com/Luzifer/mapshare.git
synced 2024-12-20 14:41:19 +00:00
Add disk retention of state
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
2fa10b94cf
commit
c11d1e728c
6 changed files with 86 additions and 6 deletions
|
@ -8,6 +8,6 @@
|
||||||
|
|
||||||
This project is a very simple and data protecting alternative to sharing a location through Glympse or similar services.
|
This project is a very simple and data protecting alternative to sharing a location through Glympse or similar services.
|
||||||
|
|
||||||
You can setup your own instance in minutes, it does not require any database (even retained location data is dropped on restart of the service!) and you can share your location from a mobile browser. To view the location nothing more than a browser is required.
|
You can setup your own instance in minutes, it does not require any database (though you can have the retained locations stored on disk) and you can share your location from a mobile browser. To view the location nothing more than a browser is required.
|
||||||
|
|
||||||
When sharing a location you have the choice to select whether the server should retain the location data (until restart) or just pipe it through. Retaining the data has the advantage new viewers (or viewers whose websocket has reconnected) instantly see your location. When not retaining data the data is received, sent to all connected sockets and afterwards instantly forgotten.
|
When sharing a location you have the choice to select whether the server should retain the location data or just pipe it through. Retaining the data has the advantage new viewers (or viewers whose websocket has reconnected) instantly see your location. When not retaining data the data is received, sent to all connected sockets and afterwards instantly forgotten.
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
github.com/gorilla/websocket v1.4.1
|
github.com/gorilla/websocket v1.4.1
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||||
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e // indirect
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e // indirect
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -12,6 +12,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGi
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
|
|
@ -58,7 +58,7 @@ func handleMapSocket(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// In case a retained position is available queue it
|
// In case a retained position is available queue it
|
||||||
reqRetainerLock.RLock()
|
reqRetainerLock.RLock()
|
||||||
if p, ok := reqRetainer[mapID]; ok {
|
if p, ok := reqRetainer[mapID]; ok && time.Since(p.Time) < cfg.StateTimeout {
|
||||||
updates <- p
|
updates <- p
|
||||||
}
|
}
|
||||||
reqRetainerLock.RUnlock()
|
reqRetainerLock.RUnlock()
|
||||||
|
@ -111,6 +111,12 @@ func handleMapSubmit(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
reqRetainerLock.Unlock()
|
reqRetainerLock.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := retainState(); err != nil {
|
||||||
|
log.WithError(err).Error("Unable to retain state to disk")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
reqDistributorsLock.RLock()
|
reqDistributorsLock.RLock()
|
||||||
defer reqDistributorsLock.RUnlock()
|
defer reqDistributorsLock.RUnlock()
|
||||||
|
|
||||||
|
|
14
main.go
14
main.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -13,15 +14,18 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfg = struct {
|
cfg = struct {
|
||||||
Listen string `flag:"listen" default:":3000" description:"Port/IP to listen on"`
|
Listen string `flag:"listen" default:":3000" description:"Port/IP to listen on"`
|
||||||
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
||||||
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
StateFile string `flag:"state-file" default:"" description:"Where to store retained locations (empty for no state)"`
|
||||||
|
StateTimeout time.Duration `flag:"state-timeout" default:"24h" description:"When to drop retained states"`
|
||||||
|
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
version = "dev"
|
version = "dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
rconfig.AutoEnv(true)
|
||||||
if err := rconfig.ParseAndValidate(&cfg); err != nil {
|
if err := rconfig.ParseAndValidate(&cfg); err != nil {
|
||||||
log.Fatalf("Unable to parse commandline options: %s", err)
|
log.Fatalf("Unable to parse commandline options: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -39,6 +43,10 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if err := loadState(); err != nil {
|
||||||
|
log.WithError(err).Fatal("Unable to load state")
|
||||||
|
}
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
r.HandleFunc("/", handleRedirectRandom).Methods(http.MethodGet)
|
r.HandleFunc("/", handleRedirectRandom).Methods(http.MethodGet)
|
||||||
|
|
63
state.go
Normal file
63
state.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadState() error {
|
||||||
|
if cfg.StateFile == "" {
|
||||||
|
// No state file, no retaining
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(cfg.StateFile); err != nil {
|
||||||
|
log.WithError(err).Warn("Unable to load state, using empty state")
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.Wrap(err, "Unable to access state file")
|
||||||
|
}
|
||||||
|
|
||||||
|
reqRetainerLock.Lock()
|
||||||
|
defer reqRetainerLock.Unlock()
|
||||||
|
|
||||||
|
f, err := os.Open(cfg.StateFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to open state file")
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return errors.Wrap(json.NewDecoder(f).Decode(&reqRetainer), "Unable to decode state file")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func retainState() error {
|
||||||
|
if cfg.StateFile == "" {
|
||||||
|
// No state file, no retaining
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(cfg.StateFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to create state file")
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
reqRetainerLock.RLock()
|
||||||
|
defer reqRetainerLock.RUnlock()
|
||||||
|
|
||||||
|
var tmpState = make(map[string]position)
|
||||||
|
for m, p := range reqRetainer {
|
||||||
|
if time.Since(p.Time) > cfg.StateTimeout {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tmpState[m] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Wrap(json.NewEncoder(f).Encode(tmpState), "Unable to encode state file")
|
||||||
|
}
|
Loading…
Reference in a new issue