aprs.me/lib/aprsme/devices_seeder.ex
Graham McIntire 709780ff24
Fix potential crashes and unsafe operations throughout the app
- Fixed File.read crash in devices_seeder.ex with proper error handling
- Added timeout to TCP connect in aprs_is_connection.ex (10s timeout)
- Fixed unsafe socket send operations with error handling
- Added Process.alive? check before sending to PIDs in streaming_packets_pubsub.ex
- Fixed stream materialization memory issue in packet_consumer.ex
- Added error handling for datetime parsing errors
- Prevented crashes from dead processes and closed sockets

These fixes ensure the application is more resilient to:
- Missing files
- Network timeouts
- Dead processes
- Closed sockets
- Invalid data parsing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-28 08:35:12 -05:00

65 lines
1.5 KiB
Elixir

defmodule Aprsme.DevicesSeeder do
@moduledoc false
alias Aprsme.Devices
alias Aprsme.Repo
def seed_from_json(path \\ "test/support/test_devices.json") do
case File.read(path) do
{:ok, body} ->
case Jason.decode(body) do
{:ok, json} ->
seed_from_decoded_json(json)
{:error, reason} ->
{:error, "Failed to decode JSON: #{inspect(reason)}"}
end
{:error, reason} ->
{:error, "Failed to read file #{path}: #{inspect(reason)}"}
end
end
defp seed_from_decoded_json(json) do
tocalls = Map.get(json, "tocalls", %{})
mice = Map.get(json, "mice", %{})
micelegacy = Map.get(json, "micelegacy", %{})
now = DateTime.utc_now()
Repo.delete_all(Devices)
Enum.each([tocalls, mice, micelegacy], fn group ->
seed_device_group(group, now)
end)
{:ok, :seeded}
end
defp seed_device_group(group, now) do
Enum.each(group, fn {identifier, attrs} ->
insert_device(identifier, attrs, now)
end)
end
defp insert_device(identifier, attrs, now) do
attrs =
attrs
|> Map.put("identifier", identifier)
|> Map.update("features", nil, fn f ->
if is_list(f), do: f, else: [f]
end)
|> Map.put("updated_at", now)
%Devices{}
|> Devices.changeset(attrs)
|> Ecto.Changeset.apply_changes()
|> Map.from_struct()
|> then(fn map ->
Repo.insert!(
Devices.changeset(%Devices{}, map),
on_conflict: :replace_all,
conflict_target: :identifier
)
end)
end
end