dual offline/online, map info

* Server provides map info with zoom, default center and paths to both downloadable pack and tile url template.
This commit is contained in:
zegkljan 2022-02-05 17:56:13 +01:00
parent caeb5c9198
commit 08a203e4a6
5 changed files with 69 additions and 28 deletions

16
main.go
View File

@ -8,6 +8,7 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"cernobor.cz/oko-server/models"
"cernobor.cz/oko-server/server" "cernobor.cz/oko-server/server"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -17,6 +18,9 @@ func main() {
tilepackFileArg := flag.String("tilepack", "", "File that will be sent to clients when they request a tile pack. Required.") tilepackFileArg := flag.String("tilepack", "", "File that will be sent to clients when they request a tile pack. Required.")
apkFileArg := flag.String("apk", "", "APK file with the client app. If not specified, no APK will be available (404).") apkFileArg := flag.String("apk", "", "APK file with the client app. If not specified, no APK will be available (404).")
reinitDBArg := flag.Bool("reinit-db", false, "Reinitializes the DB, which means all the tables will be recreated, deleting all data.") reinitDBArg := flag.Bool("reinit-db", false, "Reinitializes the DB, which means all the tables will be recreated, deleting all data.")
minZoomArg := flag.Int("min-zoom", 1, "Minimum zoom that will be sent to clients.")
defaultCenterLatArg := flag.Float64("default-center-lat", 0, "Latitude of the default map center.")
defaultCenterLngArg := flag.Float64("default-center-lng", 0, "Longitude of the default map center.")
flag.Parse() flag.Parse()
@ -26,7 +30,17 @@ func main() {
os.Exit(1) os.Exit(1)
} }
s := server.New(*dbFileArg, *tilepackFileArg, *apkFileArg, *reinitDBArg) s := server.New(server.ServerConfig{
DbPath: *dbFileArg,
TilepackPath: *tilepackFileArg,
ApkPath: *apkFileArg,
ReinitDB: *reinitDBArg,
MinZoom: *minZoomArg,
DefaultCenter: models.Coords{
Lat: *defaultCenterLatArg,
Lng: *defaultCenterLngArg,
},
})
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT) signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

View File

@ -33,6 +33,19 @@ type Feature struct {
// transport objects // transport objects
type Coords struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
}
type MapInfo struct {
MapPackPath string `json:"map_pack_path"`
MapPackSize int64 `json:"map_pack_size"`
TilePathTemplate string `json:"tile_path_template"`
MinZoom int `json:"min_zoom"`
DefaultCenter Coords `json:"default_center"`
}
type Update struct { type Update struct {
Create []Feature `json:"create"` Create []Feature `json:"create"`
CreatedPhotos map[FeatureID][]string `json:"created_photos"` CreatedPhotos map[FeatureID][]string `json:"created_photos"`
@ -48,9 +61,9 @@ type HandshakeChallenge struct {
} }
type HandshakeResponse struct { type HandshakeResponse struct {
ID UserID `json:"id"` ID UserID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
TilePackPath string `json:"tile_pack_path"` MapInfo MapInfo `json:"map_info"`
} }
type Data struct { type Data struct {

View File

@ -4,13 +4,13 @@ const (
URIPing = "/ping" URIPing = "/ping"
URIHardFail = "/hard-fail" URIHardFail = "/hard-fail"
URISoftFail = "/soft-fail" URISoftFail = "/soft-fail"
URITilepack = "/tilepack" URIMapPack = "/mappack"
URIHandshake = "/handshake" URIHandshake = "/handshake"
URIData = "/data" URIData = "/data"
URIDataPeople = "/data/people" URIDataPeople = "/data/people"
URIDataExtra = "/data/extra"
URIDataFeatures = "/data/features" URIDataFeatures = "/data/features"
URIDataFeaturesPhoto = "/data/features/:feature/photos/:photo" URIDataFeaturesPhoto = "/data/features/:feature/photos/:photo"
URITileserverRoot = "/tileserver" URITileserverRoot = "/tileserver"
URITileserver = URITileserverRoot + "/*x" URITileserver = URITileserverRoot + "/*x"
URITileTemplate = URITileserverRoot + "/map/tiles/{z}/{x}/{y}.pbf"
) )

View File

@ -86,7 +86,7 @@ func (s *Server) setupRouter() *gin.Engine {
}) })
// resources // resources
router.GET(URITilepack, s.handleGETTilepack) router.GET(URIMapPack, s.handleGETTilepack)
// API // API
router.POST(URIHandshake, s.handlePOSTHandshake) router.POST(URIHandshake, s.handlePOSTHandshake)
@ -129,9 +129,15 @@ func (s *Server) handlePOSTHandshake(gc *gin.Context) {
} }
gc.JSON(http.StatusOK, models.HandshakeResponse{ gc.JSON(http.StatusOK, models.HandshakeResponse{
ID: id, ID: id,
Name: hs.Name, Name: hs.Name,
TilePackPath: URITilepack, MapInfo: models.MapInfo{
MapPackPath: URIMapPack,
MapPackSize: s.mapPackSize,
TilePathTemplate: URITileTemplate,
MinZoom: s.config.MinZoom,
DefaultCenter: s.config.DefaultCenter,
},
}) })
} }

