Add admin page to see package storage usage

This commit is contained in:
rubenwardy 2024-01-18 18:09:08 +00:00
parent acaf674ec5
commit 406eb5d180
4 changed files with 100 additions and 4 deletions

@ -19,10 +19,12 @@ from flask_login import current_user, login_user
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, BooleanField
from wtforms.validators import InputRequired, Length, Optional
from app.utils import rank_required, add_audit_log, add_notification, get_system_user, nonempty_or_none
from app.utils import rank_required, add_audit_log, add_notification, get_system_user, nonempty_or_none, \
get_int_or_abort
from . import bp
from .actions import actions
from app.models import UserRank, Package, db, PackageState, User, AuditSeverity, NotificationType, PackageAlias
from ...querybuilder import QueryBuilder
@bp.route("/admin/", methods=["GET", "POST"])
@ -179,3 +181,27 @@ def transfer():
# Process GET or invalid POST
return render_template("admin/transfer.html", form=form)
@bp.route("/admin/storage/")
@rank_required(UserRank.EDITOR)
def storage():
qb = QueryBuilder(request.args, cookies=True)
qb.only_approved = False
packages = qb.build_package_query().all()
show_all = len(packages) < 100
min_size = get_int_or_abort(request.args.get("min_size"), 0 if show_all else 50)
data = []
for package in packages:
size_releases = sum([x.file_size_bytes for x in package.releases])
size_screenshots = sum([x.file_size_bytes for x in package.screenshots])
latest_release = package.releases.first()
size_latest = latest_release.file_size_bytes if latest_release else 0
size_total = size_releases + size_screenshots
if size_total > min_size*10024*1024:
data.append([package, size_total, size_releases, size_screenshots, size_latest])
data.sort(key=lambda x: x[1], reverse=True)
return render_template("admin/storage.html", data=data)

@ -29,6 +29,7 @@ from .utils import is_yes, get_int_or_abort
class QueryBuilder:
types = None
search = None
only_approved = True
@property
def title(self):
@ -155,10 +156,12 @@ class QueryBuilder:
def build_package_query(self):
if self.order_by == "last_release":
query = db.session.query(Package).select_from(PackageRelease).join(Package) \
.filter_by(state=PackageState.APPROVED)
query = db.session.query(Package).select_from(PackageRelease).join(Package)
else:
query = Package.query.filter_by(state=PackageState.APPROVED)
query = Package.query
if self.only_approved:
query = query.filter(Package.state == PackageState.APPROVED)
query = query.options(subqueryload(Package.main_screenshot), subqueryload(Package.aliases))

@ -17,6 +17,7 @@
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.warning_list') }}">Warning Editor</a>
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.send_bulk_email') }}">Send bulk email</a>
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.send_bulk_notification') }}">Send bulk notification</a>
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.storage') }}">Storage usage</a>
<a class="list-group-item list-group-item-action" href="{{ url_for('admin.switch_user') }}">Sign in as another user</a>
</div>
</div>

@ -0,0 +1,66 @@
{% extends "base.html" %}
{% block title %}
Storage
{% endblock %}
{% block content %}
<h1>Storage</h1>
<p class="text-muted">
Shows storage use by package. Supports <a href="/help/api/#package-queries">Package Queries</a>, but always
sorts by total storage usage.
</p>
<div class="list-group">
<div class="list-group-item">
<div class="row text-muted">
<div class="col">
{{ _("Package") }}
</div>
<div class="col-2 text-center">
Latest release / MB
</div>
<div class="col-2 text-center">
Releases / MB
</div>
<div class="col-2 text-center">
Screenshots / MB
</div>
<div class="col-2 text-center">
Total / MB
</div>
</div>
</div>
{% for row in data %}
{% set package = row[0] %}
<a class="list-group-item list-group-item-action" href="{{ package.get_url('packages.list_releases') }}">
<div class="row">
<div class="col">
{{ _("%(title)s by %(author)s", title=package.title, author=package.author.display_name) }}
{% if package.state.name != "APPROVED" %}
<span class="badge bg-warning">
{{ package.state.value }}
</span>
{% endif %}
</div>
<div class="col-2 text-center">
{{ (row[4] / 1048576) | round | int }}
</div>
<div class="col-2 text-center">
{{ (row[2] / 1048576) | round | int }}
</div>
<div class="col-2 text-center">
{{ (row[3] / 1048576) | round | int }}
</div>
<div class="col-2 text-center">
{{ (row[1] / 1048576) | round | int }}
</div>
</div>
</a>
{% else %}
<div class="list-group-item text-muted">
<i>{{ _("No results") }}</i>
</div>
{% endfor %}
</div>
{% endblock %}