Packages

phoenix_kit

1.2.8
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib mix tasks phoenix_kit.status.ex
Raw

lib/mix/tasks/phoenix_kit.status.ex

defmodule Mix.Tasks.PhoenixKit.Status do
@moduledoc """
Shows comprehensive status of PhoenixKit installation.
This task provides a detailed overview of your PhoenixKit installation status,
including version information, database connectivity, assets status, and
suggested next actions.
## Usage
$ mix phoenix_kit.status
$ mix phoenix_kit.status --prefix=myapp
## Options
* `--prefix` - Database schema prefix (default: "public")
* `--verbose` - Show detailed diagnostic information
## Examples
# Show status for default installation
mix phoenix_kit.status
# Show status for custom schema prefix
mix phoenix_kit.status --prefix=auth
# Show detailed diagnostic information
mix phoenix_kit.status --verbose
## Sample Output
PhoenixKit v1.2.1
├── Installed: V03 ✅
├── Database: Connected ✅
├── Assets: Built ✅
└── Status: Ready to use
"""
use Mix.Task
alias Ecto.Adapters.SQL
alias PhoenixKit.Install.Common
alias PhoenixKit.Migrations.Postgres
@impl Mix.Task
@spec run([String.t()]) :: :ok
@shortdoc "Shows comprehensive PhoenixKit installation status"
@switches [
prefix: :string,
verbose: :boolean
]
@aliases [
p: :prefix,
v: :verbose
]
def run(argv) do
# Conditionally start the application if repositories aren't available
app_started_by_us = ensure_app_started()
try do
{opts, _argv, _errors} = OptionParser.parse(argv, switches: @switches, aliases: @aliases)
prefix = opts[:prefix] || "public"
verbose = opts[:verbose] || false
show_comprehensive_status(prefix, verbose)
after
# Stop the application if we started it
if app_started_by_us do
# Note: Mix doesn't have app.stop task, and typically apps stay running
# This is normal behavior for Mix tasks
:ok
end
end
end
# Main status display function
defp show_comprehensive_status(prefix, verbose) do
# Get all status information
phoenix_kit_version = get_phoenix_kit_version()
installation_status = get_installation_status(prefix)
database_status = get_database_status(prefix)
next_action = determine_next_action(installation_status, prefix)
# Display header
IO.puts("\n#{IO.ANSI.bright()}PhoenixKit v#{phoenix_kit_version}#{IO.ANSI.reset()}")
# Display status tree
display_status_tree([
{"Installed", format_installation_status(installation_status)},
{"Database", format_database_status(database_status)},
{"Next", format_next_action(next_action)}
])
# Show verbose information if requested
if verbose do
show_verbose_diagnostics(prefix, installation_status, database_status)
end
IO.puts("")
end
# Get PhoenixKit module version
defp get_phoenix_kit_version do
case :application.get_key(:phoenix_kit, :vsn) do
{:ok, vsn} when is_list(vsn) -> List.to_string(vsn)
{:ok, vsn} -> to_string(vsn)
:undefined -> "unknown"
end
end
# Get installation status
defp get_installation_status(prefix) do
case Common.check_installation_status(prefix) do
{:not_installed} ->
{:not_installed}
{:current_version, version} ->
target_version = Postgres.current_version()
if version >= target_version do
{:up_to_date, version}
else
{:needs_update, version, target_version}
end
end
end
# Get database connectivity status with hybrid repo detection
defp get_database_status(prefix) do
case get_repo_with_fallback() do
nil ->
{:no_repo_configured}
repo ->
test_repo_and_tables(repo, prefix)
end
rescue
error -> {:error, error}
end
# Test repository connection and check for PhoenixKit tables
defp test_repo_and_tables(repo, prefix) do
case ensure_repo_started(repo) do
:ok ->
test_database_connection(repo, prefix)
{:error, reason} ->
{:connection_error, reason}
end
end
# Test database connection and check tables
defp test_database_connection(repo, prefix) do
case SQL.query(repo, "SELECT 1", []) do
{:ok, _result} ->
check_phoenix_kit_tables(prefix)
{:error, reason} ->
{:connection_error, reason}
end
end
# Helper function to check if PhoenixKit tables exist
defp check_phoenix_kit_tables(prefix) do
opts = %{prefix: prefix, escaped_prefix: String.replace(prefix, "'", "\\'")}
version = Postgres.migrated_version_runtime(opts)
if version > 0 do
{:connected_with_tables, version}
else
{:connected_no_tables}
end
end
# Determine next recommended action
defp determine_next_action({:not_installed}, _prefix) do
{:install, "mix phoenix_kit.install"}
end
defp determine_next_action({:needs_update, _current, _target}, prefix) do
cmd =
if prefix != "public",
do: "mix phoenix_kit.update --prefix=#{prefix}",
else: "mix phoenix_kit.update"
{:update, cmd}
end
defp determine_next_action({:up_to_date, _version}, _prefix) do
{:ready, "Ready to use"}
end
# Format installation status for display
defp format_installation_status({:not_installed}) do
"#{IO.ANSI.red()}Not installed#{IO.ANSI.reset()}"
end
defp format_installation_status({:up_to_date, version}) do
"#{IO.ANSI.green()}V#{pad_version(version)}#{IO.ANSI.reset()}"
end
defp format_installation_status({:needs_update, current, target}) do
"#{IO.ANSI.yellow()}V#{pad_version(current)} (needs update to V#{pad_version(target)})#{IO.ANSI.reset()}"
end
# Format database status for display
defp format_database_status({:connected_with_tables, _version}) do
"#{IO.ANSI.green()}Connected ✅#{IO.ANSI.reset()}"
end
defp format_database_status({:connected_no_tables}) do
"#{IO.ANSI.yellow()}Connected (no tables)#{IO.ANSI.reset()}"
end
defp format_database_status({:connection_error, _reason}) do
"#{IO.ANSI.red()}Connection failed ❌#{IO.ANSI.reset()}"
end
defp format_database_status({:no_repo_configured}) do
"#{IO.ANSI.red()}No repo configured ❌#{IO.ANSI.reset()}"
end
defp format_database_status({:error, _error}) do
"#{IO.ANSI.red()}Error ❌#{IO.ANSI.reset()}"
end
# Format next action for display
defp format_next_action({:install, command}) do
"#{IO.ANSI.cyan()}#{command}#{IO.ANSI.reset()}"
end
defp format_next_action({:update, command}) do
"#{IO.ANSI.cyan()}#{command}#{IO.ANSI.reset()}"
end
defp format_next_action({:ready, message}) do
"#{IO.ANSI.green()}#{message}#{IO.ANSI.reset()}"
end
# Display status information in tree format
defp display_status_tree(items) do
items
|> Enum.with_index()
|> Enum.each(fn {{label, value}, index} ->
is_last = index == length(items) - 1
prefix = if is_last, do: "└── ", else: "├── "
IO.puts("#{prefix}#{IO.ANSI.bright()}#{label}#{IO.ANSI.reset()}: #{value}")
end)
end
# Show detailed diagnostic information
defp show_verbose_diagnostics(prefix, installation_status, database_status) do
IO.puts("\n#{IO.ANSI.bright()}═══ Detailed Diagnostics ═══#{IO.ANSI.reset()}")
show_installation_diagnostics(installation_status, prefix)
show_database_diagnostics(database_status, prefix)
show_configuration_diagnostics()
end
# Show installation diagnostics
defp show_installation_diagnostics(status, prefix) do
IO.puts("\n#{IO.ANSI.bright()}Installation:#{IO.ANSI.reset()}")
IO.puts(" Schema prefix: #{prefix}")
case status do
{:not_installed} ->
IO.puts(" Migration files: #{inspect(Common.find_existing_phoenix_kit_migrations())}")
{:up_to_date, version} ->
IO.puts(" Current version: V#{pad_version(version)}")
IO.puts(" Target version: V#{pad_version(Postgres.current_version())}")
{:needs_update, current, target} ->
IO.puts(" Current version: V#{pad_version(current)}")
IO.puts(" Target version: V#{pad_version(target)}")
changes = Common.describe_version_changes(current, target)
IO.puts(" Available changes:")
String.split(changes, "\n") |> Enum.each(&IO.puts(" #{&1}"))
end
end
# Show database diagnostics
defp show_database_diagnostics(status, _prefix) do
IO.puts("\n#{IO.ANSI.bright()}Database:#{IO.ANSI.reset()}")
# Show how repo was detected
show_repo_detection_info()
case status do
{:connected_with_tables, version} ->
IO.puts(" Connection: OK")
IO.puts(" PhoenixKit tables: Present (V#{pad_version(version)})")
{:connected_no_tables} ->
IO.puts(" Connection: OK")
IO.puts(" PhoenixKit tables: Missing")
{:connection_error, reason} ->
IO.puts(" Connection: Failed")
IO.puts(" Error: #{inspect(reason)}")
{:no_repo_configured} ->
IO.puts(" Connection: No repo configured")
show_repo_fallback_attempts()
{:error, error} ->
IO.puts(" Connection: Error")
IO.puts(" Details: #{inspect(error)}")
end
end
# Show detailed repo detection information
defp show_repo_detection_info do
phoenix_kit_repo = Application.get_env(:phoenix_kit, :repo)
IO.puts(" PhoenixKit repo config: #{inspect(phoenix_kit_repo)}")
if phoenix_kit_repo do
show_configured_repo_info(phoenix_kit_repo)
else
show_fallback_repo_info()
end
end
# Show information about configured repo
defp show_configured_repo_info(repo) do
IO.puts(" Detection method: PhoenixKit application config")
IO.puts(" Testing repo connection...")
test_and_report_repo_connection(repo)
end
# Show information about fallback repo detection
defp show_fallback_repo_info do
started_repo = ensure_phoenix_kit_started()
IO.puts(" After starting PhoenixKit app: #{inspect(started_repo)}")
detected_repo = detect_repo_from_project()
if detected_repo do
show_detected_repo_info(detected_repo)
else
IO.puts(" No repo detected via fallback methods")
end
end
# Show information about detected repo
defp show_detected_repo_info(repo) do
parent_app_name = Mix.Project.config()[:app]
IO.puts(" Fallback repo detected: #{inspect(repo)}")
# Show which method worked
if try_ecto_repos_config(parent_app_name) do
IO.puts(" Detection method: :ecto_repos config")
else
IO.puts(" Detection method: Naming pattern")
end
IO.puts(" Testing repo connection...")
test_and_report_repo_connection(repo)
end
# Test repo connection and report results
defp test_and_report_repo_connection(repo) do
case test_repo_connection(repo) do
:ok -> IO.puts(" Repo connection test: PASSED")
{:error, reason} -> IO.puts(" Repo connection test: FAILED - #{inspect(reason)}")
end
end
# Test repo connection
defp test_repo_connection(repo) do
# Ensure repo is started first
case ensure_repo_started(repo) do
:ok ->
case SQL.query(repo, "SELECT 1", []) do
{:ok, _result} -> :ok
{:error, reason} -> {:error, reason}
end
{:error, reason} ->
{:error, reason}
end
rescue
error -> {:error, error}
end
# Show fallback attempts when repo is not configured
defp show_repo_fallback_attempts do
parent_app_name = Mix.Project.config()[:app]
IO.puts(" Fallback attempts:")
IO.puts(" - PhoenixKit config: #{inspect(Application.get_env(:phoenix_kit, :repo))}")
IO.puts(
" - App :ecto_repos: #{inspect(Application.get_env(parent_app_name, :ecto_repos, []))}"
)
if parent_app_name do
expected_repo = Module.concat([Macro.camelize(to_string(parent_app_name)), "Repo"])
IO.puts(" - Naming pattern (#{expected_repo}): #{ensure_repo_loaded?(expected_repo)}")
end
end
# Show configuration diagnostics
defp show_configuration_diagnostics do
IO.puts("\n#{IO.ANSI.bright()}Configuration:#{IO.ANSI.reset()}")
# Check layout configuration
layout_config = Application.get_env(:phoenix_kit, :layout)
IO.puts(" Layout integration: #{if layout_config, do: "Configured", else: "Using defaults"}")
# Check theme configuration
theme_config = Application.get_env(:phoenix_kit, :theme_enabled, false)
IO.puts(" Theme system: #{if theme_config, do: "Enabled", else: "Disabled"}")
# Check mailer configuration
mailer_config = Application.get_env(:phoenix_kit, PhoenixKit.Mailer)
IO.puts(" Mailer: #{if mailer_config, do: "Configured", else: "Not configured"}")
end
# Hybrid repo detection with fallback strategies
defp get_repo_with_fallback do
# Strategy 1: Try to get from PhoenixKit application config
case Application.get_env(:phoenix_kit, :repo) do
nil ->
# Strategy 2: Try to ensure PhoenixKit application is started
case ensure_phoenix_kit_started() do
repo when not is_nil(repo) ->
repo
nil ->
# Strategy 3: Auto-detect from project configuration
detect_repo_from_project()
end
repo ->
repo
end
end
# Try to start PhoenixKit application and get repo config
defp ensure_phoenix_kit_started do
Application.ensure_all_started(:phoenix_kit)
Application.get_env(:phoenix_kit, :repo)
rescue
_ -> nil
end
# Auto-detect repository from project configuration (similar to RepoDetection)
defp detect_repo_from_project do
parent_app_name = Mix.Project.config()[:app]
# Try :ecto_repos config first
case try_ecto_repos_config(parent_app_name) do
nil -> try_naming_patterns(parent_app_name)
repo -> repo
end
end
# Try to get repo from :ecto_repos application config
defp try_ecto_repos_config(nil), do: nil
defp try_ecto_repos_config(app_name) do
case Application.get_env(app_name, :ecto_repos, []) do
[repo | _] when is_atom(repo) ->
if ensure_repo_loaded?(repo), do: repo, else: nil
[] ->
nil
end
rescue
_ -> nil
end
# Try common naming patterns
defp try_naming_patterns(nil), do: nil
defp try_naming_patterns(app_name) do
# Try most common pattern: AppName.Repo
repo_module = Module.concat([Macro.camelize(to_string(app_name)), "Repo"])
if ensure_repo_loaded?(repo_module) do
repo_module
else
nil
end
end
# Check if repo module exists and is loaded
defp ensure_repo_loaded?(repo) when is_atom(repo) and not is_nil(repo) do
Code.ensure_loaded?(repo) && function_exported?(repo, :__adapter__, 0)
rescue
_ -> false
end
defp ensure_repo_loaded?(_), do: false
# Ensure repo is properly started for database operations
# Since app.start is called in run/1, this is now much simpler
defp ensure_repo_started(repo) do
if repo_available?(repo) do
:ok
else
{:error, "Repository #{inspect(repo)} is not available"}
end
end
# Check if repo module is available and started
defp repo_available?(repo) do
Code.ensure_loaded?(repo) &&
function_exported?(repo, :__adapter__, 0) &&
Process.whereis(repo) != nil
rescue
_ -> false
end
# Ensure application is started only if needed
# Returns true if we started the app, false if it was already running
defp ensure_app_started do
# Check if we can get repo configuration
case get_repo_with_fallback() do
nil ->
# No repo found, start app and try again
Mix.Task.run("app.start")
true
repo ->
# Repo found, check if it's actually available
if repo_available?(repo) do
# Repo is available, no need to start app
false
else
# Repo not available, start app
Mix.Task.run("app.start")
true
end
end
end
# Pad version number for consistent display
defp pad_version(version) when version < 10, do: "0#{version}"
defp pad_version(version), do: to_string(version)
end