From 447a76b3cc6239a6b41541112fcab2ad7d6e0f03 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Wed, 18 Feb 2026 14:13:22 -0600 Subject: [PATCH] update --- .forgejo/workflows/build.yaml | 9 +++ .formatter.exs | 2 +- .gitignore | 1 + capture_aprs.py | 86 +++++++++++++++++++++++++++++ config/test.exs | 3 - docs/improvement-todos.md | 11 ---- k8s/flux/imagepolicy.yaml | 14 ----- k8s/flux/imagerepository.yaml | 10 ---- k8s/flux/imageupdateautomation.yaml | 24 -------- lib/aprsme_web/telemetry.ex | 20 +------ mix.exs | 3 +- mix.lock | 1 - 12 files changed, 101 insertions(+), 83 deletions(-) create mode 100755 capture_aprs.py delete mode 100644 k8s/flux/imagepolicy.yaml delete mode 100644 k8s/flux/imagerepository.yaml delete mode 100644 k8s/flux/imageupdateautomation.yaml diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index 2e5f520..d9035df 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -51,3 +51,12 @@ jobs: ${{ steps.meta.outputs.latest_tag }} cache-from: type=registry,ref=${{ steps.meta.outputs.cache_tag }} cache-to: type=registry,ref=${{ steps.meta.outputs.cache_tag }},mode=max + + - name: Update deployment image tag + run: | + sed -i "s|image: git\.mcintire\.me/graham/aprs\.me:.*|image: ${{ steps.meta.outputs.tag }}|g" k8s/deployment.yaml + git config user.email "ci@git.mcintire.me" + git config user.name "CI" + git add k8s/deployment.yaml + git diff --cached --quiet || git commit -m "chore: update aprs.me image to ${{ steps.meta.outputs.tag }} [skip ci]" + git push origin HEAD:main diff --git a/.formatter.exs b/.formatter.exs index 1d4dd1f..58990ec 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,5 +1,5 @@ [ - import_deps: [:ecto, :ecto_sql, :phoenix, :stream_data], + import_deps: [:ecto, :ecto_sql, :phoenix], subdirectories: ["priv/*/migrations"], plugins: [Phoenix.LiveView.HTMLFormatter, Styler], inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"], diff --git a/.gitignore b/.gitignore index 0172131..1ddc4c2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ npm-debug.log /.lexical /badpackets.txt +/packets.txt # Ignore Kubernetes secrets file k8s/secrets.yaml diff --git a/capture_aprs.py b/capture_aprs.py new file mode 100755 index 0000000..4317235 --- /dev/null +++ b/capture_aprs.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +""" +Connect to dallas.aprs2.net APRS-IS server and write all packets to packets.txt. +Uses receive-only login (passcode -1) so no amateur license required. +""" + +import socket +import sys +import signal +from datetime import datetime + +HOST = "dallas.aprs2.net" +PORT = 10152 # Full feed, no filter required (use 14580 with a filter) +OUTPUT_FILE = "packets.txt" + +running = True + + +def signal_handler(sig, frame): + global running + print("\nShutting down...") + running = False + + +signal.signal(signal.SIGINT, signal_handler) +signal.signal(signal.SIGTERM, signal_handler) + + +def main(): + print(f"Connecting to {HOST}:{PORT}...") + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect((HOST, PORT)) + sock.settimeout(30) + + f = sock.makefile("r", encoding="utf-8", errors="replace") + + # Read server banner + banner = f.readline().strip() + print(f"Server: {banner}") + + # Login - passcode -1 for receive-only + login = "user N0CALL pass -1 vers APRSCapture 1.0\r\n" + sock.sendall(login.encode()) + + # Read login response + response = f.readline().strip() + print(f"Login: {response}") + + print(f"Writing packets to {OUTPUT_FILE} (Ctrl+C to stop)...") + + count = 0 + with open(OUTPUT_FILE, "w", encoding="utf-8") as out: + out.write(f"# APRS capture from {HOST}:{PORT}\n") + out.write(f"# Started: {datetime.utcnow().isoformat()}Z\n\n") + + while running: + try: + line = f.readline() + if not line: + print("Connection closed by server.") + break + + line = line.strip() + if not line: + continue + + # Skip comments (server messages start with #) + out.write(line + "\n") + out.flush() + count += 1 + + if count % 100 == 0: + print(f" {count} packets captured...") + + except socket.timeout: + continue + except Exception as e: + print(f"Error: {e}") + break + + print(f"Done. Captured {count} packets to {OUTPUT_FILE}") + + +if __name__ == "__main__": + main() diff --git a/config/test.exs b/config/test.exs index 310d104..f556a35 100644 --- a/config/test.exs +++ b/config/test.exs @@ -37,9 +37,6 @@ config :aprsme, AprsmeWeb.Endpoint, secret_key_base: "IV9+ENaw9i8xjReRk4sULRvRgsmFVTGQwQGGrf4G+Q/SFMeHBCNWRlPXQ2YvT36R", server: true -# Disable Prometheus telemetry in test mode to avoid port conflicts -config :aprsme, AprsmeWeb.Telemetry, enabled: false - # Disable cleanup scheduler in test environment config :aprsme, :cleanup_scheduler, enabled: false diff --git a/docs/improvement-todos.md b/docs/improvement-todos.md index 5dd87ea..6bd1755 100644 --- a/docs/improvement-todos.md +++ b/docs/improvement-todos.md @@ -96,17 +96,6 @@ This document tracks potential improvements identified during the multi-replica - Info page (/info/:callsign) now updates in real-time with complete packet data - Ensures all UI elements refresh properly when new packets arrive -### 3. Add Metrics and Monitoring with Prometheus -- **Status**: Pending -- **Impact**: High - Production visibility -- **Details**: - - Add Prometheus metrics exporter (prometheus_ex) - - Track packet processing rates and latencies - - Monitor connection pool usage (PgBouncer & app) - - Track cache hit rates - - Add custom business metrics - - Monitor APRS-IS connection stability - ## Medium Priority diff --git a/k8s/flux/imagepolicy.yaml b/k8s/flux/imagepolicy.yaml deleted file mode 100644 index 87e50a2..0000000 --- a/k8s/flux/imagepolicy.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImagePolicy -metadata: - name: aprs-me - namespace: flux-system -spec: - imageRepositoryRef: - name: aprs-me - filterTags: - pattern: '^main-(?P[0-9]+)-[a-f0-9]+$' - extract: '$ts' - policy: - numerical: - order: asc diff --git a/k8s/flux/imagerepository.yaml b/k8s/flux/imagerepository.yaml deleted file mode 100644 index 27b4fc4..0000000 --- a/k8s/flux/imagerepository.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageRepository -metadata: - name: aprs-me - namespace: flux-system -spec: - image: git.mcintire.me/graham/aprs.me - interval: 1m - secretRef: - name: forgejo-registry diff --git a/k8s/flux/imageupdateautomation.yaml b/k8s/flux/imageupdateautomation.yaml deleted file mode 100644 index f317030..0000000 --- a/k8s/flux/imageupdateautomation.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1beta2 -kind: ImageUpdateAutomation -metadata: - name: aprs-me - namespace: flux-system -spec: - interval: 1m - sourceRef: - kind: GitRepository - name: aprs-me - git: - checkout: - ref: - branch: main - push: - branch: main - commit: - author: - name: FluxCD - email: fluxcd@aprs.me - messageTemplate: 'chore: update aprs.me image to {{range .Changed.Changes}}{{.NewValue}}{{end}} [skip ci]' - update: - path: ./k8s - strategy: Setters diff --git a/lib/aprsme_web/telemetry.ex b/lib/aprsme_web/telemetry.ex index a05ab96..c1edfa8 100644 --- a/lib/aprsme_web/telemetry.ex +++ b/lib/aprsme_web/telemetry.ex @@ -12,23 +12,9 @@ defmodule AprsmeWeb.Telemetry do @impl true def init(_arg) do - children = - if Application.get_env(:aprsme, AprsmeWeb.Telemetry)[:enabled] == false do - [ - # Only telemetry poller in test mode - {:telemetry_poller, measurements: periodic_measurements(), period: 10_000} - ] - - # Telemetry poller will execute the given period measurements - # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics - else - [ - {:telemetry_poller, measurements: periodic_measurements(), period: 10_000}, - # Add reporters as children of your supervision tree. - # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()} - {TelemetryMetricsPrometheus, [metrics: metrics()]} - ] - end + children = [ + {:telemetry_poller, measurements: periodic_measurements(), period: 10_000} + ] Supervisor.init(children, strategy: :one_for_one) end diff --git a/mix.exs b/mix.exs index df9b278..9ff031f 100644 --- a/mix.exs +++ b/mix.exs @@ -81,7 +81,6 @@ defmodule Aprsme.MixProject do {:resend, "~> 0.4.1"}, {:telemetry_metrics, "~> 1.0"}, {:telemetry_poller, "~> 1.0"}, - {:telemetry_metrics_prometheus, "~> 1.1"}, aprs_dep(), {:esbuild, "~> 0.9", runtime: Mix.env() == :dev}, {:tailwind, "~> 0.4.0", runtime: Mix.env() == :dev}, @@ -101,7 +100,7 @@ defmodule Aprsme.MixProject do {:stream_data, "~> 1.2.0", only: [:dev, :test]}, {:igniter, "~> 0.7.0", only: [:dev, :test]}, {:mox, "~> 1.2", only: :test}, - {:styler, "~> 1.10.0", only: [:dev, :test], runtime: false}, + {:styler, "~> 1.10", only: :dev, runtime: false}, # {:httpoison, "~> 1.8"}, {:hammer, "~> 7.0"}, {:cachex, "~> 4.1"}, diff --git a/mix.lock b/mix.lock index 1c94009..7fbb07d 100644 --- a/mix.lock +++ b/mix.lock @@ -104,7 +104,6 @@ "tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"}, - "telemetry_metrics_prometheus": {:hex, :telemetry_metrics_prometheus, "1.1.0", "1cc23e932c1ef9aa3b91db257ead31ea58d53229d407e059b29bb962c1505a13", [:mix], [{:plug_cowboy, "~> 2.1", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}], "hexpm", "d43b3659b3244da44fe0275b717701542365d4519b79d9ce895b9719c1ce4d26"}, "telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.1", "c9755987d7b959b557084e6990990cb96a50d6482c683fb9622a63837f3cd3d8", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5e2c599da4983c4f88a33e9571f1458bf98b0cf6ba930f1dc3a6e8cf45d5afb6"}, "telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"}, "tesla": {:hex, :tesla, "1.14.0", "2abcc2a5cf45b9b351f0dd3a03d2a1511cc25bb6000b5c65def9093e0535a9d7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "1c5a8575fb1d990100f016fe0bae70a0902ce01dec5fc693f694d09161142e18"},