Files
maptool/lib/maptool/livetrack_store.rb
T
randy f4b5237f01
CI / check (push) Successful in 47s
CI / deploy (push) Successful in 19s
Render a session's track on the map
Clicking a LiveTrack session now fetches and draws its track (line +
latest position). Garmin's own API 403s any plain server-side request
(Cloudflare bot protection), so LiveTrackClient fetches the session
page instead and pulls the track out of the server-rendered data
embedded in it. The webhook now captures the session's token too,
since the id alone isn't enough to fetch anything.
2026-09-06 22:07:13 +02:00

73 lines
2.3 KiB
Ruby

require "json"
require "fileutils"
module Maptool
# JSON-file-backed store of Garmin LiveTrack sessions, appended to by a
# webhook whenever a new LiveTrack email arrives (see docs/deployment.adoc).
# The token is what actually lets us fetch the session's track later (see
# LiveTrackClient) — Garmin's session URL is /session/<id>/token/<token>,
# and the id alone isn't enough. Validity/expiry isn't tracked here —
# whoever fetches the track finds out then.
class LiveTrackStore
def initialize(path)
@path = path
@mutex = Mutex.new
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, "[]") unless File.exist?(@path)
end
# Records `session_id`/`token`, moving it to the front if already present.
def add(session_id, token)
@mutex.synchronize do
sessions = read
sessions.reject! { |s| s["id"] == session_id }
sessions.unshift({ "id" => session_id, "token" => token, "received_at" => Time.now.utc.iso8601 })
File.write(@path, JSON.pretty_generate(sessions))
end
end
# Most recently received first.
def list
read
end
def find(session_id)
read.find { |s| s["id"] == session_id }
end
private
def read
JSON.parse(File.read(@path))
rescue Errno::ENOENT, JSON::ParserError
[]
end
end
end
if __FILE__ == $0
require "tmpdir"
Dir.mktmpdir do |dir|
store = Maptool::LiveTrackStore.new(File.join(dir, "livetrack.json"))
raise "expected empty list" unless store.list == []
store.add("abc123", "tok-abc")
store.add("def456", "tok-def")
raise "expected 2 sessions" unless store.list.size == 2
raise "expected newest first" unless store.list.first["id"] == "def456"
raise "expected token stored" unless store.find("abc123")["token"] == "tok-abc"
raise "expected unknown id to be nil" unless store.find("nope").nil?
store.add("abc123", "tok-abc-2")
raise "expected re-added session moved to front, not duplicated" \
unless store.list.map { |s| s["id"] } == %w[abc123 def456]
raise "expected token updated on re-add" unless store.find("abc123")["token"] == "tok-abc-2"
reloaded = Maptool::LiveTrackStore.new(File.join(dir, "livetrack.json"))
raise "expected data to survive reload" unless reloaded.list.size == 2
end
puts "ok"
end