Commit graph

1020 commits

Author SHA1 Message Date
CI
1caa2fe6bc chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771548585-d9c04f7 [skip ci] 2026-02-20 00:51:10 +00:00
d9c04f7e0c
Fix code review findings: security, correctness, performance, dead code
Security:
- Add SQL identifier validation to DbOptimizer.analyze_table/vacuum_table
- Move health/readiness routes to non-rate-limited pipeline

Correctness:
- Fix monitor reference leak in StreamingPacketsPubSub (track refs, demonitor on unsubscribe)
- Persist circuit breaker half-open state transitions
- Simplify ShutdownHandler.terminate to avoid pointless Process.send_after

Performance:
- Batch marker removal into single WebSocket event (remove_markers_batch)
- Use Task.Supervisor.start_child for fire-and-forget broadcasts

Dead code removal:
- Remove copy_insert/regular_batch_insert/rows_to_csv from DbOptimizer
- Remove dead packet_pipeline_integration tests
- Remove unused telemetry event detach

Other:
- Fix Callsign.valid? docstring to match permissive behavior
- Fix DatabaseMetrics to delegate instead of hardcoding pool stats
- Add circuit breaker and encoding test coverage
2026-02-19 18:49:27 -06:00
CI
0084539bb1 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771548120-118a55b [skip ci] 2026-02-20 00:43:20 +00:00
118a55b26e
Fix APRS object name extraction for names with special characters
The object name regex only matched [A-Z0-9 ] which missed names with
hyphens (P-K5SGD), hash symbols (#146.760), and other printable ASCII.
Changed to .{9}[*_] to match all valid object names including killed
objects. Also merged the two cond branches so data_extended is tried
first with information_field parsing as fallback.

Removed dead copy_insert/3 tests from db_optimizer_test.
2026-02-19 18:41:37 -06:00
8a8e16aaba
Expand API test response to full container width
Move JSON response display outside the max-w-md form container
so API results use the full available width instead of being
constrained to the narrow form width.
2026-02-19 18:41:37 -06:00
CI
39bc7e3eaa chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771547268-27f9b32 [skip ci] 2026-02-20 00:29:11 +00:00
27f9b32dff
Fix duplicate station icons when live packet replaces historical
Historical loading overrides all markers to historical:true, including
the most-recent marker. When a new live packet arrives, the conversion
check only looked for non-historical markers, missing the existing
"current" marker. Broaden the check to also convert markers with
is_most_recent_for_callsign set.
2026-02-19 18:27:26 -06:00
ac51ecaada
Fix multiple TODO bugs: drain handler, callsign dedup, packet pipeline
- Fix drain_connections missing {:noreply, socket} wrapper
- Fix get_callsign_key to match atom keys from Ecto structs
- Add SpatialPubSub.broadcast_packet to PacketDistributor
- Fix pid_alive? treating {:badrpc, _} as truthy
- Fix SpatialPubSub ensure_float crash on integer strings
- Fix inverted memory calculation in ConnectionMonitor
- Remove dead SSL handler from Is module, consolidate into handle_socket_data
- Remove duplicate packet transformation in Is.dispatch
- Remove invalid placeholders option from insert_all calls
- Change PacketPipelineSupervisor to :rest_for_one strategy
- Update TODO.md marking fixed items
2026-02-19 18:27:25 -06:00
CI
c8777f9687 chore: update aprs.me image to git.mcintire.me/graham/aprs.me:main-1771546872-2030a7c [skip ci] 2026-02-20 00:22:33 +00:00
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