feat(aprs): add read-only AprsRepo for aprs.me database access
Wire up Microwaveprop.AprsRepo as an optional secondary Ecto repo that points at the sister aprs.me Postgres for read-only access. This is the foundation for upcoming 144 MHz calibration work that uses verified APRS RF hops as ground truth. * lib/microwaveprop/aprs_repo.ex declares a read_only repo; no schemas, no migrations, no writes — Microwaveprop never persists APRS data. * Application supervisor starts the repo only when configured (dev/test via config blocks, prod via APRS_DATABASE_URL). A missing config logs a warning and skips startup so the main app still boots. * config/dev.exs + config/test.exs add local aprsme_dev / aprsme_test endpoints. Test pool uses Ecto SQL Sandbox; tests that don't touch APRS data don't need to check the connection out. * config/runtime.exs guards the prod block on APRS_DATABASE_URL — unset env logs a warning and leaves the repo unconfigured.
This commit is contained in:
parent
1f8765f757
commit
714d1ce432
5 changed files with 88 additions and 5 deletions
|
|
@ -3,6 +3,15 @@ import Config
|
||||||
# Do not include metadata nor timestamps in development logs
|
# Do not include metadata nor timestamps in development logs
|
||||||
config :logger, :default_formatter, format: "[$level] $message\n"
|
config :logger, :default_formatter, format: "[$level] $message\n"
|
||||||
|
|
||||||
|
# Read-only secondary repo pointing at the local aprs.me dev database.
|
||||||
|
# See lib/microwaveprop/aprs_repo.ex — used by APRS calibration tooling
|
||||||
|
# only; the main app boots fine without it.
|
||||||
|
config :microwaveprop, Microwaveprop.AprsRepo,
|
||||||
|
username: "postgres",
|
||||||
|
hostname: "localhost",
|
||||||
|
database: "aprsme_dev",
|
||||||
|
pool_size: 2
|
||||||
|
|
||||||
# Configure your database
|
# Configure your database
|
||||||
config :microwaveprop, Microwaveprop.Repo,
|
config :microwaveprop, Microwaveprop.Repo,
|
||||||
username: "postgres",
|
username: "postgres",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
|
alias Microwaveprop.Workers.CanadianSoundingFetchWorker
|
||||||
|
alias Oban.Plugins.Cron
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
# config/runtime.exs is executed for all environments, including
|
# config/runtime.exs is executed for all environments, including
|
||||||
# during releases. It is executed after compilation and before the
|
# during releases. It is executed after compilation and before the
|
||||||
# system starts, so it is typically used to load production configuration
|
# system starts, so it is typically used to load production configuration
|
||||||
|
|
@ -16,9 +21,6 @@ import Config
|
||||||
#
|
#
|
||||||
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
|
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
|
||||||
# script that automatically sets the env var above.
|
# script that automatically sets the env var above.
|
||||||
alias Microwaveprop.Workers.CanadianSoundingFetchWorker
|
|
||||||
alias Oban.Plugins.Cron
|
|
||||||
|
|
||||||
if System.get_env("PHX_SERVER") do
|
if System.get_env("PHX_SERVER") do
|
||||||
config :microwaveprop, MicrowavepropWeb.Endpoint, server: true
|
config :microwaveprop, MicrowavepropWeb.Endpoint, server: true
|
||||||
end
|
end
|
||||||
|
|
@ -355,6 +357,24 @@ if config_env() == :prod do
|
||||||
# pool_count: 4,
|
# pool_count: 4,
|
||||||
socket_options: maybe_ipv6
|
socket_options: maybe_ipv6
|
||||||
|
|
||||||
|
# Optional secondary repo for read-only access to aprs.me's database.
|
||||||
|
# Used by APRS-based 144 MHz calibration tooling. When the env var is
|
||||||
|
# unset we leave the AprsRepo unconfigured; Microwaveprop.Application
|
||||||
|
# logs a warning and skips starting it, and the rest of the app boots
|
||||||
|
# normally.
|
||||||
|
case System.get_env("APRS_DATABASE_URL") do
|
||||||
|
nil ->
|
||||||
|
Logger.warning("APRS_DATABASE_URL not set — Microwaveprop.AprsRepo will not start; APRS calibration disabled.")
|
||||||
|
|
||||||
|
:ok
|
||||||
|
|
||||||
|
aprs_url ->
|
||||||
|
config :microwaveprop, Microwaveprop.AprsRepo,
|
||||||
|
url: aprs_url,
|
||||||
|
pool_size: String.to_integer(System.get_env("APRS_POOL_SIZE") || "2"),
|
||||||
|
socket_options: maybe_ipv6
|
||||||
|
end
|
||||||
|
|
||||||
config :microwaveprop, MicrowavepropWeb.Endpoint,
|
config :microwaveprop, MicrowavepropWeb.Endpoint,
|
||||||
# SSL terminated by Cloudflare tunnel; generated URLs still use https
|
# SSL terminated by Cloudflare tunnel; generated URLs still use https
|
||||||
url: [host: host, port: 443, scheme: "https"],
|
url: [host: host, port: 443, scheme: "https"],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
|
alias Ecto.Adapters.SQL.Sandbox
|
||||||
alias Microwaveprop.Qrz.Client
|
alias Microwaveprop.Qrz.Client
|
||||||
|
|
||||||
# Only in tests, remove the complexity from the password hashing algorithm
|
# Only in tests, remove the complexity from the password hashing algorithm
|
||||||
|
|
@ -15,6 +16,21 @@ config :microwaveprop, Client,
|
||||||
plug: {Req.Test, Client},
|
plug: {Req.Test, Client},
|
||||||
retry: false
|
retry: false
|
||||||
|
|
||||||
|
# Read-only secondary repo for aprs.me. Configured here so the app boots
|
||||||
|
# under MIX_ENV=test, but uses the SQL.Sandbox in :manual mode so tests
|
||||||
|
# that don't touch APRS data don't have to check the DB out. Tests that
|
||||||
|
# do query the AprsRepo must call `Ecto.Adapters.SQL.Sandbox.checkout/1`
|
||||||
|
# explicitly.
|
||||||
|
config :microwaveprop, Microwaveprop.AprsRepo,
|
||||||
|
username: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
hostname: "localhost",
|
||||||
|
database: "aprsme_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||||
|
pool: Sandbox,
|
||||||
|
pool_size: 2,
|
||||||
|
ownership_timeout: 60_000,
|
||||||
|
timeout: 60_000
|
||||||
|
|
||||||
config :microwaveprop, Microwaveprop.Geocoder,
|
config :microwaveprop, Microwaveprop.Geocoder,
|
||||||
plug: {Req.Test, Microwaveprop.Geocoder},
|
plug: {Req.Test, Microwaveprop.Geocoder},
|
||||||
retry: false
|
retry: false
|
||||||
|
|
@ -33,7 +49,7 @@ config :microwaveprop, Microwaveprop.Repo,
|
||||||
password: "postgres",
|
password: "postgres",
|
||||||
hostname: "localhost",
|
hostname: "localhost",
|
||||||
database: "microwaveprop_test#{System.get_env("MIX_TEST_PARTITION")}",
|
database: "microwaveprop_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||||
pool: Ecto.Adapters.SQL.Sandbox,
|
pool: Sandbox,
|
||||||
pool_size: System.schedulers_online() * 2,
|
pool_size: System.schedulers_online() * 2,
|
||||||
ownership_timeout: 60_000,
|
ownership_timeout: 60_000,
|
||||||
timeout: 60_000
|
timeout: 60_000
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ defmodule Microwaveprop.Application do
|
||||||
MicrowavepropWeb.Telemetry,
|
MicrowavepropWeb.Telemetry,
|
||||||
Microwaveprop.PromEx,
|
Microwaveprop.PromEx,
|
||||||
Microwaveprop.Repo,
|
Microwaveprop.Repo,
|
||||||
|
# AprsRepo is optional — only starts when configured with a :url
|
||||||
|
# (dev/test from config blocks, prod from APRS_DATABASE_URL). Missing
|
||||||
|
# config logs a warning and skips startup so the app still boots.
|
||||||
|
aprs_repo_child_spec(),
|
||||||
{Cluster.Supervisor, [topologies, [name: Microwaveprop.ClusterSupervisor]]},
|
{Cluster.Supervisor, [topologies, [name: Microwaveprop.ClusterSupervisor]]},
|
||||||
{Phoenix.PubSub, name: Microwaveprop.PubSub},
|
{Phoenix.PubSub, name: Microwaveprop.PubSub},
|
||||||
# Partitioned Task.Supervisor — callers route heavy `async_stream`
|
# Partitioned Task.Supervisor — callers route heavy `async_stream`
|
||||||
|
|
@ -43,7 +47,7 @@ defmodule Microwaveprop.Application do
|
||||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||||
# for other strategies and supported options
|
# for other strategies and supported options
|
||||||
opts = [strategy: :one_for_one, name: Microwaveprop.Supervisor]
|
opts = [strategy: :one_for_one, name: Microwaveprop.Supervisor]
|
||||||
result = Supervisor.start_link(children, opts)
|
result = children |> Enum.reject(&is_nil/1) |> Supervisor.start_link(opts)
|
||||||
|
|
||||||
# Attach the Oban exception telemetry tap so per-worker recovery
|
# Attach the Oban exception telemetry tap so per-worker recovery
|
||||||
# callbacks and structured logs fire on job failure. Must run after
|
# callbacks and structured logs fire on job failure. Must run after
|
||||||
|
|
@ -92,6 +96,23 @@ defmodule Microwaveprop.Application do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns Microwaveprop.AprsRepo child spec if configured (has a :url
|
||||||
|
# or :database key), otherwise logs a warning and returns nil so the
|
||||||
|
# supervisor children list can filter it out. APRS calibration is an
|
||||||
|
# optional integration with the sister aprs.me database — we never
|
||||||
|
# crash boot when it's unavailable.
|
||||||
|
defp aprs_repo_child_spec do
|
||||||
|
config = Application.get_env(:microwaveprop, Microwaveprop.AprsRepo, [])
|
||||||
|
|
||||||
|
if Keyword.has_key?(config, :url) or Keyword.has_key?(config, :database) do
|
||||||
|
Microwaveprop.AprsRepo
|
||||||
|
else
|
||||||
|
Logger.warning("Microwaveprop.AprsRepo not configured (no :url or :database) — APRS calibration disabled.")
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the deployment (image build) timestamp.
|
Returns the deployment (image build) timestamp.
|
||||||
|
|
||||||
|
|
|
||||||
17
lib/microwaveprop/aprs_repo.ex
Normal file
17
lib/microwaveprop/aprs_repo.ex
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
defmodule Microwaveprop.AprsRepo do
|
||||||
|
@moduledoc """
|
||||||
|
Secondary repo for read-only access to aprs.me's `packets` table.
|
||||||
|
|
||||||
|
Used by `Microwaveprop.Aprs` and the `Mix.Tasks.Calibrate.Aprs144` mix
|
||||||
|
task to pull verified RF hops as ground truth for 144 MHz scoring.
|
||||||
|
|
||||||
|
This repo never owns schema, migrations, or writes — it only ever
|
||||||
|
issues SELECT queries against aprs.me's existing partitioned `packets`
|
||||||
|
table. The connection is configured to fail closed if `APRS_DATABASE_URL`
|
||||||
|
is unset in production.
|
||||||
|
"""
|
||||||
|
use Ecto.Repo,
|
||||||
|
otp_app: :microwaveprop,
|
||||||
|
adapter: Ecto.Adapters.Postgres,
|
||||||
|
read_only: true
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue