mirror of
https://github.com/stevenhowes/osgrid-server.git
synced 2026-05-26 15:53:42 +01:00
159 lines
4.0 KiB
Go
159 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
middleware "github.com/paulcager/go-http-middleware"
|
|
"github.com/paulcager/osgridref"
|
|
flag "github.com/spf13/pflag"
|
|
)
|
|
|
|
const (
|
|
apiVersion = "v5"
|
|
)
|
|
|
|
var (
|
|
staticCache time.Duration
|
|
listenPort string
|
|
)
|
|
|
|
func main() {
|
|
flag.StringVar(&listenPort, "port", ":9090", "Port to listen on")
|
|
flag.DurationVar(&staticCache, "static-cache-max-age", 1*time.Hour, "If not zero, the max-age property to set in Cache-Control for responses")
|
|
flag.Parse()
|
|
|
|
server := makeHTTPServer(listenPort)
|
|
log.Fatal(server.ListenAndServe())
|
|
}
|
|
|
|
type Reply struct {
|
|
OSGridRef string `json:"osGridRef"`
|
|
Easting int `json:"easting"`
|
|
Northing int `json:"northing"`
|
|
Lat float64 `json:"lat"`
|
|
Lon float64 `json:"lon"`
|
|
UsageCount int `json:"usageCount"`
|
|
}
|
|
|
|
func makeHTTPServer(listenPort string) *http.Server {
|
|
http.Handle("/"+apiVersion+"/gridref/", middleware.MakeCachingHandler(1*time.Hour, http.HandlerFunc(
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
gridRefStr := r.URL.Path[len("/"+apiVersion+"/gridref/"):]
|
|
gridRef, err := osgridref.ParseOsGridRef(gridRefStr)
|
|
if err != nil {
|
|
handleError(w, r, gridRefStr, err)
|
|
return
|
|
}
|
|
|
|
lat, lon := gridRef.ToLatLon()
|
|
handle(w, r, gridRef, lat, lon)
|
|
})))
|
|
|
|
http.Handle("/"+apiVersion+"/latlon/", middleware.MakeCachingHandler(1*time.Hour, http.HandlerFunc(
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
latLonStr := r.URL.Path[len("/"+apiVersion+"/latlon/"):]
|
|
latLon, err := osgridref.ParseLatLon(latLonStr, 0, osgridref.WGS84)
|
|
if err != nil {
|
|
handleError(w, r, latLonStr, err)
|
|
return
|
|
}
|
|
|
|
gridRef := latLon.ToOsGridRef()
|
|
handle(w, r, gridRef, latLon.Lat, latLon.Lon)
|
|
})))
|
|
|
|
if !strings.Contains(listenPort, ":") {
|
|
listenPort = ":" + listenPort
|
|
}
|
|
|
|
log.Println("Starting HTTP server on " + listenPort)
|
|
s := &http.Server{
|
|
ReadHeaderTimeout: 20 * time.Second,
|
|
WriteTimeout: 2 * time.Minute,
|
|
IdleTimeout: 10 * time.Minute,
|
|
Handler: middleware.MakeLoggingHandler(http.DefaultServeMux),
|
|
Addr: listenPort,
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func md5hash(text string) string {
|
|
hash := md5.New()
|
|
hash.Write([]byte(text))
|
|
return fmt.Sprintf("%x", hash.Sum(nil))
|
|
}
|
|
|
|
func handleError(w http.ResponseWriter, _ *http.Request, str string, _ error) {
|
|
w.Header().Add("Access-Control-Allow-Origin", "*")
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
fmt.Fprintf(w, "Invalid request: %q\n", str)
|
|
}
|
|
|
|
func handle(w http.ResponseWriter, r *http.Request, ref osgridref.OsGridRef, lat float64, lon float64) {
|
|
authorization := r.Header.Get("Authorization")
|
|
|
|
// extract bearer token from authorization header
|
|
if authorization != "" {
|
|
authorization = strings.TrimPrefix(strings.ToLower(authorization), "bearer ")
|
|
} else {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
usageCount := 0
|
|
|
|
// if file exists, increment usage count
|
|
if authorization != "" {
|
|
authorization = md5hash(authorization)
|
|
filePath := "keys/" + authorization
|
|
if _, err := os.Stat(filePath); err == nil {
|
|
data, err := os.ReadFile(filePath)
|
|
if err == nil {
|
|
usageCount, err = strconv.Atoi(strings.TrimSpace(string(data)))
|
|
if err == nil {
|
|
usageCount++
|
|
err = os.WriteFile(filePath, []byte(strconv.Itoa(usageCount)), 0644)
|
|
if err != nil {
|
|
log.Printf("Failed to write usage count: %s", err)
|
|
}
|
|
} else {
|
|
log.Printf("Failed to parse usage count: %s", err)
|
|
}
|
|
} else {
|
|
log.Printf("Failed to read file: %s", err)
|
|
}
|
|
} else {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
return
|
|
}
|
|
}
|
|
|
|
reply := Reply{
|
|
OSGridRef: ref.StringNCompact(8),
|
|
Easting: ref.Easting,
|
|
Northing: ref.Northing,
|
|
Lat: lat,
|
|
Lon: lon,
|
|
UsageCount: usageCount,
|
|
}
|
|
|
|
w.Header().Add("Content-Type", "application/json")
|
|
w.Header().Add("Access-Control-Allow-Origin", "*")
|
|
enc := json.NewEncoder(w)
|
|
enc.SetEscapeHTML(false)
|
|
err := enc.Encode(reply)
|
|
if err != nil {
|
|
log.Printf("Failed to write response: %s", err)
|
|
w.WriteHeader(http.StatusBadGateway)
|
|
}
|
|
}
|