Skip to content

Commit

Permalink
osm toilet first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
baditaflorin committed Jun 18, 2024
1 parent 38efab1 commit a331b8f
Show file tree
Hide file tree
Showing 22 changed files with 1,716 additions and 5 deletions.
36 changes: 31 additions & 5 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions osm-toilet-map/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// config/config.go
package config

import (
"fmt"
"log"
"os"

"github.com/joho/godotenv"
)

type Config struct {
ClientID string
ClientSecret string
RedirectURI string
AuthURL string
TokenURL string
Query string
ChangesetComment string
CreatedBy string
Port string
}

func LoadConfig() *Config {
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}

port := os.Getenv("PORT")
if port == "" {
port = "8080"
}

redirectURIBase := os.Getenv("REDIRECT_URI_BASE")
redirectURICallback := os.Getenv("REDIRECT_URI_CALLBACK")
redirectURI := fmt.Sprintf("%s:%s%s", redirectURIBase, port, redirectURICallback)

return &Config{
ClientID: os.Getenv("CLIENT_ID"),
ClientSecret: os.Getenv("CLIENT_SECRET"),
RedirectURI: redirectURI,
AuthURL: os.Getenv("AUTH_URL"),
TokenURL: os.Getenv("TOKEN_URL"),
Query: os.Getenv("QUERY"),
ChangesetComment: os.Getenv("CHANGESET_COMMENT"),
CreatedBy: os.Getenv("CREATED_BY"),
Port: port,
}
}
7 changes: 7 additions & 0 deletions osm-toilet-map/constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// constants/constants.go
package constants

const (
ChangesetComment = "Adding a new Public Toilet node"
CreatedBy = "ToiletMap.com Editor"
)
12 changes: 12 additions & 0 deletions osm-toilet-map/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module toilet_map

go 1.20

require (
github.com/gorilla/mux v1.8.1
github.com/gorilla/sessions v1.3.0
github.com/joho/godotenv v1.5.1
golang.org/x/oauth2 v0.21.0
)

require github.com/gorilla/securecookie v1.1.2 // indirect
57 changes: 57 additions & 0 deletions osm-toilet-map/handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package handlers

import (
"encoding/json"
"net/http"
"toilet_map/config"
"toilet_map/oauth"
"toilet_map/osm"

"golang.org/x/oauth2"
)

func HandleData(cfg *config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bbox := r.URL.Query().Get("bbox")
if bbox == "" {
http.Error(w, "Bounding box is required", http.StatusBadRequest)
return
}
osm.FetchNodes(cfg, bbox)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(osm.Nodes.Elements)
}
}

func HandleMap(cfg *config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/map.html")
}
}

func HandleLogin(cfg *config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
url := oauth.Oauth2Config.AuthCodeURL("state", oauth2.AccessTypeOffline)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
}

func HandleCallback(cfg *config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
token, err := oauth.Oauth2Config.Exchange(oauth2.NoContext, code)
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}

session, _ := oauth.Store.Get(r, "session-name")
session.Values["oauth-token"] = token
if err := session.Save(r, w); err != nil {
http.Error(w, "Failed to save session: "+err.Error(), http.StatusInternalServerError)
return
}

http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
35 changes: 35 additions & 0 deletions osm-toilet-map/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"fmt"
"log"
"net/http"
"toilet_map/config"
"toilet_map/handlers"
"toilet_map/oauth"

"github.com/gorilla/mux"
)

func main() {
cfg := config.LoadConfig()

oauth.Init(cfg)

router := mux.NewRouter()
router.HandleFunc("/", handlers.HandleMap(cfg)).Methods("GET")
router.HandleFunc("/data", handlers.HandleData(cfg)).Methods("GET")
router.HandleFunc("/login", handlers.HandleLogin(cfg)).Methods("GET")
router.HandleFunc("/callback", handlers.HandleCallback(cfg)).Methods("GET")

// Serve static files
fs := http.FileServer(http.Dir("./static"))
router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))

http.Handle("/", router)

