feat: Add media cleanup functionality with UI and backend support for unused media management
This commit is contained in:
+22
-3
@@ -257,9 +257,28 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_staff %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="{% url 'admin:index' %}">Admin</a>
|
||||
{% if request.user.is_staff or request.user|has_group:"cid_user_manager" %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Admin
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
{% if request.user.is_staff %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'admin:index' %}">Django admin</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.user.is_superuser %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'logs_view' %}">Logs</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.user.is_superuser or request.user|has_group:"cid_user_manager" %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'media_cleanup' %}">Unused media cleanup</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.user.is_authenticated %}
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
<li>Manage <a href="{% url 'trainees' %}">trainees</a> · <a
|
||||
href="{% url 'accounts_list' %}">users</a> · <a
|
||||
href="{% url 'generic:manage_cids' %}">candidates</a></li>
|
||||
<li><a href="{% url 'media_cleanup' %}" class="link-primary">Unused media cleanup</a></li>
|
||||
{% endif %}
|
||||
{% if request.user.is_superuser %}
|
||||
<li><a href="{% url 'django_psutil_dash:dashboard' %}" class="link-primary">psutil</a></li>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1">Unused Media Cleanup</h2>
|
||||
<p class="text-muted mb-0">
|
||||
Run django-unused-media from the web UI to preview or remove orphaned files.
|
||||
</p>
|
||||
</div>
|
||||
<a href="{% url 'logs_view' %}" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-journal-text me-1"></i> View logs
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<form
|
||||
method="post"
|
||||
hx-post="{% url 'media_cleanup' %}"
|
||||
hx-target="#media-cleanup-result"
|
||||
hx-swap="innerHTML"
|
||||
class="row g-3"
|
||||
>
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="col-12 col-lg-4">
|
||||
<label for="id_minimum_file_age" class="form-label">Minimum file age (seconds)</label>
|
||||
<input
|
||||
id="id_minimum_file_age"
|
||||
type="number"
|
||||
min="0"
|
||||
step="1"
|
||||
name="minimum_file_age"
|
||||
class="form-control"
|
||||
value="{{ minimum_file_age|default:60 }}"
|
||||
>
|
||||
<div class="form-text">Skip very recent files to avoid race conditions with active uploads.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-8">
|
||||
<label for="id_exclude" class="form-label">Exclude masks (one per line)</label>
|
||||
<textarea
|
||||
id="id_exclude"
|
||||
name="exclude"
|
||||
rows="4"
|
||||
class="form-control"
|
||||
placeholder="example: thumbnails/*"
|
||||
>{{ exclude }}</textarea>
|
||||
<div class="form-text">Uses django-unused-media wildcard matching.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="id_remove_empty_dirs"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
name="remove_empty_dirs"
|
||||
{% if remove_empty_dirs %}checked{% endif %}
|
||||
>
|
||||
<label for="id_remove_empty_dirs" class="form-check-label">
|
||||
Remove empty directories after cleanup
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 d-flex flex-wrap gap-2">
|
||||
<button
|
||||
type="submit"
|
||||
name="action"
|
||||
value="preview"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
<i class="bi bi-search me-1"></i> Preview unused files
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
name="action"
|
||||
value="cleanup"
|
||||
class="btn btn-danger"
|
||||
hx-confirm="Delete unused media files now? This cannot be undone."
|
||||
>
|
||||
<i class="bi bi-trash me-1"></i> Delete unused files
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="media-cleanup-result">
|
||||
{% include 'rad/partials/_media_cleanup_result.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,60 @@
|
||||
{% if ran %}
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 mb-2">
|
||||
{% if success %}
|
||||
<span class="badge bg-success">Completed</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">Failed</span>
|
||||
{% endif %}
|
||||
|
||||
{% if dry_run %}
|
||||
<span class="badge bg-info text-dark">Dry run</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning text-dark">Live cleanup</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="small text-muted mb-3">
|
||||
minimum_file_age={{ minimum_file_age }}
|
||||
{% if remove_empty_dirs %} | remove_empty_dirs=on{% else %} | remove_empty_dirs=off{% endif %}
|
||||
{% if exclude %} | exclude masks provided{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="border rounded p-2 small">
|
||||
<div class="text-muted">Media size before</div>
|
||||
<div><strong>{{ media_size_before|filesizeformat }}</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="border rounded p-2 small">
|
||||
<div class="text-muted">Media size after</div>
|
||||
<div><strong>{{ media_size_after|filesizeformat }}</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="border rounded p-2 small">
|
||||
<div class="text-muted">Recovered space</div>
|
||||
<div><strong>{{ recovered_size|filesizeformat }}</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if error_message %}
|
||||
<div class="alert alert-danger py-2">{{ error_message }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if output %}
|
||||
<pre class="mb-0 p-3 border rounded small" style="max-height: 28rem; overflow-y: auto;">{{ output }}</pre>
|
||||
{% else %}
|
||||
<div class="alert alert-secondary mb-0 py-2">No output was returned by the command.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-secondary mb-0">
|
||||
Use <strong>Preview unused files</strong> first, then run deletion when you are happy with the output.
|
||||
</div>
|
||||
{% endif %}
|
||||
Reference in New Issue
Block a user