Commit graph

1011 commits

Author SHA1 Message Date
2030a7c8a6
Fix trail hover showing wrong igate RF path
Use mousemove instead of mouseover so the RF path updates as the
cursor moves along the trail. Send the mouse position rather than
the nearest-position's coordinates so the path line anchors where
the user is actually hovering. Debounce and deduplicate events to
avoid flooding the server.
2026-02-19 18:20:49 -06:00
CI
a42dc1d68f chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771546731-753fa50 [skip ci] 2026-02-20 00:20:13 +00:00
753fa50463
Fix ConnectionMonitor deadlock and LeaderElection silent leadership loss
ConnectionMonitor.gather_cluster_stats was calling :rpc.call(Node.self())
which GenServer.call'd back into itself while blocked in handle_info,
causing a 5-second deadlock timeout every 30 seconds. Fixed by computing
local stats directly from state.

LeaderElection never verified that a leader still held the :global
registration. After :global conflict resolution (two isolated nodes
merging), the loser was silently unregistered but stayed is_leader: true
forever, causing both pods to connect to APRS-IS with the same callsign
in a reconnect loop. Fixed by adding verify_leadership/1 to
check_leadership which detects lost registrations and steps down.
2026-02-19 18:18:31 -06:00
CI
6e29bd80be chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771544325-29da64a [skip ci] 2026-02-19 23:38:53 +00:00
29da64a991
Remove TODO.md — all items assessed or completed 2026-02-19 17:38:26 -06:00
CI
cb884606f3 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771544148-497ad85 [skip ci] 2026-02-19 23:35:56 +00:00
497ad85af2
Assess bounds update loading indicator TODO as not needed
Existing historical loading spinner already covers the slow DB
query path; the only unindicated gap is the 400ms debounce which
is too short for a spinner without causing flicker.
2026-02-19 17:35:28 -06:00
CI
8c1a3c84ab chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771543306-66061a5 [skip ci] 2026-02-19 23:23:10 +00:00
66061a5abe
Fix leader election: prevent self-deregistration on re-election
:global.register_name returns :no even when the name is already
registered to the calling PID. This caused the leader to clear its
own leadership state every 5 seconds after cluster formation triggered
a redundant re-election attempt.

Now checks if we already hold the registration before attempting to
re-register, preserving leadership state across cluster topology changes.
2026-02-19 17:21:26 -06:00
CI
dda4bbce15 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771538611-976b6f1 [skip ci] 2026-02-19 22:04:59 +00:00
976b6f153f
Fix slideover toggle: always-light button, map resize via LiveView JS
The slideover toggle button now uses a fixed light theme (white bg,
dark arrow) so it's always visible on the map regardless of OS dark
mode. Removed prefers-color-scheme overrides since the button overlays
the always-light map tiles.

Fixed map not resizing when closing the sidebar — the #aprs-map div
has phx-update="ignore" so server-rendered classes never updated.
Now uses LiveView JS commands (JS.toggle_class + JS.dispatch) to
toggle classes client-side and trigger map.invalidateSize(), replacing
the custom push_event/handleEvent JS handler.
2026-02-19 16:02:08 -06:00
75ceb73607
Fix Erlang clustering: use IP-based node names with DNS strategy
DNSSRV strategy resolves to IP-based names but nodes were named by
pod name, causing connection failures. Switch to DNS strategy with
IP-based node naming so libcluster discovery matches actual node names.
2026-02-19 16:01:20 -06:00
CI
bff63bb8a8 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771538286-d182024 [skip ci] 2026-02-19 21:58:14 +00:00
d182024b8a
Scale to 2 replicas and enable Erlang clustering 2026-02-19 15:57:42 -06:00
CI
ae9d8eb595 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771537665-ca9ad68 [skip ci] 2026-02-19 21:49:06 +00:00
ca9ad68c25
Drop 31 unused indexes on packets table (~4.6 GB)
Exact duplicates, indexes subsumed by better composites,
dead lower() indexes (codebase uses upper()), and indexes
with no matching query pattern in the codebase.
2026-02-19 15:47:27 -06:00
CI
a9b0fad1de chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771537316-14fa16d [skip ci] 2026-02-19 21:43:18 +00:00
14fa16df6d
Fix RF path visualization for hovering over track points
parse_rf_path was filtering out all paths containing qA* constructs,
which is virtually every APRS-IS packet. The qA* element delimits the
RF path from the APRS-IS metadata — it should be split on, not
discarded. Now properly extracts digipeaters (before qA*) and the
igate (after qA*), while filtering APRS aliases like WIDE/RELAY/TRACE.

Also adds hover handlers to trail polylines so hovering anywhere on a
call's track shows the RF path, not just on the dot markers.
2026-02-19 15:41:32 -06:00
CI
91ced547b1 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771536841-1d0b1ba [skip ci] 2026-02-19 21:34:09 +00:00
1d0b1ba99c
Mark backpressure item as done in TODO.md 2026-02-19 15:33:22 -06:00
fa89c6d3ca
Add TCP backpressure to packet pipeline
When the PacketProducer buffer crosses 80% capacity, signal Aprsme.Is
to switch its TCP socket to passive mode, leveraging TCP flow control
to slow APRS-IS intake. Resume active mode when buffer drains below 30%.

