diff --git a/lib/mix/tasks/phoenix_kit.status.ex b/lib/mix/tasks/phoenix_kit.status.ex index 32ca0b360..bfb1243db 100644 --- a/lib/mix/tasks/phoenix_kit.status.ex +++ b/lib/mix/tasks/phoenix_kit.status.ex @@ -532,11 +532,15 @@ defmodule Mix.Tasks.PhoenixKit.Status do # Ensure repo is properly started for database operations. # Since we use --no-start, the repo may not be running yet. + # We start repo with its application config so it gets the connection params. defp ensure_repo_started(repo) do if repo_available?(repo) do :ok else - case repo.start_link([]) do + parent_app = Mix.Project.config()[:app] + config = Application.get_env(parent_app, repo, []) + + case repo.start_link(config) do {:ok, _pid} -> :ok {:error, {:already_started, _pid}} -> :ok {:error, reason} -> {:error, "Failed to start #{inspect(repo)}: #{inspect(reason)}"} diff --git a/lib/modules/publishing/web/editor.ex b/lib/modules/publishing/web/editor.ex index de7b29999..ca06b96f2 100644 --- a/lib/modules/publishing/web/editor.ex +++ b/lib/modules/publishing/web/editor.ex @@ -527,33 +527,10 @@ defmodule PhoenixKit.Modules.Publishing.Web.Editor do ) has_changes = Forms.dirty?(socket_with_slug.assigns.post, new_form, socket.assigns.content) - language = Helpers.editor_language(socket.assigns) - new_status = new_form["status"] - - # Update both metadata.status and language_statuses for the current language - # This ensures the language switcher reflects the user's selection immediately - current_language_statuses = Map.get(socket.assigns.post, :language_statuses, %{}) - updated_language_statuses = Map.put(current_language_statuses, language, new_status) - - # Update post with current form values for accurate public URL - form_slug = new_form["slug"] - form_url_slug = new_form["url_slug"] - - updated_post = - socket.assigns.post - |> Map.put(:metadata, Map.merge(socket.assigns.post.metadata, %{status: new_status})) - |> Map.put(:language_statuses, updated_language_statuses) - |> then(fn p -> - if form_slug && form_slug != "", do: Map.put(p, :slug, form_slug), else: p - end) - |> then(fn p -> - if form_url_slug && form_url_slug != "", - do: Map.put(p, :url_slug, form_url_slug), - else: p - end) - - public_url = Helpers.build_public_url(updated_post, language) + + {updated_post, public_url} = + update_post_from_form(socket.assigns.post, new_form, language) socket = socket_with_slug @@ -1052,6 +1029,31 @@ defmodule PhoenixKit.Modules.Publishing.Web.Editor do handle_event("attempt_cancel", %{}, socket) end + # Update post struct with current form values for accurate public URL and status display + defp update_post_from_form(post, form, language) do + new_status = form["status"] + current_language_statuses = Map.get(post, :language_statuses, %{}) + updated_language_statuses = Map.put(current_language_statuses, language, new_status) + + form_slug = form["slug"] + form_url_slug = form["url_slug"] + + updated_post = + post + |> Map.put(:metadata, Map.merge(post.metadata, %{status: new_status})) + |> Map.put(:language_statuses, updated_language_statuses) + |> then(fn p -> + if form_slug && form_slug != "", do: Map.put(p, :slug, form_slug), else: p + end) + |> then(fn p -> + if form_url_slug && form_url_slug != "", + do: Map.put(p, :url_slug, form_url_slug), + else: p + end) + + {updated_post, Helpers.build_public_url(updated_post, language)} + end + # ============================================================================ # Handle Info - Autosave # ============================================================================ diff --git a/lib/modules/sitemap/sources/router_discovery.ex b/lib/modules/sitemap/sources/router_discovery.ex index 9d144b869..9f02561a9 100644 --- a/lib/modules/sitemap/sources/router_discovery.ex +++ b/lib/modules/sitemap/sources/router_discovery.ex @@ -322,7 +322,7 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.RouterDiscovery do UrlEntry.new(%{ loc: url, - lastmod: nil, + lastmod: module_lastmod(route), changefreq: "weekly", priority: 0.5, title: title, @@ -331,6 +331,43 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.RouterDiscovery do }) end + # Approximate lastmod from the beam file modification time of the route's LiveView module. + # Falls back to the plug module if no LiveView metadata is found. + defp module_lastmod(route) do + module = extract_liveview_module(route) || route.plug + beam_file_mtime(module) + rescue + _ -> nil + end + + defp extract_liveview_module(route) do + case route.metadata do + %{phoenix_live_view: {module, _, _, _}} when is_atom(module) -> module + %{phoenix_live_view: {module, _, _}} when is_atom(module) -> module + _ -> nil + end + rescue + _ -> nil + end + + defp beam_file_mtime(module) when is_atom(module) do + case :code.which(module) do + beam_path when is_list(beam_path) -> + case File.stat(List.to_string(beam_path)) do + {:ok, %{mtime: mtime}} -> + NaiveDateTime.from_erl!(mtime) |> DateTime.from_naive!("Etc/UTC") + + _ -> + nil + end + + _ -> + nil + end + end + + defp beam_file_mtime(_), do: nil + defp build_url(path, nil) do base = Settings.get_setting("site_url", "") normalized_base = String.trim_trailing(base, "/") diff --git a/lib/modules/sitemap/sources/static.ex b/lib/modules/sitemap/sources/static.ex index 765da3374..636032418 100644 --- a/lib/modules/sitemap/sources/static.ex +++ b/lib/modules/sitemap/sources/static.ex @@ -199,7 +199,7 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Static do UrlEntry.new(%{ loc: url, - lastmod: nil, + lastmod: Date.utc_today(), changefreq: Map.get(config, "changefreq", "weekly"), priority: Map.get(config, "priority", 0.5), title: Map.get(config, "title", path), @@ -224,7 +224,7 @@ defmodule PhoenixKit.Modules.Sitemap.Sources.Static do UrlEntry.new(%{ loc: url, - lastmod: nil, + lastmod: Date.utc_today(), changefreq: Map.get(config, "changefreq", "weekly"), priority: Map.get(config, "priority", 0.5), title: Map.get(config, "title", path), diff --git a/lib/phoenix_kit/migrations/postgres.ex b/lib/phoenix_kit/migrations/postgres.ex index 33d32387a..86d9b9747 100644 --- a/lib/phoenix_kit/migrations/postgres.ex +++ b/lib/phoenix_kit/migrations/postgres.ex @@ -1097,22 +1097,46 @@ defmodule PhoenixKit.Migrations.Postgres do defp ensure_repo_loaded?(_), do: false - # Ensure repo is properly started for database operations - # Note: For Mix tasks, the application should already be started + # Ensure repo is properly started for database operations. + # In --no-start context, Repo process may not be running yet. defp ensure_repo_started(repo) do - # Try Mix.Ecto.ensure_repo if available - if Code.ensure_loaded?(Mix.Ecto) do - Mix.Ecto.ensure_repo(repo, []) + if Process.whereis(repo) != nil do :ok else - # Basic check if repo is available - if Code.ensure_loaded?(repo) && function_exported?(repo, :__adapter__, 0) do - :ok - else - {:error, "Repository #{inspect(repo)} is not available"} - end + start_repo_with_config(repo) end rescue error -> {:error, "Failed to start repo: #{inspect(error)}"} end + + defp start_repo_with_config(repo) do + # Try Mix.Ecto.ensure_repo first (handles config resolution) + if Code.ensure_loaded?(Mix.Ecto) do + Mix.Ecto.ensure_repo(repo, []) + + # ensure_repo loads but doesn't start — start the process + if Process.whereis(repo) == nil do + do_start_repo(repo) + else + :ok + end + else + do_start_repo(repo) + end + end + + defp do_start_repo(repo) do + # Get config from parent app's application env + app = + if Code.ensure_loaded?(Mix) and function_exported?(Mix.Project, :config, 0), + do: Mix.Project.config()[:app] + + config = if app, do: Application.get_env(app, repo, []), else: [] + + case repo.start_link(config) do + {:ok, _pid} -> :ok + {:error, {:already_started, _pid}} -> :ok + {:error, reason} -> {:error, "Failed to start #{inspect(repo)}: #{inspect(reason)}"} + end + end end