Packages
phoenix_kit
1.7.205
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/modules/storage/web/health.ex
defmodule PhoenixKitWeb.Live.Modules.Storage.Health do
@moduledoc """
Media health check LiveView.
Compares file instance location counts against the configured redundancy
target and reports under-replicated files.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Modules.Storage
alias PhoenixKit.Modules.Storage.Workers.SyncFilesJob
alias PhoenixKit.PubSub.Manager, as: PubSubManager
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
@sync_topic "media:sync_progress"
@sync_state_key :phoenix_kit_media_sync_state
def mount(params, _session, socket) do
locale = params["locale"] || socket.assigns[:current_locale]
project_title = Settings.get_project_title()
if connected?(socket) do
PubSubManager.subscribe(@sync_topic)
end
# Check if a sync is already running
sync_state = get_sync_state()
socket =
socket
|> assign(:current_path, Routes.path("/admin/settings/media/health"))
|> assign(:page_title, gettext("Media Health"))
|> assign(:project_title, project_title)
|> assign(:current_locale, locale)
|> assign(:url_path, Routes.path("/admin/settings/media/health"))
|> assign(:sync_log, [])
|> assign(:sync_paused, SyncFilesJob.paused?())
|> apply_sync_state(sync_state)
|> load_health_report()
{:ok, socket}
end
def handle_event("refresh", _params, socket) do
{:noreply, load_health_report(socket)}
end
def handle_event("sync", _params, socket) do
if get_sync_state() != nil do
{:noreply, put_flash(socket, :warning, gettext("Sync is already running"))}
else
%{} |> SyncFilesJob.new() |> Oban.insert()
socket =
socket
|> assign(:syncing, true)
|> assign(:sync_progress, 0)
|> assign(:sync_total, 0)
|> assign(:sync_synced, 0)
|> assign(:sync_failed, 0)
|> assign(:sync_log, [])
|> assign(:prev_synced, 0)
{:noreply, socket}
end
end
def handle_event("pause_sync", _params, socket) do
SyncFilesJob.pause()
{:noreply, assign(socket, :sync_paused, true)}
end
def handle_event("resume_sync", _params, socket) do
SyncFilesJob.resume()
{:noreply, assign(socket, :sync_paused, false)}
end
def handle_event("stop_sync", _params, socket) do
SyncFilesJob.stop()
{:noreply, socket}
end
def handle_info(
{:sync_progress,
%{
done: done,
total: total,
synced: synced,
failed: failed,
log: log,
status: :in_progress
}},
socket
) do
# Update health stats live based on sync progress
# synced is cumulative, so compute delta from previous callback
prev_synced = socket.assigns[:prev_synced] || 0
delta = synced - prev_synced
report = socket.assigns.report
new_healthy = report.healthy + delta
new_percentage =
if report.total > 0,
do: Float.round(new_healthy / report.total * 100, 1),
else: 100.0
updated_report = %{
report
| healthy: new_healthy,
under_replicated: Enum.drop(report.under_replicated, delta),
health_percentage: new_percentage
}
# Append log entry (newest first, cap at 100)
sync_log =
if log do
[log | socket.assigns.sync_log] |> Enum.take(100)
else
socket.assigns.sync_log
end
socket =
socket
|> assign(:syncing, true)
|> assign(:sync_progress, done)
|> assign(:sync_total, total)
|> assign(:sync_synced, synced)
|> assign(:sync_failed, failed)
|> assign(:prev_synced, synced)
|> assign(:report, updated_report)
|> assign(:sync_log, sync_log)
{:noreply, socket}
end
def handle_info({:sync_progress, %{status: :complete} = result}, socket) do
socket =
socket
|> assign(:syncing, false)
|> assign(:sync_paused, false)
|> assign(:sync_progress, result.total)
|> assign(:sync_total, result.total)
|> assign(:sync_synced, result.synced)
|> assign(:sync_failed, result.failed)
|> load_health_report()
|> put_flash(
:info,
gettext("Sync complete: %{synced} synced, %{failed} failed",
synced: result.synced,
failed: result.failed
)
)
{:noreply, socket}
end
def handle_info({:sync_progress, %{status: :stopped} = result}, socket) do
socket =
socket
|> assign(:syncing, false)
|> assign(:sync_paused, false)
|> assign(:sync_synced, result[:synced] || 0)
|> assign(:sync_failed, result[:failed] || 0)
|> load_health_report()
|> put_flash(
:warning,
gettext("Sync stopped: %{synced} synced, %{failed} failed",
synced: result[:synced] || 0,
failed: result[:failed] || 0
)
)
{:noreply, socket}
end
def handle_info(_msg, socket), do: {:noreply, socket}
defp load_health_report(socket) do
redundancy_target =
Settings.get_setting_cached("storage_redundancy_copies", "1")
|> String.to_integer()
report = Storage.get_health_report(redundancy_target)
assign(socket, :report, report)
end
defp apply_sync_state(socket, nil) do
socket
|> assign(:syncing, false)
|> assign(:sync_progress, 0)
|> assign(:sync_total, 0)
|> assign(:sync_synced, 0)
|> assign(:sync_failed, 0)
|> assign(:prev_synced, 0)
end
defp apply_sync_state(socket, state) do
synced = state[:synced] || 0
socket
|> assign(:syncing, true)
|> assign(:sync_progress, state[:done] || 0)
|> assign(:sync_total, state[:total] || 0)
|> assign(:sync_synced, synced)
|> assign(:sync_failed, state[:failed] || 0)
|> assign(:prev_synced, synced)
end
defp get_sync_state do
:persistent_term.get(@sync_state_key, nil)
rescue
ArgumentError -> nil
end
end