mirror of https://github.com/jlelse/GoBlog
parent
bf3074a54d
commit
783afc1c3e
|
@ -26,6 +26,7 @@ type config struct {
|
|||
PrivateMode *configPrivateMode `mapstructure:"privateMode"`
|
||||
EasterEgg *configEasterEgg `mapstructure:"easterEgg"`
|
||||
Debug bool `mapstructure:"debug"`
|
||||
MapTiles *configMapTiles `mapstructure:"mapTiles"`
|
||||
}
|
||||
|
||||
type configServer struct {
|
||||
|
@ -279,6 +280,13 @@ type configWebmention struct {
|
|||
DisableReceiving bool `mapstructure:"disableReceiving"`
|
||||
}
|
||||
|
||||
type configMapTiles struct {
|
||||
Source string `mapstructure:"source"`
|
||||
Attribution string `mapstructure:"attribution"`
|
||||
MinZoom int `mapstructure:"minZoom"`
|
||||
MaxZoom int `mapstructure:"maxZoom"`
|
||||
}
|
||||
|
||||
func (a *goBlog) initConfig(file string) error {
|
||||
log.Println("Initialize configuration...")
|
||||
if file != "" {
|
||||
|
|
|
@ -131,6 +131,13 @@ pathRedirects:
|
|||
to: "/$1$2"
|
||||
type: 301 # custom redirect type
|
||||
|
||||
# Map tiles
|
||||
mapTiles:
|
||||
source: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" # (Optional) URL to use for map tiles
|
||||
attribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors" # (Optional) Attribution for map tiles
|
||||
minZoom: 0 # (Optional) Minimum zoom level
|
||||
maxZoom: 20 # (Optional) Maximum zoom level
|
||||
|
||||
# Blogs
|
||||
defaultBlog: en # Default blog (needed because you can define multiple blogs)
|
||||
blogs:
|
||||
|
|
43
geo.go
43
geo.go
|
@ -5,8 +5,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
|
@ -76,44 +74,3 @@ func geoOSMLink(g *gogeouri.Geo) string {
|
|||
|
||||
//go:embed leaflet/*
|
||||
var leafletFiles embed.FS
|
||||
|
||||
func (a *goBlog) proxyTiles(basePath string) http.HandlerFunc {
|
||||
osmUrl, _ := url.Parse("https://tile.openstreetmap.org/")
|
||||
tileProxy := http.StripPrefix(basePath, httputil.NewSingleHostReverseProxy(osmUrl))
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
targetUrl := *osmUrl
|
||||
targetUrl.Path = r.URL.Path
|
||||
proxyRequest, _ := http.NewRequest(http.MethodGet, targetUrl.String(), nil)
|
||||
// Copy request headers
|
||||
for _, k := range []string{
|
||||
"Accept-Encoding",
|
||||
"Accept-Language",
|
||||
"Accept",
|
||||
"Cache-Control",
|
||||
"If-Modified-Since",
|
||||
"If-None-Match",
|
||||
"User-Agent",
|
||||
} {
|
||||
proxyRequest.Header.Set(k, r.Header.Get(k))
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
tileProxy.ServeHTTP(rec, proxyRequest)
|
||||
res := rec.Result()
|
||||
// Copy result headers
|
||||
for _, k := range []string{
|
||||
"Accept-Ranges",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Age",
|
||||
"Cache-Control",
|
||||
"Content-Length",
|
||||
"Content-Type",
|
||||
"Etag",
|
||||
"Expires",
|
||||
} {
|
||||
w.Header().Set(k, res.Header.Get(k))
|
||||
}
|
||||
w.WriteHeader(res.StatusCode)
|
||||
_, _ = io.Copy(w, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) {
|
|||
Data: map[string]interface{}{
|
||||
"locations": locationsJson,
|
||||
"tracks": tracksJson,
|
||||
"attribution": a.getMapAttribution(),
|
||||
"minzoom": a.getMinZoom(),
|
||||
"maxzoom": a.getMaxZoom(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func (a *goBlog) proxyTiles(basePath string) http.HandlerFunc {
|
||||
tileSource := "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
if c := a.cfg.MapTiles; c != nil && c.Source != "" {
|
||||
tileSource = c.Source
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Create a new request to proxy to the tile server
|
||||
targetUrl := tileSource
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{s}", chi.URLParam(r, "s"))
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{z}", chi.URLParam(r, "z"))
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{x}", chi.URLParam(r, "x"))
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{y}", chi.URLParam(r, "y"))
|
||||
proxyRequest, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, targetUrl, nil)
|
||||
proxyRequest.Header.Set(userAgent, appUserAgent)
|
||||
// Copy request headers
|
||||
for _, k := range []string{
|
||||
"Accept-Encoding",
|
||||
"Accept-Language",
|
||||
"Accept",
|
||||
"Cache-Control",
|
||||
"If-Modified-Since",
|
||||
"If-None-Match",
|
||||
"User-Agent",
|
||||
} {
|
||||
proxyRequest.Header.Set(k, r.Header.Get(k))
|
||||
}
|
||||
// Do the request
|
||||
res, err := a.httpClient.Do(proxyRequest)
|
||||
if err != nil {
|
||||
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Copy result headers
|
||||
for _, k := range []string{
|
||||
"Accept-Ranges",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Age",
|
||||
"Cache-Control",
|
||||
"Content-Length",
|
||||
"Content-Type",
|
||||
"Etag",
|
||||
"Expires",
|
||||
} {
|
||||
w.Header().Set(k, res.Header.Get(k))
|
||||
}
|
||||
// Copy result
|
||||
w.WriteHeader(res.StatusCode)
|
||||
_, _ = io.Copy(w, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *goBlog) getMinZoom() int {
|
||||
if c := a.cfg.MapTiles; c != nil {
|
||||
return c.MinZoom
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (a *goBlog) getMaxZoom() int {
|
||||
if c := a.cfg.MapTiles; c != nil && c.MaxZoom > 0 {
|
||||
return c.MaxZoom
|
||||
}
|
||||
return 20
|
||||
}
|
||||
|
||||
func (a *goBlog) getMapAttribution() string {
|
||||
if c := a.cfg.MapTiles; c != nil {
|
||||
return c.Attribution
|
||||
}
|
||||
return `© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors`
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_proxyTiles(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{},
|
||||
}
|
||||
|
||||
hc := &fakeHttpClient{
|
||||
handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("Hello, World!"))
|
||||
}),
|
||||
}
|
||||
app.httpClient = hc
|
||||
|
||||
// Default tile source
|
||||
|
||||
m := chi.NewMux()
|
||||
m.Get("/x/tiles/{s}/{z}/{x}/{y}.png", app.proxyTiles("/x/tiles"))
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://example.org/x/tiles/c/8/134/84.png", nil)
|
||||
require.NoError(t, err)
|
||||
resp, err := doHandlerRequest(req, m)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
assert.Equal(t, "https://c.tile.openstreetmap.org/8/134/84.png", hc.req.URL.String())
|
||||
|
||||
// Custom tile source
|
||||
|
||||
app.cfg.MapTiles = &configMapTiles{
|
||||
Source: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
|
||||
}
|
||||
|
||||
m = chi.NewMux()
|
||||
m.Get("/x/tiles/{s}/{z}/{x}/{y}.png", app.proxyTiles("/x/tiles"))
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://example.org/x/tiles/c/8/134/84.png", nil)
|
||||
require.NoError(t, err)
|
||||
resp, err = doHandlerRequest(req, m)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
assert.Equal(t, "https://c.tile.opentopomap.org/8/134/84.png", hc.req.URL.String())
|
||||
}
|
||||
|
||||
func Test_getMinZoom(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{},
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, app.getMinZoom())
|
||||
|
||||
app.cfg.MapTiles = &configMapTiles{
|
||||
MinZoom: 1,
|
||||
}
|
||||
|
||||
assert.Equal(t, 1, app.getMinZoom())
|
||||
}
|
||||
|
||||
func Test_getMaxZoom(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{},
|
||||
}
|
||||
|
||||
assert.Equal(t, 20, app.getMaxZoom())
|
||||
|
||||
app.cfg.MapTiles = &configMapTiles{
|
||||
MaxZoom: 10,
|
||||
}
|
||||
|
||||
assert.Equal(t, 10, app.getMaxZoom())
|
||||
}
|
||||
|
||||
func Test_getMapAttribution(t *testing.T) {
|
||||
app := &goBlog{
|
||||
cfg: &config{},
|
||||
}
|
||||
|
||||
assert.Equal(t, `© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors`, app.getMapAttribution())
|
||||
|
||||
app.cfg.MapTiles = &configMapTiles{
|
||||
Attribution: "attribution",
|
||||
}
|
||||
|
||||
assert.Equal(t, "attribution", app.getMapAttribution())
|
||||
}
|
|
@ -26,6 +26,8 @@ type trackResult struct {
|
|||
Kilometers string
|
||||
Hours string
|
||||
Name string
|
||||
MapAttribution string
|
||||
MinZoom, MaxZoom int
|
||||
}
|
||||
|
||||
func (a *goBlog) getTrack(p *post) (result *trackResult, err error) {
|
||||
|
@ -62,6 +64,9 @@ func (a *goBlog) getTrack(p *post) (result *trackResult, err error) {
|
|||
Points: parseResult.points,
|
||||
PointsJSON: string(pointsJSON),
|
||||
Name: parseResult.gpxData.Name,
|
||||
MapAttribution: a.getMapAttribution(),
|
||||
MinZoom: a.getMinZoom(),
|
||||
MaxZoom: a.getMaxZoom(),
|
||||
}
|
||||
|
||||
if parseResult.md != nil {
|
||||
|
|
|
@ -99,7 +99,7 @@ func (a *goBlog) mediaFilesRouter(r chi.Router) {
|
|||
// Various other routes
|
||||
func (a *goBlog) xRouter(r chi.Router) {
|
||||
r.Use(a.privateModeHandler)
|
||||
r.Get("/tiles/{z}/{x}/{y}.png", a.proxyTiles("/x/tiles"))
|
||||
r.Get("/tiles/{s}/{z}/{x}/{y}.png", a.proxyTiles("/x/tiles"))
|
||||
r.With(cacheLoggedIn, a.cacheMiddleware).HandleFunc("/leaflet/*", a.serveFs(leafletFiles, "/x/"))
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
let locations = (mapEl.dataset.locations == "") ? [] : JSON.parse(mapEl.dataset.locations)
|
||||
let tracks = (mapEl.dataset.tracks == "") ? [] : JSON.parse(mapEl.dataset.tracks)
|
||||
|
||||
let map = L.map('map')
|
||||
let map = L.map('map', {
|
||||
minZoom: mapEl.dataset.minzoom,
|
||||
maxZoom: mapEl.dataset.maxzoom
|
||||
})
|
||||
|
||||
L.tileLayer("/x/tiles/{z}/{x}/{y}.png", {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
L.tileLayer("/x/tiles/{s}/{z}/{x}/{y}.png", {
|
||||
attribution: mapEl.dataset.attribution,
|
||||
}).addTo(map)
|
||||
|
||||
let features = []
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
let paths = (mapEl.dataset.paths == "") ? [] : JSON.parse(mapEl.dataset.paths)
|
||||
let points = (mapEl.dataset.points == "") ? [] : JSON.parse(mapEl.dataset.points)
|
||||
|
||||
let map = L.map('map')
|
||||
let map = L.map('map', {
|
||||
minZoom: mapEl.dataset.minzoom,
|
||||
maxZoom: mapEl.dataset.maxzoom
|
||||
})
|
||||
|
||||
L.tileLayer("/x/tiles/{z}/{x}/{y}.png", {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
L.tileLayer("/x/tiles/{s}/{z}/{x}/{y}.png", {
|
||||
attribution: mapEl.dataset.attribution,
|
||||
}).addTo(map)
|
||||
|
||||
let features = []
|
||||
|
|
|
@ -11,7 +11,13 @@
|
|||
{{ if .Data.nolocations }}
|
||||
<p>{{ string .Blog.Lang "nolocations" }}</p>
|
||||
{{ else }}
|
||||
<div class="p" id="map" data-locations="{{ .Data.locations }}" data-tracks="{{ .Data.tracks }}"></div>
|
||||
<div class="p" id="map"
|
||||
data-locations="{{ .Data.locations }}"
|
||||
data-tracks="{{ .Data.tracks }}"
|
||||
data-minzoom={{ .Data.minzoom }}
|
||||
data-maxzoom={{ .Data.maxzoom }}
|
||||
data-attribution="{{ .Data.attribution }}"
|
||||
></div>
|
||||
<script defer src="{{ asset "js/geomap.js" }}"></script>
|
||||
{{ end }}
|
||||
</main>
|
||||
|
|
|
@ -5,7 +5,13 @@
|
|||
{{ if $track.HasPoints }}
|
||||
{{ $lang := .Blog.Lang }}
|
||||
<p>{{ with $track.Name }}<b>{{ . }}</b> {{ end }}{{ with $track.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $track.Hours }}⌛ {{ . }}{{ end }}</p>
|
||||
<div class="p" id="map" data-paths="{{ $track.PathsJSON }}" data-points="{{ $track.PointsJSON }}"></div>
|
||||
<div class="p" id="map"
|
||||
data-paths="{{ $track.PathsJSON }}"
|
||||
data-points="{{ $track.PointsJSON }}"
|
||||
data-minzoom={{ $track.MinZoom }}
|
||||
data-maxzoom={{ $track.MaxZoom }}
|
||||
data-attribution="{{ $track.MapAttribution }}"
|
||||
></div>
|
||||
<script defer src="{{ asset "js/geotrack.js" }}"></script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
Loading…
Reference in New Issue