Add Garmin LiveTrack session tracking
CI / check (push) Successful in 6m49s
CI / deploy (push) Successful in 23s

A Gmail-watching Apps Script (documented in docs/deployment.adoc) posts
the session ID from each LiveTrack email to a new webhook, which stores
it in data/livetrack.json; a new sidebar panel lists what's been
received.
This commit is contained in:
randy committed 2026-09-06 21:05:35 +02:00
1 parent 60170efda2
commit f9ddf860ea
11 files changed
+279

No files matched your search

+25
View File
@@ -5,6 +5,7 @@ require "securerandom"
require_relative "lib/maptool/gpx"
require_relative "lib/maptool/gpx_store"
require_relative "lib/maptool/gpsd_client"
require_relative "lib/maptool/livetrack_store"
module Maptool
class App < Sinatra::Base
@@ -21,6 +22,14 @@ module Maptool
OFFLINE_PMTILES_PATH = File.join(__dir__, "data", "offline.pmtiles")
GPX_STORE = GpxStore.new(File.join(__dir__, "data", "gpx"))
LIVETRACK_STORE = LiveTrackStore.new(File.join(__dir__, "data", "livetrack.json"))
# 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.
# Blank means the endpoint is disabled, same fallback story as the
# OAuth2 secrets: fine outside production, nothing works without it set.
LIVETRACK_WEBHOOK_SECRET = ENV.fetch("LIVETRACK_WEBHOOK_SECRET", "")
# Login delegates to the Gitea instance the user already has an account
# on, rather than maintaining a local username/password — see
@@ -210,6 +219,22 @@ module Maptool
json path: path, tree: GPX_STORE.tree
end
# Called by the Apps Script watching Gmail for Garmin LiveTrack emails —
# it only extracts and forwards the session ID, nothing else.
post "/api/livetrack/session" do
halt 403 if LIVETRACK_WEBHOOK_SECRET.empty? || params[:secret] != LIVETRACK_WEBHOOK_SECRET
session_id = params[:session_id].to_s.strip
halt 400, json(error: "session_id is required") if session_id.empty?
LIVETRACK_STORE.add(session_id)
json ok: true
end
get "/api/livetrack/sessions" do
json sessions: LIVETRACK_STORE.list
end
get "/api/gnss/position" do
GNSS_LATEST_MUTEX.synchronize do
received_at = GNSS_LATEST[:received_at]