This commit is contained in:
Graham McIntire 2026-02-18 14:13:22 -06:00
parent e46e8ab3ea
commit 447a76b3cc
No known key found for this signature in database
12 changed files with 101 additions and 83 deletions

View file

@ -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

View file

@ -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"],

1
.gitignore vendored
View file

@ -35,6 +35,7 @@ npm-debug.log
/.lexical
/badpackets.txt
/packets.txt
# Ignore Kubernetes secrets file
k8s/secrets.yaml

86
capture_aprs.py Executable file
View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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<ts>[0-9]+)-[a-f0-9]+$'
extract: '$ts'
policy:
numerical:
order: asc

View file

@ -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

View file

@ -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

View file

@ -12,24 +12,10 @@ 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
children = [
{: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
Supervisor.init(children, strategy: :one_for_one)
end

View file

@ -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"},

View file

@ -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"},