diff --git a/.formatter.exs b/.formatter.exs index ef8840ce..18b26c52 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,6 +1,6 @@ [ import_deps: [:ecto, :ecto_sql, :phoenix], subdirectories: ["priv/*/migrations"], - plugins: [Phoenix.LiveView.HTMLFormatter], + plugins: [Styler, Phoenix.LiveView.HTMLFormatter], inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"] ] diff --git a/config/config.exs b/config/config.exs index a1a6c5fa..3f7a0951 100644 --- a/config/config.exs +++ b/config/config.exs @@ -7,30 +7,6 @@ # General application configuration import Config -config :towerops, - ecto_repos: [Towerops.Repo], - generators: [timestamp_type: :utc_datetime, binary_id: true] - -# Configure the endpoint -config :towerops, ToweropsWeb.Endpoint, - url: [host: "localhost"], - adapter: Bandit.PhoenixAdapter, - render_errors: [ - formats: [html: ToweropsWeb.ErrorHTML, json: ToweropsWeb.ErrorJSON], - layout: false - ], - pubsub_server: Towerops.PubSub, - live_view: [signing_salt: "Uh1ABfdI"] - -# Configure the mailer -# -# By default it uses the "Local" adapter which stores the emails -# locally. You can see the emails in your browser, at "/dev/mailbox". -# -# For production it's recommended to configure a different adapter -# at the `config/runtime.exs`. -config :towerops, Towerops.Mailer, adapter: Swoosh.Adapters.Local - # Configure esbuild (the version is required) config :esbuild, version: "0.25.4", @@ -41,6 +17,14 @@ config :esbuild, env: %{"NODE_PATH" => [Path.expand("../deps", __DIR__), Mix.Project.build_path()]} ] +# Configure Elixir's Logger +config :logger, :default_formatter, + format: "$time $metadata[$level] $message\n", + metadata: [:request_id] + +# Use Jason for JSON parsing in Phoenix +config :phoenix, :json_library, Jason + # Configure tailwind (the version is required) config :tailwind, version: "4.1.12", @@ -52,14 +36,30 @@ config :tailwind, cd: Path.expand("..", __DIR__) ] -# Configure Elixir's Logger -config :logger, :default_formatter, - format: "$time $metadata[$level] $message\n", - metadata: [:request_id] +# Configure the mailer +# +# By default it uses the "Local" adapter which stores the emails +# locally. You can see the emails in your browser, at "/dev/mailbox". +# +# For production it's recommended to configure a different adapter +# at the `config/runtime.exs`. +config :towerops, Towerops.Mailer, adapter: Swoosh.Adapters.Local -# Use Jason for JSON parsing in Phoenix -config :phoenix, :json_library, Jason +# Configure the endpoint +config :towerops, ToweropsWeb.Endpoint, + url: [host: "localhost"], + adapter: Bandit.PhoenixAdapter, + render_errors: [ + formats: [html: ToweropsWeb.ErrorHTML, json: ToweropsWeb.ErrorJSON], + layout: false + ], + pubsub_server: Towerops.PubSub, + live_view: [signing_salt: "Uh1ABfdI"] # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. +config :towerops, + ecto_repos: [Towerops.Repo], + generators: [timestamp_type: :utc_datetime, binary_id: true] + import_config "#{config_env()}.exs" diff --git a/config/dev.exs b/config/dev.exs index f3a28a05..72be9387 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -1,5 +1,26 @@ import Config +# Do not include metadata nor timestamps in development logs +config :logger, :default_formatter, format: "[$level] $message\n" + +# Initialize plugs at runtime for faster development compilation +config :phoenix, :plug_init_mode, :runtime + +# Set a higher stacktrace during development. Avoid configuring such +# in production as building large stacktraces may be expensive. +config :phoenix, :stacktrace_depth, 20 + +config :phoenix_live_view, + # Include debug annotations and locations in rendered markup. + # Changing this configuration will require mix clean and a full recompile. + debug_heex_annotations: true, + debug_attributes: true, + # Enable helpful, but potentially expensive runtime checks + enable_expensive_runtime_checks: true + +# Disable swoosh api client as it is only required for production adapters. +config :swoosh, :api_client, false + # Configure your database config :towerops, Towerops.Repo, username: "postgres", @@ -69,24 +90,3 @@ config :towerops, ToweropsWeb.Endpoint, # Enable dev routes for dashboard and mailbox config :towerops, dev_routes: true - -# Do not include metadata nor timestamps in development logs -config :logger, :default_formatter, format: "[$level] $message\n" - -# Set a higher stacktrace during development. Avoid configuring such -# in production as building large stacktraces may be expensive. -config :phoenix, :stacktrace_depth, 20 - -# Initialize plugs at runtime for faster development compilation -config :phoenix, :plug_init_mode, :runtime - -config :phoenix_live_view, - # Include debug annotations and locations in rendered markup. - # Changing this configuration will require mix clean and a full recompile. - debug_heex_annotations: true, - debug_attributes: true, - # Enable helpful, but potentially expensive runtime checks - enable_expensive_runtime_checks: true - -# Disable swoosh api client as it is only required for production adapters. -config :swoosh, :api_client, false diff --git a/config/prod.exs b/config/prod.exs index cd26aebb..6fa0160a 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -1,5 +1,14 @@ import Config +# Do not print debug messages in production +config :logger, level: :info + +# Configure Swoosh API Client +config :swoosh, api_client: Swoosh.ApiClient.Req + +# Disable Swoosh Local Memory Storage +config :swoosh, local: false + # Note we also include the path to a cache manifest # containing the digested version of static files. This # manifest is generated by the `mix assets.deploy` task, @@ -14,17 +23,7 @@ config :towerops, ToweropsWeb.Endpoint, force_ssl: [rewrite_on: [:x_forwarded_proto]], exclude: [ # paths: ["/health"], + # Runtime production configuration, including reading + # of environment variables, is done on config/runtime.exs. hosts: ["localhost", "127.0.0.1"] ] - -# Configure Swoosh API Client -config :swoosh, api_client: Swoosh.ApiClient.Req - -# Disable Swoosh Local Memory Storage -config :swoosh, local: false - -# Do not print debug messages in production -config :logger, level: :info - -# Runtime production configuration, including reading -# of environment variables, is done on config/runtime.exs. diff --git a/config/runtime.exs b/config/runtime.exs index a4d6919f..36e82781 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -20,8 +20,7 @@ if System.get_env("PHX_SERVER") do config :towerops, ToweropsWeb.Endpoint, server: true end -config :towerops, ToweropsWeb.Endpoint, - http: [port: String.to_integer(System.get_env("PORT", "4000"))] +config :towerops, ToweropsWeb.Endpoint, http: [port: String.to_integer(System.get_env("PORT", "4000"))] if config_env() == :prod do database_url = @@ -33,14 +32,6 @@ if config_env() == :prod do maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: [] - config :towerops, Towerops.Repo, - # ssl: true, - url: database_url, - pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), - # For machines with several cores, consider starting multiple pools of `pool_size` - # pool_count: 4, - socket_options: maybe_ipv6 - # The secret key base is used to sign/encrypt cookies and other secrets. # A default value is used in config/dev.exs and config/test.exs but you # want to use a different value for prod and you most likely don't want @@ -55,7 +46,13 @@ if config_env() == :prod do host = System.get_env("PHX_HOST") || "example.com" - config :towerops, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY") + config :towerops, Towerops.Repo, + # ssl: true, + url: database_url, + pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), + # For machines with several cores, consider starting multiple pools of `pool_size` + # pool_count: 4, + socket_options: maybe_ipv6 config :towerops, ToweropsWeb.Endpoint, url: [host: host, port: 443, scheme: "https"], @@ -68,6 +65,8 @@ if config_env() == :prod do ], secret_key_base: secret_key_base + config :towerops, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY") + # ## SSL Support # # To get SSL working, you will need to add the `https` key diff --git a/config/test.exs b/config/test.exs index 59ad40f2..bcbd9ccc 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,5 +1,25 @@ import Config +# Print only warnings and errors during test +config :logger, level: :warning + +# Initialize plugs at runtime for faster test compilation +config :phoenix, :plug_init_mode, :runtime + +# Sort query params output of verified routes for robust url comparisons +config :phoenix, + sort_verified_routes_query_params: true + +# Enable helpful, but potentially expensive runtime checks +config :phoenix_live_view, + enable_expensive_runtime_checks: true + +# Disable swoosh api client as it is only required for production adapters +config :swoosh, :api_client, false + +# In test we don't send emails +config :towerops, Towerops.Mailer, adapter: Swoosh.Adapters.Test + # Configure your database # # The MIX_TEST_PARTITION environment variable can be used @@ -19,23 +39,3 @@ config :towerops, ToweropsWeb.Endpoint, http: [ip: {127, 0, 0, 1}, port: 4002], secret_key_base: "XDSSqVUtRXUjEfzxefIAaPIzBfonpNMOrCRyP1a0kjPzzyOpVNSRmMBVae/bwTqj", server: false - -# In test we don't send emails -config :towerops, Towerops.Mailer, adapter: Swoosh.Adapters.Test - -# Disable swoosh api client as it is only required for production adapters -config :swoosh, :api_client, false - -# Print only warnings and errors during test -config :logger, level: :warning - -# Initialize plugs at runtime for faster test compilation -config :phoenix, :plug_init_mode, :runtime - -# Enable helpful, but potentially expensive runtime checks -config :phoenix_live_view, - enable_expensive_runtime_checks: true - -# Sort query params output of verified routes for robust url comparisons -config :phoenix, - sort_verified_routes_query_params: true diff --git a/lib/towerops/mailer.ex b/lib/towerops/mailer.ex index fd1ea24a..5acbcc50 100644 --- a/lib/towerops/mailer.ex +++ b/lib/towerops/mailer.ex @@ -1,3 +1,4 @@ defmodule Towerops.Mailer do + @moduledoc false use Swoosh.Mailer, otp_app: :towerops end diff --git a/lib/towerops_web.ex b/lib/towerops_web.ex index a8ea5389..ab69e9c3 100644 --- a/lib/towerops_web.ex +++ b/lib/towerops_web.ex @@ -23,10 +23,11 @@ defmodule ToweropsWeb do quote do use Phoenix.Router, helpers: false - # Import common connection and controller functions to use in pipelines - import Plug.Conn import Phoenix.Controller import Phoenix.LiveView.Router + + # Import common connection and controller functions to use in pipelines + import Plug.Conn end end @@ -39,7 +40,6 @@ defmodule ToweropsWeb do def controller do quote do use Phoenix.Controller, formats: [:html, :json] - use Gettext, backend: ToweropsWeb.Gettext import Plug.Conn diff --git a/lib/towerops_web/components/core_components.ex b/lib/towerops_web/components/core_components.ex index 91c94864..c7165a71 100644 --- a/lib/towerops_web/components/core_components.ex +++ b/lib/towerops_web/components/core_components.ex @@ -29,6 +29,7 @@ defmodule ToweropsWeb.CoreComponents do use Phoenix.Component use Gettext, backend: ToweropsWeb.Gettext + alias Phoenix.HTML.FormField alias Phoenix.LiveView.JS @doc """ @@ -166,8 +167,7 @@ defmodule ToweropsWeb.CoreComponents do values: ~w(checkbox color date datetime-local email file month number password search select tel text textarea time url week hidden) - attr :field, Phoenix.HTML.FormField, - doc: "a form field struct retrieved from the form, for example: @form[:email]" + attr :field, FormField, doc: "a form field struct retrieved from the form, for example: @form[:email]" attr :errors, :list, default: [] attr :checked, :boolean, doc: "the checked flag for checkbox inputs" @@ -177,11 +177,10 @@ defmodule ToweropsWeb.CoreComponents do attr :class, :any, default: nil, doc: "the input class to use over defaults" attr :error_class, :any, default: nil, doc: "the input error class to use over defaults" - attr :rest, :global, - include: ~w(accept autocomplete capture cols disabled form list max maxlength min minlength + attr :rest, :global, include: ~w(accept autocomplete capture cols disabled form list max maxlength min minlength multiple pattern placeholder readonly required rows size step) - def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do + def input(%{field: %FormField{} = field} = assigns) do errors = if Phoenix.Component.used_input?(field), do: field.errors, else: [] assigns @@ -452,8 +451,7 @@ defmodule ToweropsWeb.CoreComponents do to: selector, time: 300, transition: - {"transition-all ease-out duration-300", - "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95", + {"transition-all ease-out duration-300", "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95", "opacity-100 translate-y-0 sm:scale-100"} ) end diff --git a/lib/towerops_web/telemetry.ex b/lib/towerops_web/telemetry.ex index abf8f5ce..01b40bfe 100644 --- a/lib/towerops_web/telemetry.ex +++ b/lib/towerops_web/telemetry.ex @@ -1,5 +1,7 @@ defmodule ToweropsWeb.Telemetry do + @moduledoc false use Supervisor + import Telemetry.Metrics def start_link(arg) do @@ -71,8 +73,7 @@ defmodule ToweropsWeb.Telemetry do ), summary("towerops.repo.query.idle_time", unit: {:native, :millisecond}, - description: - "The time the connection spent waiting before being checked out for the query" + description: "The time the connection spent waiting before being checked out for the query" ), # VM Metrics diff --git a/mix.exs b/mix.exs index 39d73239..1bb804a7 100644 --- a/mix.exs +++ b/mix.exs @@ -52,12 +52,7 @@ defmodule Towerops.MixProject do {:esbuild, "~> 0.10", runtime: Mix.env() == :dev}, {:tailwind, "~> 0.3", runtime: Mix.env() == :dev}, {:heroicons, - github: "tailwindlabs/heroicons", - tag: "v2.2.0", - sparse: "optimized", - app: false, - compile: false, - depth: 1}, + github: "tailwindlabs/heroicons", tag: "v2.2.0", sparse: "optimized", app: false, compile: false, depth: 1}, {:swoosh, "~> 1.16"}, {:req, "~> 0.5"}, {:telemetry_metrics, "~> 1.0"}, @@ -65,7 +60,8 @@ defmodule Towerops.MixProject do {:gettext, "~> 1.0"}, {:jason, "~> 1.2"}, {:dns_cluster, "~> 0.2.0"}, - {:bandit, "~> 1.5"} + {:bandit, "~> 1.5"}, + {:styler, "~> 1.10", only: [:dev, :test], runtime: false} ] end diff --git a/mix.lock b/mix.lock index ba07ef1e..f2911346 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,7 @@ "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "postgrex": {:hex, :postgrex, "0.21.1", "2c5cc830ec11e7a0067dd4d623c049b3ef807e9507a424985b8dcf921224cd88", [: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", "27d8d21c103c3cc68851b533ff99eef353e6a0ff98dc444ea751de43eb48bdac"}, "req": {:hex, :req, "0.5.16", "99ba6a36b014458e52a8b9a0543bfa752cb0344b2a9d756651db1281d4ba4450", [: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", "974a7a27982b9b791df84e8f6687d21483795882a7840e8309abdbe08bb06f09"}, + "styler": {:hex, :styler, "1.10.0", "343f1f7bb19a8893c2841a9ae90665b68d2edf6cc37b964a5099e60c78815c2e", [:mix], [], "hexpm", "6a78876611869466139e63722df4cbbb56b18a842d88c19f23ca844d914ad91a"}, "swoosh": {:hex, :swoosh, "1.19.9", "4eb2c471b8cf06adbdcaa1d57a0ad53c0ed9348ce8586a06cc491f9f0dbcb553", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "516898263a64925c31723c56bc7999a26e97b04e869707f681f4c9bca7ee1688"}, "tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index ca6d69ae..c73edba5 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -19,15 +19,15 @@ defmodule ToweropsWeb.ConnCase do using do quote do + use ToweropsWeb, :verified_routes + + import Phoenix.ConnTest + import Plug.Conn + import ToweropsWeb.ConnCase # The default endpoint for testing @endpoint ToweropsWeb.Endpoint - use ToweropsWeb, :verified_routes - # Import conveniences for testing with connections - import Plug.Conn - import Phoenix.ConnTest - import ToweropsWeb.ConnCase end end diff --git a/test/support/data_case.ex b/test/support/data_case.ex index 9f7bfdcc..3018c195 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -16,14 +16,16 @@ defmodule Towerops.DataCase do use ExUnit.CaseTemplate + alias Ecto.Adapters.SQL.Sandbox + using do quote do - alias Towerops.Repo - import Ecto import Ecto.Changeset import Ecto.Query import Towerops.DataCase + + alias Towerops.Repo end end @@ -36,8 +38,8 @@ defmodule Towerops.DataCase do Sets up the sandbox based on the test tags. """ def setup_sandbox(tags) do - pid = Ecto.Adapters.SQL.Sandbox.start_owner!(Towerops.Repo, shared: not tags[:async]) - on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end) + pid = Sandbox.start_owner!(Towerops.Repo, shared: not tags[:async]) + on_exit(fn -> Sandbox.stop_owner(pid) end) end @doc """