Packages
phoenix_kit
1.5.1
1.7.210
1.7.209
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
Current section
Files
lib/phoenix_kit_web/live/users/media.ex
defmodule PhoenixKitWeb.Live.Users.Media do
@moduledoc """
Media management LiveView for PhoenixKit admin panel.
Provides interface for viewing and managing user media.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
alias PhoenixKit.Storage.URLSigner
alias PhoenixKit.Storage.FileInstance
def mount(params, _session, socket) do
# Set locale for LiveView process
locale = params["locale"] || socket.assigns[:current_locale] || "en"
Gettext.put_locale(PhoenixKitWeb.Gettext, locale)
Process.put(:phoenix_kit_current_locale, locale)
# Batch load all settings needed for this page (uses cached settings for performance)
settings =
Settings.get_settings_cached(
["project_title"],
%{"project_title" => "PhoenixKit"}
)
# Load existing files from database
existing_files = load_existing_files()
socket =
socket
|> allow_upload(:media_files,
accept: ["image/*", "video/*", "application/pdf"],
max_entries: 10,
max_file_size: 100_000_000,
auto_upload: false
)
|> assign(:page_title, "Media")
|> assign(:project_title, settings["project_title"])
|> assign(:current_locale, locale)
|> assign(:url_path, Routes.path("/admin/users/media"))
|> assign(:uploaded_files, existing_files)
{:ok, socket}
end
def handle_event("validate", _params, socket) do
# File validation event - called when files are selected
{:noreply, socket}
end
def handle_event("cancel_upload", %{"ref" => ref}, socket) do
{:noreply, cancel_upload(socket, :media_files, ref)}
end
def handle_event("upload", _params, socket) do
# Process uploaded files when user clicks upload button
process_uploads(socket)
end
defp process_uploads(socket) do
# Process uploaded files
uploaded_files =
consume_uploaded_entries(socket, :media_files, fn %{path: path}, entry ->
# Get file info
ext = Path.extname(entry.client_name) |> String.replace_leading(".", "")
mime_type = entry.client_type || MIME.from_path(entry.client_name)
file_type = determine_file_type(mime_type)
# Get current user
current_user = socket.assigns.phoenix_kit_current_user
user_id = if current_user, do: current_user.id, else: 1
# Get file size
{:ok, stat} = File.stat(path)
file_size = stat.size
# Calculate hash
file_hash = calculate_file_hash(path)
# Store file in storage
case PhoenixKit.Storage.store_file_in_buckets(path, file_type, user_id, file_hash, ext) do
{:ok, file} ->
# Queue background job for processing
_job =
%{file_id: file.id, user_id: user_id, filename: entry.client_name}
|> PhoenixKit.Storage.Workers.ProcessFileJob.new()
|> Oban.insert()
# Generate URLs for available variants (start with original)
urls = generate_file_urls(file.id)
{:ok,
%{
file_id: file.id,
filename: entry.client_name,
file_type: file_type,
mime_type: mime_type,
size: file_size,
status: file.status,
urls: urls
}}
{:error, reason} ->
IO.inspect(reason, label: "Storage Error")
{:error, reason}
end
end)
socket =
socket
|> assign(:uploaded_files, (socket.assigns.uploaded_files || []) ++ uploaded_files)
|> put_flash(:info, "Upload successful! #{length(uploaded_files)} file(s) processed")
{:noreply, socket}
end
# Generate URLs from pre-loaded instances (no database query needed)
defp generate_urls_from_instances(instances, file_id) do
Enum.reduce(instances, %{}, fn instance, acc ->
url = URLSigner.signed_url(file_id, instance.variant_name)
Map.put(acc, instance.variant_name, url)
end)
end
# Legacy function for cases where we need to query a single file's instances
defp generate_file_urls(file_id) do
import Ecto.Query
repo = Application.get_env(:phoenix_kit, :repo)
instances =
FileInstance
|> where([fi], fi.file_id == ^file_id)
|> repo.all()
generate_urls_from_instances(instances, file_id)
end
defp calculate_file_hash(file_path) do
file_path
|> File.read!()
|> then(fn data -> :crypto.hash(:sha256, data) end)
|> Base.encode16(case: :lower)
end
defp determine_file_type(mime_type) do
cond do
String.starts_with?(mime_type, "image/") -> "image"
String.starts_with?(mime_type, "video/") -> "video"
mime_type == "application/pdf" -> "pdf"
true -> "document"
end
end
# Format file size in human-readable format
defp format_file_size(bytes) when is_integer(bytes) do
cond do
bytes >= 1_000_000_000 -> "#{Float.round(bytes / 1_000_000_000, 2)} GB"
bytes >= 1_000_000 -> "#{Float.round(bytes / 1_000_000, 2)} MB"
bytes >= 1_000 -> "#{Float.round(bytes / 1_000, 2)} KB"
true -> "#{bytes} B"
end
end
# Get icon for file type
defp file_icon("image"), do: "hero-photo"
defp file_icon("video"), do: "hero-play-circle"
defp file_icon("pdf"), do: "hero-document-text"
defp file_icon("document"), do: "hero-document"
defp file_icon(_), do: "hero-document-arrow-down"
# Load existing files from database
defp load_existing_files do
import Ecto.Query
repo = Application.get_env(:phoenix_kit, :repo)
# Query files ordered by most recent first
files =
from(f in PhoenixKit.Storage.File,
order_by: [desc: f.inserted_at],
limit: 50
)
|> repo.all()
# Batch load ALL file instances in ONE query instead of N queries
file_ids = Enum.map(files, & &1.id)
instances_by_file =
from(fi in FileInstance,
where: fi.file_id in ^file_ids
)
|> repo.all()
|> Enum.group_by(& &1.file_id)
# Convert to same format as uploaded files
Enum.map(files, fn file ->
# Get pre-loaded instances for this file (no DB query!)
instances = Map.get(instances_by_file, file.id, [])
urls = generate_urls_from_instances(instances, file.id)
%{
file_id: file.id,
filename: file.original_file_name || "Unknown",
file_type: file.file_type,
mime_type: file.mime_type,
size: file.size || 0,
status: file.status,
urls: urls
}
end)
end
end