From cc4578653f9339affd9b50a3155905e794ea52a3 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Wed, 22 Oct 2025 16:19:17 -0500 Subject: [PATCH] cleanup and remove redis --- lib/aprsme/application.ex | 9 +- lib/aprsme/cache.ex | 71 +---- lib/aprsme/redis_cache.ex | 251 ------------------ lib/aprsme_web/live/map_live/data_builder.ex | 5 +- .../live/map_live/historical_loader.ex | 5 +- lib/aprsme_web/plugs/api_csrf.ex | 2 +- vendor/aprs | 2 +- 7 files changed, 21 insertions(+), 324 deletions(-) delete mode 100644 lib/aprsme/redis_cache.ex diff --git a/lib/aprsme/application.ex b/lib/aprsme/application.ex index 346e6bb..ac0297c 100644 --- a/lib/aprsme/application.ex +++ b/lib/aprsme/application.ex @@ -240,14 +240,7 @@ defmodule Aprsme.Application do Logger.info("Starting Redis-based caching and rate limiting") - [ - # Redis-based rate limiter - Aprsme.RedisRateLimiter, - # Redis-based caches - {Aprsme.RedisCache, name: :query_cache}, - {Aprsme.RedisCache, name: :device_cache}, - {Aprsme.RedisCache, name: :symbol_cache} - ] + [] else require Logger diff --git a/lib/aprsme/cache.ex b/lib/aprsme/cache.ex index 9c32547..c1b1a06 100644 --- a/lib/aprsme/cache.ex +++ b/lib/aprsme/cache.ex @@ -8,70 +8,44 @@ defmodule Aprsme.Cache do Get a value from cache """ def get(cache_name, key) do - if using_redis?() do - Aprsme.RedisCache.get(cache_name, key) - else - Cachex.get(cache_name, key) - end + Cachex.get(cache_name, key) end @doc """ Put a value in cache with optional TTL """ def put(cache_name, key, value, opts \\ []) do - if using_redis?() do - # Convert TTL from milliseconds to seconds for Redis, preserving sub-second values - opts = convert_ttl_to_seconds(opts) - Aprsme.RedisCache.put(cache_name, key, value, opts) - else - Cachex.put(cache_name, key, value, opts) - end + Cachex.put(cache_name, key, value, opts) end @doc """ Delete a key from cache """ def del(cache_name, key) do - if using_redis?() do - Aprsme.RedisCache.del(cache_name, key) - else - Cachex.del(cache_name, key) - end + Cachex.del(cache_name, key) end @doc """ Clear all keys from cache """ def clear(cache_name) do - if using_redis?() do - Aprsme.RedisCache.clear(cache_name) - else - Cachex.clear(cache_name) - end + Cachex.clear(cache_name) end @doc """ Get cache statistics """ def stats(cache_name) do - if using_redis?() do - Aprsme.RedisCache.stats(cache_name) - else - Cachex.stats(cache_name) - end + Cachex.stats(cache_name) end @doc """ Check if key exists """ def exists?(cache_name, key) do - if using_redis?() do - Aprsme.RedisCache.exists?(cache_name, key) - else - case Cachex.exists?(cache_name, key) do - {:ok, exists?} -> exists? - {:error, _reason} -> false - end + case Cachex.exists?(cache_name, key) do + {:ok, exists?} -> exists? + {:error, _reason} -> false end end @@ -79,35 +53,10 @@ defmodule Aprsme.Cache do Get TTL for a key """ def ttl(cache_name, key) do - if using_redis?() do - Aprsme.RedisCache.ttl(cache_name, key) - else - Cachex.ttl(cache_name, key) - end + Cachex.ttl(cache_name, key) end - # Helper functions - - defp using_redis? do - System.get_env("REDIS_URL") != nil - end - - defp convert_ttl_to_seconds(opts) do - case Keyword.get(opts, :ttl) do - nil -> - opts - - ttl_ms when is_integer(ttl_ms) and ttl_ms > 0 -> - ttl_seconds = ttl_ms |> Integer.ceil_div(1000) |> max(1) - Keyword.put(opts, :ttl, ttl_seconds) - - ttl_ms when is_integer(ttl_ms) -> - Keyword.put(opts, :ttl, ttl_ms) - - _ -> - opts - end - end + # Helper functions - no longer needed as we only use Cachex @doc """ Convert timeout keyword list to milliseconds diff --git a/lib/aprsme/redis_cache.ex b/lib/aprsme/redis_cache.ex deleted file mode 100644 index 54e4bea..0000000 --- a/lib/aprsme/redis_cache.ex +++ /dev/null @@ -1,251 +0,0 @@ -defmodule Aprsme.RedisCache do - @moduledoc """ - Redis-backed distributed cache implementation. - Provides a similar API to Cachex but uses Redis for distributed caching. - """ - - require Logger - - # 5 minutes in seconds - @default_ttl 300 - # 5 seconds timeout for Redis operations - @redis_timeout 5000 - - def child_spec(opts) do - name = Keyword.fetch!(opts, :name) - - children = [ - {Redix, name: redis_name(name), host: redis_host(), port: redis_port(), password: redis_password()} - ] - - %{ - id: {__MODULE__, name}, - type: :supervisor, - start: {Supervisor, :start_link, [children, [strategy: :one_for_one, name: :"#{name}_redis_supervisor"]]} - } - end - - @doc """ - Get a value from the cache - """ - def get(cache_name, key) do - redis_key = make_redis_key(cache_name, key) - - case Redix.command(redis_name(cache_name), ["GET", redis_key], timeout: @redis_timeout) do - {:ok, nil} -> - {:ok, nil} - - {:ok, value} -> - {:ok, deserialize(value)} - - {:error, reason} -> - Logger.error("Redis GET error for #{redis_key}: #{inspect(reason)}") - {:error, reason} - end - end - - @doc """ - Put a value in the cache with optional TTL - """ - def put(cache_name, key, value, opts \\ []) do - redis_key = make_redis_key(cache_name, key) - ttl = Keyword.get(opts, :ttl, @default_ttl) - serialized = serialize(value) - - case Redix.command(redis_name(cache_name), ["SETEX", redis_key, ttl, serialized], timeout: @redis_timeout) do - {:ok, "OK"} -> - {:ok, true} - - {:error, reason} -> - Logger.error("Redis SETEX error for #{redis_key}: #{inspect(reason)}") - {:error, reason} - end - end - - @doc """ - Delete a value from the cache - """ - def del(cache_name, key) do - redis_key = make_redis_key(cache_name, key) - - case Redix.command(redis_name(cache_name), ["DEL", redis_key], timeout: @redis_timeout) do - {:ok, _} -> - {:ok, true} - - {:error, reason} -> - Logger.error("Redis DEL error for #{redis_key}: #{inspect(reason)}") - {:error, reason} - end - end - - @doc """ - Delete multiple keys matching a pattern - """ - def del_pattern(cache_name, pattern) do - redis_pattern = make_redis_key(cache_name, pattern) - - # Use SCAN to find keys matching pattern - case scan_keys(cache_name, redis_pattern) do - {:ok, keys} when keys != [] -> - case Redix.command(redis_name(cache_name), ["DEL" | keys], timeout: @redis_timeout) do - {:ok, count} -> - {:ok, count} - - {:error, reason} -> - Logger.error("Redis DEL error for pattern #{redis_pattern}: #{inspect(reason)}") - {:error, reason} - end - - {:ok, []} -> - {:ok, 0} - - {:error, reason} -> - {:error, reason} - end - end - - @doc """ - Clear all keys for a cache - """ - def clear(cache_name) do - pattern = make_redis_key(cache_name, "*") - - case scan_keys(cache_name, pattern) do - {:ok, keys} when keys != [] -> - case Redix.pipeline(redis_name(cache_name), Enum.map(keys, &["DEL", &1]), timeout: @redis_timeout) do - {:ok, _results} -> - {:ok, true} - - {:error, reason} -> - Logger.error("Redis CLEAR error for #{cache_name}: #{inspect(reason)}") - {:error, reason} - end - - {:ok, []} -> - {:ok, true} - - {:error, reason} -> - {:error, reason} - end - end - - @doc """ - Get cache statistics - """ - def stats(cache_name) do - pattern = make_redis_key(cache_name, "*") - - case scan_keys(cache_name, pattern) do - {:ok, keys} -> - {:ok, - %{ - size: length(keys), - keys: keys - }} - - {:error, reason} -> - {:error, reason} - end - end - - @doc """ - Check if a key exists - """ - def exists?(cache_name, key) do - redis_key = make_redis_key(cache_name, key) - - case Redix.command(redis_name(cache_name), ["EXISTS", redis_key], timeout: @redis_timeout) do - {:ok, 1} -> true - {:ok, 0} -> false - {:error, _} -> false - end - end - - @doc """ - Get remaining TTL for a key - """ - def ttl(cache_name, key) do - redis_key = make_redis_key(cache_name, key) - - case Redix.command(redis_name(cache_name), ["TTL", redis_key], timeout: @redis_timeout) do - # Key doesn't exist - {:ok, -2} -> {:ok, nil} - # Key exists but has no TTL - {:ok, -1} -> {:ok, :infinity} - {:ok, seconds} -> {:ok, seconds} - {:error, reason} -> {:error, reason} - end - end - - # Private functions - - defp redis_name(cache_name), do: :"#{cache_name}_redis" - - defp make_redis_key(cache_name, key) when is_binary(key) do - "aprsme:#{cache_name}:#{key}" - end - - defp make_redis_key(cache_name, key) do - "aprsme:#{cache_name}:#{:erlang.phash2(key)}" - end - - defp serialize(value) do - :erlang.term_to_binary(value) - end - - defp deserialize(binary) when is_binary(binary) do - :erlang.binary_to_term(binary) - rescue - _ -> nil - end - - defp scan_keys(cache_name, pattern) do - scan_keys(cache_name, pattern, "0", []) - end - - defp scan_keys(cache_name, pattern, cursor, acc) do - case Redix.command(redis_name(cache_name), ["SCAN", cursor, "MATCH", pattern, "COUNT", "100"], - timeout: @redis_timeout - ) do - {:ok, [new_cursor, keys]} -> - new_acc = acc ++ keys - - if new_cursor == "0" do - {:ok, new_acc} - else - scan_keys(cache_name, pattern, new_cursor, new_acc) - end - - {:error, reason} -> - {:error, reason} - end - end - - defp redis_host do - redis_url = System.get_env("REDIS_URL", "redis://localhost:6379") - uri = URI.parse(redis_url) - uri.host || "localhost" - end - - defp redis_port do - redis_url = System.get_env("REDIS_URL", "redis://localhost:6379") - uri = URI.parse(redis_url) - uri.port || 6379 - end - - defp redis_password do - redis_url = System.get_env("REDIS_URL", "redis://localhost:6379") - uri = URI.parse(redis_url) - - case uri.userinfo do - nil -> - nil - - userinfo -> - case String.split(userinfo, ":") do - [_, password] -> password - _ -> nil - end - end - end -end diff --git a/lib/aprsme_web/live/map_live/data_builder.ex b/lib/aprsme_web/live/map_live/data_builder.ex index 28f3592..a914e7f 100644 --- a/lib/aprsme_web/live/map_live/data_builder.ex +++ b/lib/aprsme_web/live/map_live/data_builder.ex @@ -134,11 +134,14 @@ defmodule AprsmeWeb.MapLive.DataBuilder do @spec valid_coordinates?(any(), any()) :: boolean() defp valid_coordinates?(lat, lon) when is_number(lat) and is_number(lon) do lat >= -90 and lat <= 90 and lon >= -180 and lon <= 180 and - :math.is_finite(lat) and :math.is_finite(lon) + is_finite(lat) and is_finite(lon) end defp valid_coordinates?(_, _), do: false + defp is_finite(n) when is_float(n), do: n != :infinity and n != :neg_infinity and n == n + defp is_finite(n) when is_integer(n), do: true + @doc """ Build packet data list for historical display. Moved from historical_loader.ex. diff --git a/lib/aprsme_web/live/map_live/historical_loader.ex b/lib/aprsme_web/live/map_live/historical_loader.ex index 5495408..88423ac 100644 --- a/lib/aprsme_web/live/map_live/historical_loader.ex +++ b/lib/aprsme_web/live/map_live/historical_loader.ex @@ -223,7 +223,7 @@ defmodule AprsmeWeb.MapLive.HistoricalLoader do is_number(lat) and is_number(lon) and lat >= -90 and lat <= 90 and lon >= -180 and lon <= 180 and - :math.is_finite(lat) and :math.is_finite(lon) + is_finite(lat) and is_finite(lon) end) if length(valid_packets) < length(historical_packets) do @@ -430,4 +430,7 @@ defmodule AprsmeWeb.MapLive.HistoricalLoader do socket end + + defp is_finite(n) when is_float(n), do: n != :infinity and n != :neg_infinity and n == n + defp is_finite(n) when is_integer(n), do: true end diff --git a/lib/aprsme_web/plugs/api_csrf.ex b/lib/aprsme_web/plugs/api_csrf.ex index 2c38aaa..f994273 100644 --- a/lib/aprsme_web/plugs/api_csrf.ex +++ b/lib/aprsme_web/plugs/api_csrf.ex @@ -50,7 +50,7 @@ defmodule AprsmeWeb.Plugs.ApiCSRF do reject_request(conn) session_token -> - if Plug.CSRFProtection.verify_csrf_token(token, session_token) do + if Plug.CSRFProtection.valid_state_and_csrf_token?(session_token, token) do conn else reject_request(conn) diff --git a/vendor/aprs b/vendor/aprs index 0d0d21d..84a410d 160000 --- a/vendor/aprs +++ b/vendor/aprs @@ -1 +1 @@ -Subproject commit 0d0d21d9e9a342abdb486a4bc8650542df9e38ed +Subproject commit 84a410daf78f720af03701c6ab5b24fdf7cfd0b3