From 88392070443b0e00e68ec42eb418298415355e7e Mon Sep 17 00:00:00 2001 From: Alexander Don Date: Thu, 20 Nov 2025 20:51:30 +0200 Subject: [PATCH 1/7] Updated default Redundancy Copies to 1 --- lib/modules/storage/spec.md | 8 ++++---- lib/phoenix_kit/migrations/postgres/v20.ex | 4 ++-- lib/phoenix_kit/storage/manager.ex | 2 +- lib/phoenix_kit_web/live/settings/storage.ex | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/modules/storage/spec.md b/lib/modules/storage/spec.md index e2d661a06..61ed5226c 100644 --- a/lib/modules/storage/spec.md +++ b/lib/modules/storage/spec.md @@ -279,7 +279,7 @@ The V18 migration will seed one default local storage bucket: The V18 migration will add 3 new settings: ```elixir -%{key: "storage_redundancy_copies", value: "2"} # Store files on 2 buckets +%{key: "storage_redundancy_copies", value: "1"} # Store files on 2 buckets %{key: "storage_auto_generate_variants", value: "true"} # Auto-generate thumbnails/resizes %{key: "storage_default_bucket_id", value: nil} # No default bucket (use selection algorithm) ``` @@ -311,7 +311,7 @@ All instances stored **next to original** in same directory: ``` ### Redundancy -- Setting: `storage_redundancy_copies` (integer, 1-5, default: 2) +- Setting: `storage_redundancy_copies` (integer, 1-5, default: 1) - Each file + all variants replicated across N buckets - Example: redundancy = 2, file stored on 2 different buckets @@ -385,14 +385,14 @@ token = :crypto.hash(:md5, data <> secret) New settings added in V18 migration: ```elixir -storage_redundancy_copies: "2" # How many bucket copies (1-5) +storage_redundancy_copies: "1" # How many bucket copies (1-5) storage_auto_generate_variants: "true" # Auto-generate thumbnails/resizes storage_default_bucket_id: nil # Default bucket for uploads (optional) ``` **Access in code:** ```elixir -PhoenixKit.Settings.get_setting("storage_redundancy_copies", "2") +PhoenixKit.Settings.get_setting("storage_redundancy_copies", "1") ``` --- diff --git a/lib/phoenix_kit/migrations/postgres/v20.ex b/lib/phoenix_kit/migrations/postgres/v20.ex index d4f8245f8..08c4ca31b 100644 --- a/lib/phoenix_kit/migrations/postgres/v20.ex +++ b/lib/phoenix_kit/migrations/postgres/v20.ex @@ -44,7 +44,7 @@ defmodule PhoenixKit.Migrations.Postgres.V20 do ## Settings - - `storage_redundancy_copies`: How many bucket copies (default: 2) + - `storage_redundancy_copies`: How many bucket copies (default: 1) - `storage_auto_generate_variants`: Auto-generate thumbnails/resizes (default: true) - `storage_default_bucket_id`: Default bucket for uploads (optional) """ @@ -217,7 +217,7 @@ defmodule PhoenixKit.Migrations.Postgres.V20 do seed_default_bucket(prefix) # Add storage settings - insert_setting(prefix, "storage_redundancy_copies", "2") + insert_setting(prefix, "storage_redundancy_copies", "1") insert_setting(prefix, "storage_auto_generate_variants", "true") insert_setting(prefix, "storage_default_bucket_id", nil) diff --git a/lib/phoenix_kit/storage/manager.ex b/lib/phoenix_kit/storage/manager.ex index 8a571f42a..e63e26d24 100644 --- a/lib/phoenix_kit/storage/manager.ex +++ b/lib/phoenix_kit/storage/manager.ex @@ -245,7 +245,7 @@ defmodule PhoenixKit.Storage.Manager do end defp get_redundancy_copies do - PhoenixKit.Settings.get_setting("storage_redundancy_copies", "2") + PhoenixKit.Settings.get_setting("storage_redundancy_copies", "1") |> String.to_integer() |> max(1) |> min(5) diff --git a/lib/phoenix_kit_web/live/settings/storage.ex b/lib/phoenix_kit_web/live/settings/storage.ex index 33c822ec1..ce6242f9d 100644 --- a/lib/phoenix_kit_web/live/settings/storage.ex +++ b/lib/phoenix_kit_web/live/settings/storage.ex @@ -29,7 +29,7 @@ defmodule PhoenixKitWeb.Live.Settings.Storage do buckets = PhoenixKit.Storage.list_buckets() # Load storage settings from database (using basic function to avoid cache issues) - redundancy_copies = Settings.get_setting("storage_redundancy_copies", "2") + redundancy_copies = Settings.get_setting("storage_redundancy_copies", "1") auto_generate_variants = Settings.get_setting("storage_auto_generate_variants", "true") default_bucket_id = Settings.get_setting("storage_default_bucket_id", nil) @@ -188,7 +188,7 @@ defmodule PhoenixKitWeb.Live.Settings.Storage do case {redundancy_result, variants_result} do {{:ok, _}, {:ok, _}} -> # Verify the settings were saved correctly by reading them back - saved_redundancy = Settings.get_setting("storage_redundancy_copies", "2") + saved_redundancy = Settings.get_setting("storage_redundancy_copies", "1") saved_variants = Settings.get_setting("storage_auto_generate_variants", "true") socket = From 43292effe5c6a522259a3e262e76822b13449af1 Mon Sep 17 00:00:00 2001 From: Alexander Don Date: Thu, 20 Nov 2025 20:51:42 +0200 Subject: [PATCH 2/7] Fixed broken label on media settings page --- lib/phoenix_kit_web/live/settings/storage.html.heex | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/phoenix_kit_web/live/settings/storage.html.heex b/lib/phoenix_kit_web/live/settings/storage.html.heex index 1533438d7..62c6ba2d8 100644 --- a/lib/phoenix_kit_web/live/settings/storage.html.heex +++ b/lib/phoenix_kit_web/live/settings/storage.html.heex @@ -344,15 +344,10 @@ Auto-Generate Variants - Automatically create different sizes when files are uploaded + Automatically resize images and videos to configured dimensions - From 3a3ad774b1c5305a6312ffb6e70c1ff3ddd90abc Mon Sep 17 00:00:00 2001 From: Alexander Don Date: Thu, 20 Nov 2025 22:42:12 +0200 Subject: [PATCH 3/7] Fixed issue with file_location generation on upload --- lib/phoenix_kit/storage.ex | 17 +++++++++++ lib/phoenix_kit/storage/variant_generator.ex | 12 ++++++-- lib/phoenix_kit_web/live/settings/storage.ex | 30 +++++++++++++++++++ .../live/settings/storage.html.heex | 8 ++--- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/lib/phoenix_kit/storage.ex b/lib/phoenix_kit/storage.ex index 63f13997f..149a21523 100644 --- a/lib/phoenix_kit/storage.ex +++ b/lib/phoenix_kit/storage.ex @@ -1462,6 +1462,23 @@ defmodule PhoenixKit.Storage do end end + @doc """ + Creates file location records for a file instance across specified buckets. + + ## Parameters + + - `file_instance_id` - The file instance UUID + - `bucket_ids` - List of bucket UUIDs where the file is stored + - `file_path` - The storage path of the file + + ## Returns + + - `:ok` - Locations created successfully + """ + def create_file_locations_for_instance(file_instance_id, bucket_ids, file_path) do + create_file_locations(file_instance_id, bucket_ids, file_path) + end + defp generate_temp_path do temp_dir = System.tmp_dir!() random_name = :crypto.strong_rand_bytes(8) |> Base.encode16(case: :lower) diff --git a/lib/phoenix_kit/storage/variant_generator.ex b/lib/phoenix_kit/storage/variant_generator.ex index db9f90beb..2cd8b27bc 100644 --- a/lib/phoenix_kit/storage/variant_generator.ex +++ b/lib/phoenix_kit/storage/variant_generator.ex @@ -115,7 +115,7 @@ defmodule PhoenixKit.Storage.VariantGenerator do {:ok, variant_path} <- process_variant(original_path, variant_path, file.mime_type, dimension), {:ok, file_stats} <- get_variant_file_stats(variant_path), - {:ok, _storage_info} <- + {:ok, storage_info} <- store_variant_file(variant_path, variant_name, variant_storage_path, file.id), {:ok, instance} <- create_variant_instance( @@ -126,8 +126,16 @@ defmodule PhoenixKit.Storage.VariantGenerator do variant_ext, file_stats ) do + # Create file location records for this variant instance + _ = + Storage.create_file_locations_for_instance( + instance.id, + storage_info.bucket_ids, + variant_storage_path + ) + cleanup_temp_files([original_path, variant_path]) - Logger.info("Variant #{variant_name} created successfully in database") + Logger.info("Variant #{variant_name} created successfully in database with locations") {:ok, instance} else {:error, reason} = error -> diff --git a/lib/phoenix_kit_web/live/settings/storage.ex b/lib/phoenix_kit_web/live/settings/storage.ex index ce6242f9d..24614c542 100644 --- a/lib/phoenix_kit_web/live/settings/storage.ex +++ b/lib/phoenix_kit_web/live/settings/storage.ex @@ -9,6 +9,8 @@ defmodule PhoenixKitWeb.Live.Settings.Storage do require Logger + import Ecto.Query + alias PhoenixKit.Settings alias PhoenixKit.System.Dependencies alias PhoenixKit.Utils.Routes @@ -28,6 +30,9 @@ defmodule PhoenixKitWeb.Live.Settings.Storage do # Load buckets buckets = PhoenixKit.Storage.list_buckets() + # Load file counts per bucket (unique files, not instances) + bucket_file_counts = get_bucket_file_counts(buckets) + # Load storage settings from database (using basic function to avoid cache issues) redundancy_copies = Settings.get_setting("storage_redundancy_copies", "1") auto_generate_variants = Settings.get_setting("storage_auto_generate_variants", "true") @@ -54,6 +59,7 @@ defmodule PhoenixKitWeb.Live.Settings.Storage do |> assign(:page_title, "Storage Settings") |> assign(:project_title, project_title) |> assign(:buckets, buckets) + |> assign(:bucket_file_counts, bucket_file_counts) |> assign(:redundancy_copies, current_redundancy) |> assign(:auto_generate_variants, auto_generate_variants == "true") |> assign(:default_bucket_id, default_bucket_id) @@ -305,4 +311,28 @@ defmodule PhoenixKitWeb.Live.Settings.Storage do "#{bucket.provider}: unknown configuration" end end + + # Get count of unique files stored on each bucket + defp get_bucket_file_counts(buckets) do + repo = PhoenixKit.Config.get_repo() + + Enum.reduce(buckets, %{}, fn bucket, acc -> + # Count distinct files that have at least one instance located on this bucket + # We count files, not instances or locations + count = + repo.one( + from f in PhoenixKit.Storage.File, + join: fi in PhoenixKit.Storage.FileInstance, + on: fi.file_id == f.id, + join: fl in PhoenixKit.Storage.FileLocation, + on: fl.file_instance_id == fi.id, + where: fl.bucket_id == ^bucket.id and fl.status == "active", + select: count(f.id, :distinct) + ) + + Map.put(acc, bucket.id, count || 0) + end) + rescue + _ -> %{} + end end diff --git a/lib/phoenix_kit_web/live/settings/storage.html.heex b/lib/phoenix_kit_web/live/settings/storage.html.heex index 62c6ba2d8..5ee7a472d 100644 --- a/lib/phoenix_kit_web/live/settings/storage.html.heex +++ b/lib/phoenix_kit_web/live/settings/storage.html.heex @@ -131,7 +131,7 @@ Name Provider Priority - Usage + Files Status Actions @@ -179,10 +179,10 @@ <% end %> - <%!-- Usage --%> + <%!-- Files Count --%> - - Calculating... + + <%= Map.get(@bucket_file_counts, bucket.id, 0) %> files From ddb19ec98c0c041ef4edf96d77d2bdaab64e34e3 Mon Sep 17 00:00:00 2001 From: Alexander Don Date: Fri, 21 Nov 2025 00:39:57 +0200 Subject: [PATCH 4/7] Mix format --- lib/phoenix_kit_web/live/settings/storage.html.heex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/phoenix_kit_web/live/settings/storage.html.heex b/lib/phoenix_kit_web/live/settings/storage.html.heex index 5ee7a472d..88b2c06cb 100644 --- a/lib/phoenix_kit_web/live/settings/storage.html.heex +++ b/lib/phoenix_kit_web/live/settings/storage.html.heex @@ -182,7 +182,7 @@ <%!-- Files Count --%> - <%= Map.get(@bucket_file_counts, bucket.id, 0) %> files + {Map.get(@bucket_file_counts, bucket.id, 0)} files From 0e4d4ec127a2e7c026e9cfd15ca51962a6794fe1 Mon Sep 17 00:00:00 2001 From: Alexander Don Date: Fri, 21 Nov 2025 23:53:01 +0200 Subject: [PATCH 5/7] Fixed the namingo on the oban config generation with iginiter --- lib/phoenix_kit/install/oban_config.ex | 47 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/phoenix_kit/install/oban_config.ex b/lib/phoenix_kit/install/oban_config.ex index 1ad5f7adb..bbca018c3 100644 --- a/lib/phoenix_kit/install/oban_config.ex +++ b/lib/phoenix_kit/install/oban_config.ex @@ -101,8 +101,8 @@ defmodule PhoenixKit.Install.ObanConfig do Igniter.update_file(igniter, "config/config.exs", fn source -> content = Rewrite.Source.get(source, :content) - # Check if Oban config already exists - if String.contains?(content, "config :#{app_name}, Oban") do + # Check if Oban config already exists (with more robust detection) + if oban_config_already_exists?(content, app_name) do source else # Find insertion point before import_config statements @@ -129,23 +129,52 @@ defmodule PhoenixKit.Install.ObanConfig do end end - # Get repo module from PhoenixKit config or use placeholder - defp get_repo_module(_igniter) do + # Get repo module from PhoenixKit config or detect from app + defp get_repo_module(igniter) do config_path = "config/config.exs" + app_name = IgniterHelpers.get_parent_app_name(igniter) if File.exists?(config_path) do content = File.read!(config_path) - # Look for existing PhoenixKit repo config + # First try: Look for existing PhoenixKit repo config case Regex.run(~r/config :phoenix_kit,\s+repo:\s+([A-Za-z0-9_.]+)/, content) do - [_, repo] -> repo - _ -> "MyApp.Repo" + [_, repo] -> + repo + + _ -> + # Second try: Look for ecto_repos in app config + app_module = Macro.camelize(to_string(app_name)) + + case Regex.run(~r/config :#{app_name}.*?ecto_repos:\s*\[([A-Za-z0-9_.]+)\]/s, content) do + [_, repo] -> repo + _ -> "#{app_module}.Repo" + end end else - "MyApp.Repo" + app_module = Macro.camelize(to_string(app_name)) + "#{app_module}.Repo" end rescue - _ -> "MyApp.Repo" + _ -> + app_name = IgniterHelpers.get_parent_app_name(igniter) + app_module = Macro.camelize(to_string(app_name)) + "#{app_module}.Repo" + end + + # Check if Oban config already exists in the file + defp oban_config_already_exists?(content, app_name) do + lines = String.split(content, "\n") + + Enum.any?(lines, fn line -> + trimmed = String.trim(line) + + # Not a comment and contains config for Oban + !String.starts_with?(trimmed, "#") and + (String.contains?(line, "config :#{app_name}, Oban") or + # Also check for variations with spaces + Regex.match?(~r/config\s+:#{app_name},\s+Oban/, line)) + end) end # Find the location to insert config before import_config statements From 4f788a01fb54d653784833072d1b07ff46250fbf Mon Sep 17 00:00:00 2001 From: Alexander Don Date: Fri, 21 Nov 2025 23:53:07 +0200 Subject: [PATCH 6/7] Fixed button placement --- .../live/users/media.html.heex | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/phoenix_kit_web/live/users/media.html.heex b/lib/phoenix_kit_web/live/users/media.html.heex index a515eb1b7..ea76b1446 100644 --- a/lib/phoenix_kit_web/live/users/media.html.heex +++ b/lib/phoenix_kit_web/live/users/media.html.heex @@ -16,7 +16,15 @@ <%= if @show_upload do %>
-

Upload Media

+
+

Upload Media

+ +
<%= if @has_buckets do %>

Upload images, videos, or PDFs. Files are automatically processed and optimized. @@ -57,16 +65,14 @@

Uploaded Files ({@total_count})

- + + <% end %>
<.pagination_info page={@current_page} From 7c69103aa5a44c06849bb957dbe8fd93430b376f Mon Sep 17 00:00:00 2001 From: Alexander Don Date: Fri, 21 Nov 2025 23:53:23 +0200 Subject: [PATCH 7/7] Mix format --- lib/phoenix_kit/install/oban_config.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/phoenix_kit/install/oban_config.ex b/lib/phoenix_kit/install/oban_config.ex index bbca018c3..901479c52 100644 --- a/lib/phoenix_kit/install/oban_config.ex +++ b/lib/phoenix_kit/install/oban_config.ex @@ -170,9 +170,9 @@ defmodule PhoenixKit.Install.ObanConfig do trimmed = String.trim(line) # Not a comment and contains config for Oban + # Also check for variations with spaces !String.starts_with?(trimmed, "#") and (String.contains?(line, "config :#{app_name}, Oban") or - # Also check for variations with spaces Regex.match?(~r/config\s+:#{app_name},\s+Oban/, line)) end) end