Add an import plugin system, with the Overpass POI query as its first plugin
A plugin is a directory under plugins/ with a plugin.json manifest
({"label": "...", "command": "./run.sh"}) plus a script that reads
--south/--west/--north/--east and prints a GeoJSON FeatureCollection
to stdout (lib/maptool/plugins.rb) — that's the entire interface a
plugin needs to know about this app. The sidebar's new import icon
(logged in only, GET /api/plugins + POST /api/plugins/:name/run) lists
whatever's found and runs one for the current map viewport, saving its
output as a new .geojson file the same way an upload would be.
The point: a scraper for a site whose ToS forbids that (Park4Night,
iOverlander, Freecampsites) doesn't need to live in this public repo
to work here — its own directory just goes under plugins/ locally and
is never git add'ed, no special-casing needed anywhere.
plugins/overpass-pois wraps the existing rake overpass:pois query as
the first, in-repo, reference example of the contract.
Also fills in four modules (auth.js, locate.js, measure.js, notes.js)
missing from layout.erb's cache-busting importmap alongside adding
plugins.js's own entry — a pre-existing gap that meant they could keep
serving a stale cached copy across a deploy despite the importmap
existing specifically to prevent that.
This commit is contained in:
1 parent
2b49eda7cb
commit
72ffd0ef18
17 files changed
+434
-14
No files matched your search
@@ -11,6 +11,7 @@ require_relative "lib/maptool/gpsd_client"
|
||||
require_relative "lib/maptool/livetrack_store"
|
||||
require_relative "lib/maptool/livetrack_client"
|
||||
require_relative "lib/maptool/kiwix"
|
||||
require_relative "lib/maptool/plugins"
|
||||
|
||||
module Maptool
|
||||
class App < Sinatra::Base
|
||||
@@ -63,6 +64,14 @@ module Maptool
|
||||
GPX_STORE = GpxStore.new(File.join(DATA_DIR, "gpx"))
|
||||
LIVETRACK_STORE = LiveTrackStore.new(File.join(DATA_DIR, "livetrack.json"))
|
||||
|
||||
# Not under DATA_DIR: unlike everything else there (uploads, PMTiles,
|
||||
# the kiwix ZIM), a plugin is code, not local data — plugins/overpass-pois
|
||||
# ships checked into this repo as the reference example of the
|
||||
# contract. A private/scraper plugin just lives in its own directory
|
||||
# here too, simply never `git add`ed, no special-casing needed.
|
||||
# Overridable so tests can point it at a throwaway fixture directory.
|
||||
PLUGINS_DIR = ENV.fetch("MAPTOOL_PLUGINS_DIR") { File.join(__dir__, "plugins") }
|
||||
|
||||
# Shared secret for the Garmin LiveTrack webhook (a Google Apps Script
|
||||
# watching Gmail for LiveTrack emails, see docs/deployment.adoc) — not a
|
||||
# Gitea-authenticated user, so it can't use the OAuth2 login flow above.
|
||||
@@ -329,6 +338,34 @@ module Maptool
|
||||
json ok: true
|
||||
end
|
||||
|
||||
# Lists whatever import plugins are on disk (see lib/maptool/plugins.rb
|
||||
# for the contract) — read-only, doesn't need require_login! the way
|
||||
# actually running one does below.
|
||||
get "/api/plugins" do
|
||||
json Plugins.list(PLUGINS_DIR)
|
||||
end
|
||||
|
||||
# Runs one plugin for the given bbox (the current map viewport, from
|
||||
# the frontend's "Run for this area" button) and saves its GeoJSON
|
||||
# output as a new file the same way an uploaded .geojson would be
|
||||
# saved — reusing GpxStore#save rather than #append_feature since this
|
||||
# writes a whole FeatureCollection at once, not one feature into an
|
||||
# existing one.
|
||||
post "/api/plugins/:name/run" do
|
||||
require_login!
|
||||
bbox = %w[south west north east].map { |key| params[key].to_s }
|
||||
halt 400, json(error: "south, west, north, east are required") if bbox.any?(&:empty?)
|
||||
|
||||
begin
|
||||
geojson = Plugins.run(PLUGINS_DIR, params[:name], bbox.map(&:to_f))
|
||||
rescue Plugins::RunError => e
|
||||
halt 502, json(error: e.message)
|
||||
end
|
||||
|
||||
path = GPX_STORE.save(folder: "", filename: "#{params[:name]}.geojson", io: StringIO.new(JSON.generate(geojson)))
|
||||
json path: path, tree: GPX_STORE.tree
|
||||
end
|
||||
|
||||
# ?download=geojson exports the file's GeoJSON as an attachment instead
|
||||
# of rendering it on the map — the only way to get a .gpx/.fit's
|
||||
# converted form out of the app, since GpxStore#geojson's cache is
|
||||
|
||||
Reference in new issue
Block a user