diff --git a/lib/mix/tasks/phoenix_kit.install.ex b/lib/mix/tasks/phoenix_kit.install.ex index be555df80..e590f413d 100644 --- a/lib/mix/tasks/phoenix_kit.install.ex +++ b/lib/mix/tasks/phoenix_kit.install.ex @@ -52,6 +52,7 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do BasicConfiguration, BrowserPipelineIntegration, CssIntegration, + DbConnectionCheck, DemoFiles, EndpointIntegration, LayoutConfig, @@ -174,6 +175,9 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do # Run standard igniter process result = super(argv) + # Simple database check - must succeed to continue + DbConnectionCheck.check!() + # After igniter is done, handle interactive migration MigrationStrategy.handle_interactive_migration_after_config(elem(opts, 1)) diff --git a/lib/mix/tasks/phoenix_kit.status.ex b/lib/mix/tasks/phoenix_kit.status.ex index bfb1243db..69c2668a1 100644 --- a/lib/mix/tasks/phoenix_kit.status.ex +++ b/lib/mix/tasks/phoenix_kit.status.ex @@ -42,6 +42,7 @@ defmodule Mix.Tasks.PhoenixKit.Status do alias PhoenixKit.Config alias PhoenixKit.Install.Common + alias PhoenixKit.Install.DbConnectionCheck alias PhoenixKit.Migrations.Postgres @impl Mix.Task @@ -70,6 +71,18 @@ defmodule Mix.Tasks.PhoenixKit.Status do {:ok, _} = Application.ensure_all_started(:ecto_sql) {:ok, _} = Application.ensure_all_started(:phoenix_kit) + # Ensure repo is started before DB check + case get_repo_with_fallback() do + nil -> + :ok + + repo -> + ensure_repo_started(repo) + end + + # Simple database check - must succeed to continue + DbConnectionCheck.check!() + {opts, _argv, _errors} = OptionParser.parse(argv, switches: @switches, aliases: @aliases) prefix = opts[:prefix] || "public" diff --git a/lib/mix/tasks/phoenix_kit.update.ex b/lib/mix/tasks/phoenix_kit.update.ex index 221d421dc..295a71a6a 100644 --- a/lib/mix/tasks/phoenix_kit.update.ex +++ b/lib/mix/tasks/phoenix_kit.update.ex @@ -88,6 +88,7 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do BasicConfiguration, Common, CssIntegration, + DbConnectionCheck, IgniterHelpers, ObanConfig, RateLimiterConfig @@ -221,6 +222,10 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do Application.put_env(:phoenix_kit, :update_mode, true) Mix.Task.run("app.start") + + # Simple database check - must succeed to continue + DbConnectionCheck.check!() + result = super(argv) post_igniter_tasks(elem(opts, 0)) diff --git a/lib/phoenix_kit/install/db_connection_check.ex b/lib/phoenix_kit/install/db_connection_check.ex new file mode 100644 index 000000000..f17646cc4 --- /dev/null +++ b/lib/phoenix_kit/install/db_connection_check.ex @@ -0,0 +1,50 @@ +defmodule PhoenixKit.Install.DbConnectionCheck do + @moduledoc """ + Simple database connection check for PhoenixKit installation. + """ + alias PhoenixKit.Config + + @dialyzer {:nowarn_function, check!: 0} + + @doc """ + Check if database is reachable. Returns true if connected, false otherwise. + """ + def check? do + case Config.get(:repo) do + {:ok, repo} when is_atom(repo) -> + check_repo?(repo) + + _ -> + false + end + end + + @doc """ + Check DB connection and exit with error if not connected. + """ + @spec check!() :: no_return() + def check! do + unless check?() do + Mix.shell().error(""" + ❌ Cannot connect to database. + + Please ensure: + 1. PostgreSQL is running + 2. Database exists (run: mix ecto.create) + 3. Configuration in config/dev.exs is correct + """) + + exit({:shutdown, 1}) + end + end + + defp check_repo?(repo) do + with true <- Code.ensure_loaded?(repo), + true <- function_exported?(repo, :__adapter__, 0), + {:ok, %{rows: [[1]]}} <- repo.query("SELECT 1", [], log: false) do + true + else + _ -> false + end + end +end