Turned on :error_handling, :underspecs, and :unmatched_returns in mix.exs dialyzer config. The 97 warnings this surfaced were fixed in place rather than suppressed: - unmatched_return (79): explicit discard with `_ = ...` for fire-and-forget side effects (Process.cancel_timer, :ets.new, send/2), and pattern-matched `:ok = ...` for control-plane Phoenix.PubSub subscribe/unsubscribe/broadcast calls so a future return-shape change fails loud. - contract_supertype (18): tightened @spec arg and return types on data_builder, historical_loader, url_params, packet_utils, encoding_utils, aprs_symbol, weather_controller, packet_replay to match each function's actual success typing. No behavioural change. mix compile clean, 1008 tests pass, dialyzer count is now 0.
169 lines
4.4 KiB
Elixir
169 lines
4.4 KiB
Elixir
defmodule AprsmeWeb.PageController do
|
|
@moduledoc false
|
|
use AprsmeWeb, :controller
|
|
|
|
alias Aprsme.Cluster.LeaderElection
|
|
|
|
def health(conn, _params) do
|
|
# Use our health check plug logic
|
|
health_status = Application.get_env(:aprsme, :health_status, :healthy)
|
|
version = :aprsme |> Application.spec(:vsn) |> List.to_string()
|
|
|
|
cond do
|
|
health_status == :draining ->
|
|
conn
|
|
|> put_status(503)
|
|
|> json(%{
|
|
status: "draining",
|
|
version: version,
|
|
message: "Application is draining connections",
|
|
timestamp: DateTime.utc_now()
|
|
})
|
|
|
|
Aprsme.ShutdownHandler.shutting_down?() ->
|
|
conn
|
|
|> put_status(503)
|
|
|> json(%{
|
|
status: "shutting_down",
|
|
version: version,
|
|
message: "Application is shutting down",
|
|
timestamp: DateTime.utc_now()
|
|
})
|
|
|
|
true ->
|
|
# Normal health check
|
|
db_healthy =
|
|
try do
|
|
_ = Aprsme.Repo.query!("SELECT 1")
|
|
true
|
|
rescue
|
|
_ -> false
|
|
end
|
|
|
|
if db_healthy do
|
|
json(conn, %{
|
|
status: "ok",
|
|
version: version,
|
|
database: "connected",
|
|
timestamp: DateTime.utc_now()
|
|
})
|
|
else
|
|
conn
|
|
|> put_status(503)
|
|
|> json(%{
|
|
status: "error",
|
|
version: version,
|
|
database: "disconnected",
|
|
timestamp: DateTime.utc_now()
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
@sitemap_paths ~w(/ /about /api /status /packets)
|
|
|
|
def sitemap(conn, _params) do
|
|
base_url = AprsmeWeb.Endpoint.url()
|
|
|
|
urls =
|
|
Enum.map(@sitemap_paths, fn path ->
|
|
" <url><loc>#{base_url}#{path}</loc></url>"
|
|
end)
|
|
|
|
xml = """
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
#{Enum.join(urls, "\n")}
|
|
</urlset>
|
|
"""
|
|
|
|
conn
|
|
|> put_resp_content_type("application/xml")
|
|
|> send_resp(200, xml)
|
|
end
|
|
|
|
def api_catalog(conn, _params) do
|
|
base_url = AprsmeWeb.Endpoint.url()
|
|
ws_url = String.replace(base_url, ~r/^http/, "ws")
|
|
|
|
body = %{
|
|
linkset: [
|
|
%{
|
|
anchor: "#{base_url}/api/v1",
|
|
"service-doc": [%{href: "#{base_url}/api", type: "text/html"}],
|
|
status: [%{href: "#{base_url}/status.json", type: "application/json"}]
|
|
},
|
|
%{
|
|
anchor: "#{ws_url}/mobile/websocket",
|
|
"service-doc": [
|
|
%{
|
|
href: "https://github.com/gmcintire/aprs.me/blob/main/docs/mobile-api.md",
|
|
type: "text/markdown"
|
|
}
|
|
],
|
|
status: [%{href: "#{base_url}/status.json", type: "application/json"}]
|
|
}
|
|
]
|
|
}
|
|
|
|
conn
|
|
|> put_resp_content_type("application/linkset+json")
|
|
|> send_resp(200, Jason.encode!(body))
|
|
end
|
|
|
|
def ready(conn, _params) do
|
|
# Simple readiness check without database dependency
|
|
# This is faster for startup health checks
|
|
version = :aprsme |> Application.spec(:vsn) |> List.to_string()
|
|
|
|
json(conn, %{
|
|
status: "ready",
|
|
version: version,
|
|
timestamp: DateTime.utc_now()
|
|
})
|
|
end
|
|
|
|
def status_json(conn, _params) do
|
|
# Get cluster-wide APRS-IS connection status
|
|
aprs_status = LeaderElection.get_cluster_aprs_status()
|
|
|
|
# Get application version
|
|
version = :aprsme |> Application.spec(:vsn) |> List.to_string()
|
|
|
|
# Calculate uptime in a human-readable format
|
|
uptime_display = format_uptime(aprs_status.uptime_seconds)
|
|
|
|
json(conn, %{
|
|
aprs_is: %{
|
|
connected: aprs_status.connected,
|
|
server: aprs_status.server,
|
|
port: aprs_status.port,
|
|
connected_at: aprs_status.connected_at,
|
|
uptime_seconds: aprs_status.uptime_seconds,
|
|
uptime_display: uptime_display,
|
|
login_id: aprs_status.login_id,
|
|
filter: aprs_status.filter
|
|
},
|
|
application: %{
|
|
version: version,
|
|
timestamp: DateTime.utc_now()
|
|
}
|
|
})
|
|
end
|
|
|
|
defp format_uptime(seconds) when seconds <= 0, do: "Not connected"
|
|
|
|
defp format_uptime(seconds) do
|
|
days = div(seconds, 86_400)
|
|
hours = div(rem(seconds, 86_400), 3600)
|
|
minutes = div(rem(seconds, 3600), 60)
|
|
secs = rem(seconds, 60)
|
|
|
|
cond do
|
|
days > 0 -> "#{days}d #{hours}h #{minutes}m #{secs}s"
|
|
hours > 0 -> "#{hours}h #{minutes}m #{secs}s"
|
|
minutes > 0 -> "#{minutes}m #{secs}s"
|
|
true -> "#{secs}s"
|
|
end
|
|
end
|
|
end
|