A 30-second safety valve timer auto-resumes if backpressure gets stuck.
Disconnect/reconnect/terminate paths all clear backpressure state.
2026-02-19 15:31:51 -06:00
CI
046f97db54 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771535328-15e6fda [skip ci] 2026-02-19 21:10:13 +00:00
15e6fda5ac
Fix broken scrolling on non-map pages
Remove overly aggressive CSS overrides that set height: auto and
overflow: auto on html/body. These forced the html element to expand
to full content height, preventing the browser's native viewport
scrolling. The browser handles non-map page scrolling correctly by
default — only map pages need explicit overflow: hidden.
2026-02-19 15:08:16 -06:00
bb6c24856d
Add RemoteIp plug to show real client IP in K8s logs
Extracts client IP from CF-Connecting-IP or X-Forwarded-For headers
and sets conn.remote_ip before request logging, so K8s logs show the
actual client address instead of the internal cluster/ingress IP.
2026-02-19 15:06:41 -06:00
CI
17bef39d51 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771534930-b5d7cf4 [skip ci] 2026-02-19 21:03:36 +00:00
b5d7cf4a38
Replace ip-api.com geolocation with Cloudflare headers
Use CF-IPLatitude/CF-IPLongitude headers instead of making HTTP calls
to ip-api.com on every page load. Eliminates ~500ms latency, external
API dependency, and flaky tests.

Also disable test server to prevent port conflicts during development.
2026-02-19 15:01:49 -06:00
CI
ba4f3ee392 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771534282-8716202 [skip ci] 2026-02-19 20:54:49 +00:00
871620223d
Performance improvements, code cleanup, remove sobelow
- Switch PacketProducer buffer from list to :queue for O(1) overflow handling
- Add cached leadership check via :persistent_term to eliminate GenServer.call
  bottleneck in packet distribution hot path
- Simplify packets_live coordinate extraction from 70 lines of nested
  conditionals to helper functions (extract_coordinate/2, format_coordinate/1)
- Remove debug logging from hot paths (packets.ex queries, PacketDistributor,
  app.js console.logs)
- Remove sobelow dependency (false positives blocking commits)
- Add 25 new tests for PacketProducer, coordinate helpers, and cached leadership
2026-02-19 14:51:01 -06:00
CI
1b23c3026e chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771531931-509f271 [skip ci] 2026-02-19 20:13:37 +00:00
509f271eac
Improve packet pipeline reliability, security, and cleanup efficiency
- Fix XSS vulnerability in buildPopupContent fallback (escape callsign/comment)
- Add batch insert retry with individual fallback in PacketConsumer
- Add APRS-IS login response validation (logresp dispatch handler)
- Fix stale generation check bypass in HistoricalLoader
- Replace two-step SELECT+DELETE cleanup with single CTE-based batch DELETE
- Change default packet retention from 365 to 7 days
- Delete MapHelpers (100% duplicate of CoordinateUtils + BoundsUtils)
- Delete AprsIsConnection (dead code, second unused APRS-IS connection)
- Fix InfoMap hook memory leak (store/cancel setTimeout ref)
- Extract singleton loadMapBundle with callback queue in app.js
- Extract @heat_map_max_zoom constant in DisplayManager
- Add telemetry to cleanup worker and PacketProducer buffer overflow
- Track PacketProducer buffer_size as integer for O(1) length checks
- Remove duplicate bounds functions from index.ex
2026-02-19 14:11:48 -06:00
CI
e995b0f144 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771529673-a7d934c [skip ci] 2026-02-19 19:36:02 +00:00
a7d934cb5b
Fix APRS-IS connection, packet pipeline, and cleanup reliability
- Handle login send failure in AprsIsConnection instead of crashing
- Close leaked sockets on login failure in Aprsme.Is (init + reconnect)
- Keep rescheduling keepalive timer when disconnected to prevent permanent loss
- Use supervised BroadcastTaskSupervisor instead of unsupervised Task.start
  for packet broadcasting
- Remove racy Process.alive? check in StreamingPacketsPubSub; send/2 to
  dead PIDs is a no-op, :DOWN monitors handle cleanup
- Add dateline wrapping to SpatialPubSub.point_in_bounds? matching
  StreamingPacketsPubSub behavior
- Add bad_packets table cleanup to PacketCleanupWorker
- Add TODO.md with remaining improvements
2026-02-19 13:34:10 -06:00
CI
baf3d7b857 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771528828-b4f85bf [skip ci] 2026-02-19 19:21:54 +00:00
b4f85bf12c
Fix callsign registration to be case-insensitive
Users could register duplicate callsigns with different casing (e.g.,
"w1aw" and "W1AW") because the database unique index was case-sensitive.
Additionally, live validation showed false "already taken" errors on
every keystroke.

