{% extends "administration/base.html" %} {% load i18n %} {% load static %} {% block content %} <section class="pf-c-page__main-section pf-m-light"> <div class="pf-c-content"> <h1>{% trans 'System Overview' %}</h1> </div> </section> <section class="pf-c-page__main-section"> <div class="pf-l-gallery pf-m-gutter"> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 3;grid-row-end: span 2;"> <div class="pf-c-card__header"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-server"></i> {% trans 'Logins over the last 24 hours' %} </div> </div> <div class="pf-c-card__body" style="position: relative; height:100%; width:100%"> <canvas id="logins-last-metrics"></canvas> </div> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-4-col" style="grid-column-end: span 2;grid-row-end: span 3;"> <div class="pf-c-card__header"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-server"></i> {% trans 'Apps with most usage' %} </div> </div> <div class="pf-c-card__body"> <table class="pf-c-table pf-m-compact" role="grid"> <thead> <tr role="row"> <th role="columnheader" scope="col">{% trans 'Application' %}</th> <th role="columnheader" scope="col">{% trans 'Logins' %}</th> <th role="columnheader" scope="col"></th> </tr> </thead> <tbody role="rowgroup"> {% for app in most_used_applications %} <tr role="row"> <td role="cell"> {{ app.application.name }} </td> <td role="cell"> {{ app.total_logins }} </td> <td role="cell"> <progress value="{{ app.total_logins }}" max="{{ most_used_applications.0.total_logins }}"></progress> </td> </tr> {% endfor %} </tbody> </table> </div> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact"> <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-plugged"></i> {% trans 'Providers' %} </div> <a href="{% url 'passbook_admin:providers' %}"> <i class="fa fa-external-link-alt"> </i> </a> </div> <div class="pf-c-card__body"> {% if providers_without_application.exists %} <p class="pb-aggregate-card"> <i class="fa fa-exclamation-triangle"></i> {{ provider_count }} </p> <p>{% trans 'Warning: At least one Provider has no application assigned.' %}</p> {% else %} <p class="pb-aggregate-card"> <i class="fa fa-check-circle"></i> {{ provider_count }} </p> {% endif %} </div> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact"> <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-infrastructure"></i> {% trans 'Policies' %} </div> <a href="{% url 'passbook_admin:policies' %}"> <i class="fa fa-external-link-alt"> </i> </a> </div> <div class="pf-c-card__body"> {% if policies_without_binding %} <p class="pb-aggregate-card"> <i class="fa fa-exclamation-triangle"></i> {{ policy_count }} </p> <p>{% trans 'Policies without binding exist.' %}</p> {% else %} <p class="pb-aggregate-card"> <i class="fa fa-check-circle"></i> {{ policy_count }} </p> {% endif %} </div> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact"> <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-user"></i> {% trans 'Users' %} </div> <a href="{% url 'passbook_admin:users' %}"> <i class="fa fa-external-link-alt"> </i> </a> </div> <div class="pf-c-card__body"> <p class="pb-aggregate-card"> <i class="fa fa-check-circle"></i> {{ user_count }} </p> </div> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact"> <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-bundle"></i> {% trans 'Version' %} </div> <a href="https://github.com/BeryJu/passbook/releases" target="_blank"> <i class="fa fa-external-link-alt"> </i> </a> </div> <div class="pf-c-card__body"> <p class="pb-aggregate-card"> {% if version >= version_latest %} <i class="fa fa-check-circle"></i> {{ version }} {% else %} <i class="fa fa-exclamation-triangle"></i> {{ version }} {% endif %} </p> {% if version >= version_latest %} {% blocktrans %} Up-to-date! {% endblocktrans %} {% else %} {% blocktrans with latest=version_latest %} {{ latest }} is available! {% endblocktrans %} {% endif %} </div> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact"> <div class="pf-c-card__header"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-server"></i> {% trans 'Workers' %} </div> </div> <fetch-fill-slot class="pf-c-card__body" url="{% url 'passbook_api:admin_overview-list' %}" key="worker_count"> <div slot="value < 1"> <p class="pb-aggregate-card"> <i class="fa fa-exclamation-triangle"></i> <span data-value></span> </p> <p>{% trans 'No workers connected.' %}</p> </div> <div slot="value >= 1"> <p class="pb-aggregate-card"> <i class="fa fa-check-circle"></i> <span data-value></span> </p> </div> <div> <span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading..."> <span class="pf-c-spinner__clipper"></span> <span class="pf-c-spinner__lead-ball"></span> <span class="pf-c-spinner__tail-ball"></span> </span> </div> </fetch-fill-slot> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact"> <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-server"></i> {% trans 'Cached Policies' %} </div> <a data-target="modal" data-modal="clearPolicyCache"> <i class="fa fa-trash"> </i> </a> </div> <div class="pf-c-card__body"> {% if cached_policies < 1 %} <p class="pb-aggregate-card"> <i class="fa fa-exclamation-triangle"></i> {{ cached_policies }} </p> <p>{% trans 'No policies cached. Users may experience slow response times.' %}</p> {% else %} <p class="pb-aggregate-card"> <i class="fa fa-check-circle"></i> {{ cached_policies }} </p> {% endif %} </div> </div> <div class="pf-c-card pf-c-card-aggregate pf-l-gallery__item pf-m-compact"> <div class="pf-c-card__header pf-l-flex pf-m-justify-content-space-between"> <div class="pf-c-card__header-main"> <i class="pf-icon pf-icon-server"></i> {% trans 'Cached Flows' %} </div> <a data-target="modal" data-modal="clearFlowCache"> <i class="fa fa-trash"> </i> </a> </div> <div class="pf-c-card__body"> {% if cached_flows < 1 %} <p class="pb-aggregate-card"> <span class="fa fa-exclamation-triangle"></span> {{ cached_flows }} </p> <p>{% trans 'No flows cached.' %}</p> {% else %} <p class="pb-aggregate-card"> <i class="fa fa-check-circle"></i> {{ cached_flows }} </p> {% endif %} </div> </div> </section> </div> <div class="pf-c-backdrop" id="clearPolicyCache" hidden> <div class="pf-l-bullseye"> <div class="pf-c-modal-box pf-m-sm" role="dialog"> <button data-modal-close class="pf-c-button pf-m-plain" type="button" aria-label="Close dialog"> <i class="fas fa-times" aria-hidden="true"></i> </button> <div class="pf-c-modal-box__header"> <h1 class="pf-c-title pf-m-2xl" id="modal-title">{% trans 'Clear Policy Cache' %}?</h1> </div> <div class="pf-c-modal-box__body" id="modal-description"> <form method="post" id="clear_policies"> {% csrf_token %} <input type="hidden" name="clear_policies"> <p> {% blocktrans %} Are you sure you want to clear the policy cache? This will cause all policies to be re-evaluated on their next usage. {% endblocktrans %} </p> </form> </div> <footer class="pf-c-modal-box__footer pf-m-align-left"> <button form="clear_policies" class="pf-c-button pf-m-primary" type="submit">{% trans 'Clear' %}</button> <button data-modal-close class="pf-c-button pf-m-link" type="button">{% trans 'Cancel' %}</button> </footer> </div> </div> </div> <div class="pf-c-backdrop" id="clearFlowCache" hidden> <div class="pf-l-bullseye"> <div class="pf-c-modal-box pf-m-sm" role="dialog"> <button data-modal-close class="pf-c-button pf-m-plain" type="button" aria-label="Close dialog"> <i class="fas fa-times" aria-hidden="true"></i> </button> <div class="pf-c-modal-box__header"> <h1 class="pf-c-title pf-m-2xl" id="modal-title">{% trans 'Clear Flow Cache' %}?</h1> </div> <div class="pf-c-modal-box__body" id="modal-description"> <form method="post" id="clear_flows"> {% csrf_token %} <input type="hidden" name="clear_flows"> <p> {% blocktrans %} Are you sure you want to clear the flow cache? This will cause all flows to be re-evaluated on their next usage. {% endblocktrans %} </p> </form> </div> <footer class="pf-c-modal-box__footer pf-m-align-left"> <button form="clear_flows" class="pf-c-button pf-m-primary" type="submit">{% trans 'Clear' %}</button> <button data-modal-close class="pf-c-button pf-m-link" type="button">{% trans 'Cancel' %}</button> </footer> </div> </div> </div> <script src="{% static 'node_modules/chart.js/dist/Chart.bundle.min.js' %}"></script> <script> var ctx = document.getElementById('logins-last-metrics').getContext('2d'); fetch("{% url 'passbook_api:admin_metrics-list' %}").then(r => r.json()).then(r => { var myChart = new Chart(ctx, { type: 'bar', data: { datasets: [ { label: 'Failed Logins', backgroundColor: "rgba(201, 25, 11, .5)", spanGaps: true, data: r.logins_failed_per_1h, }, { label: 'Successful Logins', backgroundColor: "rgba(189, 229, 184, .5)", spanGaps: true, data: r.logins_per_1h, }, ] }, options: { maintainAspectRatio: false, spanGaps: true, scales: { xAxes: [{ stacked: true, gridLines: { color: "rgba(0, 0, 0, 0)", }, type: 'time', offset: true, ticks: { callback: function (value, index, values) { const date = new Date(); const delta = (date - values[index].value); const ago = Math.round(delta / 1000 / 3600); return `${ago} Hours ago`; }, autoSkip: true, maxTicksLimit: 8 } }], yAxes: [{ stacked: true, gridLines: { color: "rgba(0, 0, 0, 0)", } }] } } }); }); </script> {% endblock %}