View File

@ -7,6 +7,7 @@ import (
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"os"
"time" "time"
_ "embed" _ "embed"
@ -29,23 +30,21 @@ type Server struct {
log *logrus.Logger log *logrus.Logger
ctx context.Context ctx context.Context
tileserverSvSet *mbsh.ServiceSet tileserverSvSet *mbsh.ServiceSet
mapPackSize int64
} }
type ServerConfig struct { type ServerConfig struct {
DbPath string DbPath string
TilepackPath string TilepackPath string
ApkPath string ApkPath string
ReinitDB bool ReinitDB bool
MinZoom int
DefaultCenter models.Coords
} }
func New(dbPath, tilepackPath, apkPath string, reinitDB bool) *Server { func New(config ServerConfig) *Server {
return &Server{ return &Server{
config: ServerConfig{ config: config,
DbPath: dbPath,
TilepackPath: tilepackPath,
ApkPath: apkPath,
ReinitDB: reinitDB,
},
} }
} }
@ -63,6 +62,7 @@ func (s *Server) Run(ctx context.Context) {
Addr: fmt.Sprintf(":%d", 8080), Addr: fmt.Sprintf(":%d", 8080),
Handler: router, Handler: router,
} }
s.log.Infof("Running on %s", server.Addr)
go func() { go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
s.log.Infof("listen: %s\n", err) s.log.Infof("listen: %s\n", err)
@ -99,6 +99,7 @@ func (s *Server) setupDB() {
s.dbpool = dbpool s.dbpool = dbpool
if s.config.ReinitDB { if s.config.ReinitDB {
s.log.Info("Reinitializing DB.")
conn := s.getDbConn() conn := s.getDbConn()
defer s.dbpool.Put(conn) defer s.dbpool.Put(conn)
@ -124,24 +125,30 @@ func (s *Server) setupTiles() {
if err != nil { if err != nil {
s.log.WithError(err).Fatal("Failed to create tileserver service set.") s.log.WithError(err).Fatal("Failed to create tileserver service set.")
} }
err = svs.AddTileset(s.config.TilepackPath, "main") err = svs.AddTileset(s.config.TilepackPath, "map")
if err != nil { if err != nil {
s.log.WithError(err).Fatal("Failed to register main tileset.") s.log.WithError(err).Fatal("Failed to register tileset.")
} }
s.tileserverSvSet = svs s.tileserverSvSet = svs
info, err := os.Stat(s.config.TilepackPath)
if err != nil {
s.log.WithError(err).Fatal("Failed to get info about tile pack file.")
}
s.mapPackSize = info.Size()
} }
func (s *Server) handshake(hc models.HandshakeChallenge) (models.UserID, error) { func (s *Server) handshake(hc models.HandshakeChallenge) (models.UserID, error) {
conn := s.getDbConn() conn := s.getDbConn()
defer s.dbpool.Put(conn) defer s.dbpool.Put(conn)
userID, err := func() (uid int, err error) { userID, err := func() (uid int64, err error) {
defer sqlitex.Save(conn)(&err) defer sqlitex.Save(conn)(&err)
var id *int var id *int64
if hc.Exists { if hc.Exists {
err = sqlitex.Exec(conn, "select id from users where name = ?", func(stmt *sqlite.Stmt) error { err = sqlitex.Exec(conn, "select id from users where name = ?", func(stmt *sqlite.Stmt) error {
id = ptrInt(stmt.ColumnInt(0)) id = ptrInt64(stmt.ColumnInt64(0))
return nil return nil
}, hc.Name) }, hc.Name)
if sqlite.ErrCode(err) != sqlite.SQLITE_OK { if sqlite.ErrCode(err) != sqlite.SQLITE_OK {
@ -154,8 +161,8 @@ func (s *Server) handshake(hc models.HandshakeChallenge) (models.UserID, error)
return 0, errs.ErrAttemptedSystemUser return 0, errs.ErrAttemptedSystemUser
} }
} else { } else {
err = sqlitex.Exec(conn, "insert into users(name) values(?) returning id", func(stmt *sqlite.Stmt) error { err = sqlitex.Exec(conn, "insert into users(name) values(?)", func(stmt *sqlite.Stmt) error {
id = ptrInt(stmt.ColumnInt(0)) id = ptrInt64(stmt.ColumnInt64(0))
return nil return nil
}, hc.Name) }, hc.Name)
if sqlite.ErrCode(err) == sqlite.SQLITE_CONSTRAINT_UNIQUE { if sqlite.ErrCode(err) == sqlite.SQLITE_CONSTRAINT_UNIQUE {
@ -164,6 +171,7 @@ func (s *Server) handshake(hc models.HandshakeChallenge) (models.UserID, error)
if sqlite.ErrCode(err) != sqlite.SQLITE_OK { if sqlite.ErrCode(err) != sqlite.SQLITE_OK {
return 0, err return 0, err
} }
id = ptrInt64(conn.LastInsertRowID())
} }
return *id, nil return *id, nil
}() }()