Changes:
- Add case-insensitive unique index using lower(callsign)
- Skip callsign uniqueness validation during live form changes
- Add test verifying case-insensitive uniqueness enforcement
2026-02-19 13:20:07 -06:00
CI
e5659e889f chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771518150-f6730b2 [skip ci] 2026-02-19 16:23:59 +00:00
f6730b282a
Fix API documentation to match implementation
- REST API: Fix id type from integer to UUID, add device/equipment
  fields, correct rate limiting info (100 req/min, not "none"),
  add 429 status code
- Mobile API: Fix search_callsign response field (lon not lng),
  add path to packet fields, correct rate limiting/timeout docs,
  clarify update_bounds behavior
- Fix flaky pipeline tests (async race on global Application config)
2026-02-19 10:22:10 -06:00
CI
33b4a35900 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771517296-4de4829 [skip ci] 2026-02-19 16:09:44 +00:00
4de48294c6
Remove DaisyUI, migrate to pure Tailwind CSS with TailwindUI patterns
Replace all DaisyUI component classes with pure Tailwind CSS utility
classes following TailwindUI conventions. Use dark: variants for dark
mode instead of DaisyUI's data-theme semantic color system.

- Remove DaisyUI plugin and theme vendor files
- Configure Tailwind v4 custom dark variant for data-theme attribute
- Migrate all components: cards, tables, badges, buttons, forms,
  navigation, dropdowns, modals, alerts, star ratings, spinners
- Update all auth pages (login, register, settings, password reset,
  confirmation) to TailwindUI card layout
- Update content pages (about, API docs, status, packets, bad packets,
  info, weather) to TailwindUI patterns
- Replace opacity-based text styling with semantic gray colors
- Update test assertions to match new class names
2026-02-19 10:07:50 -06:00
CI
f9af61f29a chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771514834-aca0571 [skip ci] 2026-02-19 15:28:45 +00:00
aca0571a3e
Replace JS hooks with LiveView patterns for map page styling, slideover, and theme switching
- Remove BodyClassHook: use CSS :has() selectors on data-map-page attribute
  instead of JS toggling body.map-page class
- Remove ResponsiveSlideoverHook: pass viewport_width via LiveSocket connect
  params and determine initial slideover state server-side
- Convert theme selector from data-set-theme attributes with DOMContentLoaded
  listeners to JS.dispatch("phx:set-theme") with a single event handler
- Fix buggy reRenderAllCharts that incorrectly used `new MapAPRSMap()` as a
  Map constructor; charts now re-render via themeChanged event directly
2026-02-19 09:26:53 -06:00
CI
f1b363a2e2 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771514450-f591e15 [skip ci] 2026-02-19 15:22:21 +00:00
f591e15449
Fix registration form showing validation errors before submission
Only display field errors when the changeset has an action set,
preventing "can't be blank" messages from appearing on initial
page load.
2026-02-19 09:20:31 -06:00
CI
f94925407d chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771514008-68f64f3 [skip ci] 2026-02-19 15:17:11 +00:00
68f64f3699
Display APRS object/item names instead of sender callsign
APRS object packets (e.g. DAPNET transmitters) were showing the
sender's callsign on the map instead of the object name. For example,
DB0SDA (Germany) sending an object for P-K5SGD (Texas) would display
as "DB0SDA" at the Texas coordinates.

- Add display_name/1 to PacketUtils that returns object_name for
  objects, item_name for items, falling back to sender
- Update DataBuilder to use display_name in all callsign display paths
- Fix object/item detection order in packet_consumer to prevent
  objects from being incorrectly flagged as items
- Add DataBuilder tests for object/item display name behavior
- Fix flaky PacketPipelineSupervisor test (ensure module loaded)
2026-02-19 09:13:03 -06:00
CI
8e7405ccb4 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771460127-d803d07 [skip ci] 2026-02-19 00:16:56 +00:00
d803d070ac
Add other SSIDs list to map sidebar, fix context/web boundary
- Add Packets.get_other_ssids/1 to query other SSIDs for a base callsign
- Show other SSIDs in MapLive sidebar with track and info link buttons
- Clicking an SSID tracks it on the map (zooms, shows marker, updates URL)
- Refactor InfoLive.Show to use shared Packets.get_other_ssids/1
- Remove web-layer dependency from Packets context (no more
  AprsmeWeb.TimeHelpers calls from data layer)
- Return raw received_at DateTime instead of pre-formatted timestamp maps
- Format timestamps in the view layer where they belong
2026-02-18 18:15:02 -06:00
CI
3b64c95fe9 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771452962-abc6ebe [skip ci] 2026-02-18 22:16:10 +00:00
abc6ebe798
Restore FluxCD auto-deployment via GitOps
Replace direct kubectl deployment with GitOps pattern:
- Build and push to git.mcintire.me registry using built-in token
- Commit updated image tag in k8s/deployment.yaml after each build
- FluxCD picks up the manifest change and handles the k8s rollout
- Remove Tailscale, kubectl, and ghcr.io dependencies
2026-02-18 16:15:43 -06:00
CI
33587fb866 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771448056-e22d385 [skip ci] 2026-02-18 20:55:46 +00:00
e22d385f6a
Disable APRS-IS connection when running mix tasks 2026-02-18 14:52:24 -06:00