diff --git a/main.go b/main.go index dbf4a8b..df97203 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "os/signal" "syscall" + "cernobor.cz/oko-server/models" "cernobor.cz/oko-server/server" "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.") 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.") + 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() @@ -26,7 +30,17 @@ func main() { 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) signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT) diff --git a/models/models.go b/models/models.go index 3dbe75c..0f8f9a5 100644 --- a/models/models.go +++ b/models/models.go @@ -33,6 +33,19 @@ type Feature struct { // 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 { Create []Feature `json:"create"` CreatedPhotos map[FeatureID][]string `json:"created_photos"` @@ -48,9 +61,9 @@ type HandshakeChallenge struct { } type HandshakeResponse struct { - ID UserID `json:"id"` - Name string `json:"name"` - TilePackPath string `json:"tile_pack_path"` + ID UserID `json:"id"` + Name string `json:"name"` + MapInfo MapInfo `json:"map_info"` } type Data struct { diff --git a/server/uris.go b/server/constants.go similarity index 80% rename from server/uris.go rename to server/constants.go index 2279a72..84b572f 100644 --- a/server/uris.go +++ b/server/constants.go @@ -4,13 +4,13 @@ const ( URIPing = "/ping" URIHardFail = "/hard-fail" URISoftFail = "/soft-fail" - URITilepack = "/tilepack" + URIMapPack = "/mappack" URIHandshake = "/handshake" URIData = "/data" URIDataPeople = "/data/people" - URIDataExtra = "/data/extra" URIDataFeatures = "/data/features" URIDataFeaturesPhoto = "/data/features/:feature/photos/:photo" URITileserverRoot = "/tileserver" URITileserver = URITileserverRoot + "/*x" + URITileTemplate = URITileserverRoot + "/map/tiles/{z}/{x}/{y}.pbf" ) diff --git a/server/handling.go b/server/handling.go index 17ac4b9..2433070 100644 --- a/server/handling.go +++ b/server/handling.go @@ -86,7 +86,7 @@ func (s *Server) setupRouter() *gin.Engine { }) // resources - router.GET(URITilepack, s.handleGETTilepack) + router.GET(URIMapPack, s.handleGETTilepack) // API router.POST(URIHandshake, s.handlePOSTHandshake) @@ -129,9 +129,15 @@ func (s *Server) handlePOSTHandshake(gc *gin.Context) { } gc.JSON(http.StatusOK, models.HandshakeResponse{ - ID: id, - Name: hs.Name, - TilePackPath: URITilepack, + ID: id, + Name: hs.Name, + MapInfo: models.MapInfo{ + MapPackPath: URIMapPack, + MapPackSize: s.mapPackSize, + TilePathTemplate: URITileTemplate, + MinZoom: s.config.MinZoom, + DefaultCenter: s.config.DefaultCenter, + }, }) } diff --git a/server/server.go b/server/server.go index 302c779..cea540b 100644 --- a/server/server.go +++ b/server/server.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "net/url" + "os" "time" _ "embed" @@ -29,23 +30,21 @@ type Server struct { log *logrus.Logger ctx context.Context tileserverSvSet *mbsh.ServiceSet + mapPackSize int64 } type ServerConfig struct { - DbPath string - TilepackPath string - ApkPath string - ReinitDB bool + DbPath string + TilepackPath string + ApkPath string + ReinitDB bool + MinZoom int + DefaultCenter models.Coords } -func New(dbPath, tilepackPath, apkPath string, reinitDB bool) *Server { +func New(config ServerConfig) *Server { return &Server{ - config: ServerConfig{ - DbPath: dbPath, - TilepackPath: tilepackPath, - ApkPath: apkPath, - ReinitDB: reinitDB, - }, + config: config, } } @@ -63,6 +62,7 @@ func (s *Server) Run(ctx context.Context) { Addr: fmt.Sprintf(":%d", 8080), Handler: router, } + s.log.Infof("Running on %s", server.Addr) go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { s.log.Infof("listen: %s\n", err) @@ -99,6 +99,7 @@ func (s *Server) setupDB() { s.dbpool = dbpool if s.config.ReinitDB { + s.log.Info("Reinitializing DB.") conn := s.getDbConn() defer s.dbpool.Put(conn) @@ -124,24 +125,30 @@ func (s *Server) setupTiles() { if err != nil { 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 { - s.log.WithError(err).Fatal("Failed to register main tileset.") + s.log.WithError(err).Fatal("Failed to register tileset.") } 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) { conn := s.getDbConn() defer s.dbpool.Put(conn) - userID, err := func() (uid int, err error) { + userID, err := func() (uid int64, err error) { defer sqlitex.Save(conn)(&err) - var id *int + var id *int64 if hc.Exists { 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 }, hc.Name) 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 } } else { - err = sqlitex.Exec(conn, "insert into users(name) values(?) returning id", func(stmt *sqlite.Stmt) error { - id = ptrInt(stmt.ColumnInt(0)) + err = sqlitex.Exec(conn, "insert into users(name) values(?)", func(stmt *sqlite.Stmt) error { + id = ptrInt64(stmt.ColumnInt64(0)) return nil }, hc.Name) 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 { return 0, err } + id = ptrInt64(conn.LastInsertRowID()) } return *id, nil }()