fmt.Printf("Server starting on :%s...\n", cfg.Port)
if err := http.ListenAndServe(":"+cfg.Port, nil); err != nil {
log.Fatalf("Error starting server: %s", err)
}
}
125 changes: 125 additions & 0 deletions osm-toilet-map/oauth/oauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// oauth/oauth.go
package oauth

import (
"encoding/gob"
"fmt"
"log"
"net/http"
"toilet_map/config"
"toilet_map/utils"

"github.com/gorilla/sessions"
"golang.org/x/oauth2"
)

var (
Oauth2Config *oauth2.Config
Store = sessions.NewCookieStore([]byte("super-secret-key"))
)

func Init(cfg *config.Config) {
// Register oauth2.Token with gob
gob.Register(&oauth2.Token{})

Oauth2Config = &oauth2.Config{
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret,
RedirectURL: cfg.RedirectURI,
Scopes: []string{
"read_prefs",
"write_prefs",
"write_api",
},
Endpoint: oauth2.Endpoint{
AuthURL: cfg.AuthURL,
TokenURL: cfg.TokenURL,
},
}
}

func createChangesetRequest(cfg *config.Config, token *oauth2.Token) (*http.Request, error) {
xmlData := fmt.Sprintf(`
<osm>
<changeset>
<tag k="created_by" v="%s"/>
<tag k="comment" v="%s"/>
</changeset>
</osm>`, cfg.CreatedBy, cfg.ChangesetComment)

return utils.CreateRequest("PUT", "https://api.openstreetmap.org/api/0.6/changeset/create", "text/xml", []byte(xmlData))
}

func createNodeRequest(changesetID int, lat, lon float64, tags map[string]string) (*http.Request, error) {
var tagsXML string
for key, value := range tags {
tagsXML += fmt.Sprintf(`<tag k="%s" v="%s"/>`, key, value)
}

xmlData := fmt.Sprintf(`
<osm>
<node changeset="%d" lat="%f" lon="%f">
%s
</node>
</osm>`, changesetID, lat, lon, tagsXML)

return utils.CreateRequest("PUT", "https://api.openstreetmap.org/api/0.6/node/create", "text/xml", []byte(xmlData))
}

func CreateChangeset(cfg *config.Config, token *oauth2.Token) (int, error) {
client := Oauth2Config.Client(oauth2.NoContext, token)
req, err := createChangesetRequest(cfg, token)
if err != nil {
return 0, err
}
req.Header.Set("Authorization", "Bearer "+token.AccessToken)

body, err := utils.DoRequest(client, req)
if err != nil {
return 0, err
}

var changesetID int
fmt.Sscanf(string(body), "%d", &changesetID)

return changesetID, nil
}

func CloseChangeset(token *oauth2.Token, changesetID int) error {
client := Oauth2Config.Client(oauth2.NoContext, token)
endpointURL := fmt.Sprintf("https://api.openstreetmap.org/api/0.6/changeset/%d/close", changesetID)

req, err := utils.CreateRequest("PUT", endpointURL, "text/xml", nil)
if err != nil {
return fmt.Errorf("failed to create request: %v", err)
}
req.Header.Set("Authorization", "Bearer "+token.AccessToken)

_, err = utils.DoRequest(client, req)
return err
}

func CreateMapNode(cfg *config.Config, token *oauth2.Token, lat, lon float64, tags map[string]string) {
changesetID, err := CreateChangeset(cfg, token)
if err != nil {
log.Fatalf("Failed to create changeset: %v", err)
}

client := Oauth2Config.Client(oauth2.NoContext, token)
req, err := createNodeRequest(changesetID, lat, lon, tags)
if err != nil {
log.Fatalf("Failed to create request: %v", err)
}
req.Header.Set("Authorization", "Bearer "+token.AccessToken)

_, err = utils.DoRequest(client, req)
if err != nil {
log.Fatalf("Failed to create node: %v", err)
}

fmt.Printf("Node created successfully\n")

if err := CloseChangeset(token, changesetID); err != nil {
log.Fatalf("Failed to close changeset: %v", err)
}
}
Loading

0 comments on commit a331b8f

Please sign in to comment.