diff --git a/elixir/apps/domain/lib/domain/clients.ex b/elixir/apps/domain/lib/domain/clients.ex index 1126cfe91..4b88a6569 100644 --- a/elixir/apps/domain/lib/domain/clients.ex +++ b/elixir/apps/domain/lib/domain/clients.ex @@ -39,6 +39,15 @@ defmodule Domain.Clients do |> Repo.aggregate(:count) end + def count_incompatible_for(account, gateway_version) do + Client.Query.not_deleted() + |> Client.Query.by_account_id(account.id) + |> Client.Query.by_last_seen_within(1, "week") + |> Client.Query.by_incompatible_for(gateway_version) + |> Client.Query.only_for_active_actors() + |> Repo.aggregate(:count) + end + def fetch_client_by_id(id, preload: :identity) do Client.Query.not_deleted() |> Client.Query.by_id(id) diff --git a/elixir/apps/domain/lib/domain/clients/client/query.ex b/elixir/apps/domain/lib/domain/clients/client/query.ex index 3a7227e7f..f5de0782b 100644 --- a/elixir/apps/domain/lib/domain/clients/client/query.ex +++ b/elixir/apps/domain/lib/domain/clients/client/query.ex @@ -54,6 +54,19 @@ defmodule Domain.Clients.Client.Query do }) end + def by_incompatible_for(queryable, gateway_version) do + %{major: g_major, minor: g_minor} = Version.parse!(gateway_version) + + # Incompatible if majors differ or client is two or more minors behind + where( + queryable, + [clients: clients], + fragment("split_part(?, '.', 1)::int", clients.last_seen_version) < ^g_major or + (fragment("split_part(?, '.', 1)::int", clients.last_seen_version) == ^g_major and + fragment("split_part(?, '.', 2)::int", clients.last_seen_version) <= ^(g_minor - 2)) + ) + end + def returning_not_deleted(queryable) do select(queryable, [clients: clients], clients) end diff --git a/elixir/apps/domain/lib/domain/component_versions.ex b/elixir/apps/domain/lib/domain/component_versions.ex index 73ef7b58d..24ae84210 100644 --- a/elixir/apps/domain/lib/domain/component_versions.ex +++ b/elixir/apps/domain/lib/domain/component_versions.ex @@ -1,5 +1,5 @@ defmodule Domain.ComponentVersions do - alias Domain.ComponentVersions + alias Domain.{Actors.Actor, Clients.Client, ComponentVersions} use Supervisor require Logger @@ -34,6 +34,12 @@ defmodule Domain.ComponentVersions do ComponentVersions.component_version(:gateway) end + def client_version(%Client{} = client) do + client + |> get_component_type() + |> component_version() + end + def component_version(component) do Domain.Config.get_env(:domain, ComponentVersions, []) |> Keyword.get(:versions, []) @@ -85,4 +91,14 @@ defmodule Domain.ComponentVersions do |> Keyword.fetch!(:versions) end end + + defp get_component_type(%Client{last_seen_user_agent: "Mac OS" <> _rest}), do: :apple + defp get_component_type(%Client{last_seen_user_agent: "iOS" <> _rest}), do: :apple + + defp get_component_type(%Client{last_seen_user_agent: "Android" <> _rest}), + do: :android + + defp get_component_type(%Client{actor: %Actor{type: :service_account}}), do: :headless + + defp get_component_type(_), do: :gui end diff --git a/elixir/apps/domain/lib/domain/mailer/notifications.ex b/elixir/apps/domain/lib/domain/mailer/notifications.ex index bd7773bca..604321a91 100644 --- a/elixir/apps/domain/lib/domain/mailer/notifications.ex +++ b/elixir/apps/domain/lib/domain/mailer/notifications.ex @@ -2,17 +2,26 @@ defmodule Domain.Mailer.Notifications do import Swoosh.Email import Domain.Mailer import Phoenix.Template, only: [embed_templates: 2] + import Phoenix.VerifiedRoutes + + @endpoint Web.Endpoint + @router Web.Router embed_templates "notifications/*.html", suffix: "_html" embed_templates "notifications/*.text", suffix: "_text" - def outdated_gateway_email(account, gateways, email) do + def outdated_gateway_email(account, gateways, incompatible_client_count, email) do + outdated_clients_url = + url(~p"/#{account.id}/clients?#{[clients_order_by: "clients:asc:last_seen_version"]}") + default_email() |> subject("Firezone Gateway Upgrade Available") |> to(email) |> render_body(__MODULE__, :outdated_gateway, account: account, gateways: gateways, + outdated_clients_url: outdated_clients_url, + incompatible_client_count: incompatible_client_count, latest_version: Domain.ComponentVersions.gateway_version() ) end diff --git a/elixir/apps/domain/lib/domain/mailer/notifications/outdated_gateway.html.eex b/elixir/apps/domain/lib/domain/mailer/notifications/outdated_gateway.html.eex index 21ec97218..e1eb4874f 100644 --- a/elixir/apps/domain/lib/domain/mailer/notifications/outdated_gateway.html.eex +++ b/elixir/apps/domain/lib/domain/mailer/notifications/outdated_gateway.html.eex @@ -19,7 +19,7 @@ td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;} - Firezone Gateway Update Available + Firezone Gateway Upgrade Available