From 8d5bfe127efb292164530a1b8cdcd59a07feeaf8 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 18 Jul 2025 10:15:09 -0500 Subject: [PATCH] add email --- config/dev.exs | 4 ++++ config/runtime.exs | 21 +++++++++++---------- lib/aprsme/packet_consumer.ex | 20 ++++++++++++++++---- mix.exs | 1 + mix.lock | 2 ++ 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/config/dev.exs b/config/dev.exs index 9c2dc15..c1d1a2c 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -71,6 +71,10 @@ config :aprsme, AprsmeWeb.Endpoint, # Run `mix help phx.gen.cert` for more information. config :aprsme, dev_routes: true +# Mailer configuration for development +# Using Swoosh.Adapters.Local from config.exs which stores emails locally +# Access sent emails at http://localhost:4000/dev/mailbox + # Configure Hammer for development environment config :hammer, backend: {Hammer.Backend.ETS, [expiry_ms: 60_000 * 60 * 4, cleanup_interval_ms: 60_000 * 10]} diff --git a/config/runtime.exs b/config/runtime.exs index 27a9d75..58bb1c7 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -46,6 +46,13 @@ if config_env() == :prod do host = System.get_env("PHX_HOST") || "example.com" port = String.to_integer(System.get_env("PORT") || "4000") + # ## Configuring the mailer + # + # Configure Resend for email delivery + config :aprsme, Aprsme.Mailer, + adapter: Resend.Swoosh.Adapter, + api_key: System.get_env("RESEND_API_KEY") + config :aprsme, Aprsme.Repo, # ssl: true, url: database_url, @@ -99,6 +106,10 @@ if config_env() == :prod do server: true, check_origin: ["https://#{host}", "http://10.0.19.222:33897", "https://s.aprs.me"] + # Optional: Set the default "from" email address + config :aprsme, + default_from_email: "w5isp@aprs.me" + config :aprsme, ecto_repos: [Aprsme.Repo], aprs_is_server: System.get_env("APRS_SERVER", "dallas.aprs2.net"), @@ -127,16 +138,6 @@ if config_env() == :prod do ] ] - # ## Configuring the mailer - # - # In production you need to configure the mailer to use a different adapter. - # Also, you may need to configure the Swoosh API client of your choice if you - # are not using SMTP. Here is an example of the configuration: - # - # config :aprsme, Aprsme.Mailer, - # adapter: Swoosh.Adapters.Mailgun, - # api_key: System.get_env("MAILGUN_API_KEY"), - # domain: System.get_env("MAILGUN_DOMAIN") # # For this example you need include a HTTP client required by Swoosh API client. # Swoosh supports Hackney, Finch, and Req out of the box: diff --git a/lib/aprsme/packet_consumer.ex b/lib/aprsme/packet_consumer.ex index 6eaa2ce..3c8fcb8 100644 --- a/lib/aprsme/packet_consumer.ex +++ b/lib/aprsme/packet_consumer.ex @@ -97,7 +97,7 @@ defmodule Aprsme.PacketConsumer do require Logger # Monitor memory usage before processing - {memory_before, _} = :erlang.statistics(:runtime) + memory_before = :erlang.memory(:total) start_time = System.monotonic_time(:millisecond) # Chunk size optimized for PostgreSQL work_mem=16MB @@ -118,24 +118,36 @@ defmodule Aprsme.PacketConsumer do duration = end_time - start_time # Monitor memory usage after processing - {memory_after, _} = :erlang.statistics(:runtime) + memory_after = :erlang.memory(:total) memory_diff = memory_after - memory_before + # Get current process memory info + process_info = Process.info(self(), [:memory, :heap_size, :total_heap_size]) + # Force garbage collection if memory usage is high - # 50MB threshold - if memory_diff > 50_000 do + # 10MB threshold (more aggressive than previous 50MB) + # Also check process memory independently + if memory_diff > 10_485_760 or process_info[:memory] > 20_971_520 do :erlang.garbage_collect() Logger.warning("High memory usage detected, forced garbage collection", memory_info: LogSanitizer.log_data( memory_diff_bytes: memory_diff, + process_memory: process_info[:memory], + heap_size: process_info[:heap_size], + total_heap_size: process_info[:total_heap_size], batch_size: length(packets), gc_forced: true ) ) end + # Always do minor GC after large batches to prevent memory accumulation + if length(packets) > 500 do + :erlang.garbage_collect(self(), type: :minor) + end + :telemetry.execute( [ :aprsme, diff --git a/mix.exs b/mix.exs index bf93440..3c7502a 100644 --- a/mix.exs +++ b/mix.exs @@ -72,6 +72,7 @@ defmodule Aprsme.MixProject do {:phoenix_live_view, "~> 1.0.17"}, {:postgrex, ">= 0.0.0"}, {:swoosh, "~> 1.16"}, + {:resend, "~> 0.4.1"}, {:telemetry_metrics, "~> 1.0"}, {:telemetry_poller, "~> 1.0"}, aprs_dep(), diff --git a/mix.lock b/mix.lock index 7f6f2b9..1d6d27e 100644 --- a/mix.lock +++ b/mix.lock @@ -71,6 +71,7 @@ "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, "postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"}, "req": {:hex, :req, "0.5.15", "662020efb6ea60b9f0e0fac9be88cd7558b53fe51155a2d9899de594f9906ba9", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "a6513a35fad65467893ced9785457e91693352c70b58bbc045b47e5eb2ef0c53"}, + "resend": {:hex, :resend, "0.4.4", "f80c6efb6f5d1043f57a1ae1e02cd366a562cac6338097aca38675c7ebe94156", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.3", [hex: :swoosh, repo: "hexpm", optional: false]}, {:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "7dab4e8ae395e8ea984e4402988cc9e02b13739707deef127d8f14d181cb88ed"}, "rewrite": {:hex, :rewrite, "1.1.2", "f5a5d10f5fed1491a6ff48e078d4585882695962ccc9e6c779bae025d1f92eda", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "7f8b94b1e3528d0a47b3e8b7bfeca559d2948a65fa7418a9ad7d7712703d39d4"}, "sentry": {:hex, :sentry, "11.0.1", "e4e0ae12a74c59639808442a79d7ddd224fd5987fb9a14b35a1d01d9606117a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0 or ~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.5", [hex: :opentelemetry, repo: "hexpm", optional: true]}, {:opentelemetry_api, "~> 1.4", [hex: :opentelemetry_api, repo: "hexpm", optional: true]}, {:opentelemetry_exporter, "~> 1.0", [hex: :opentelemetry_exporter, repo: "hexpm", optional: true]}, {:opentelemetry_semantic_conventions, "~> 1.27", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "ef12b6ee61c24bee3807eee92563567c89f5eec954d6bb913eb797e04625a613"}, "sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"}, @@ -86,6 +87,7 @@ "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_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.3", "b27ba2814cc08b5c4eb5f0245120198542cd023f575c490fa14447ae6763ea8d", [: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.21", [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", "4f419aa2ab908cff43a117d3b5de99edad8fd54690211cbbdc7d2941c03a1458"}, "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"}, "thousand_island": {:hex, :thousand_island, "1.3.14", "ad45ebed2577b5437582bcc79c5eccd1e2a8c326abf6a3464ab6c06e2055a34a", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0d24a929d31cdd1d7903a4fe7f2409afeedff092d277be604966cd6aa4307ef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},