Merge pull request #3689 from modoboa/dashboard_statistics_widget

Added widget to display global stats about the instance
This commit is contained in:
Antoine Nguyen
2025-08-20 09:41:42 +02:00
committed by GitHub
7 changed files with 100 additions and 1 deletions
+3
View File
@@ -10,4 +10,7 @@ export default {
getNewsFeed() {
return repository.get('/admin/news_feed/')
},
getStatistics() {
return repository.get('/admin/statistics/')
},
}
@@ -0,0 +1,36 @@
<template>
<v-card :title="$gettext('Statistics')">
<v-card-text>
<v-table>
<tbody>
<tr>
<td>{{ $gettext('Domain count') }}</td>
<td>{{ statistics.domain_count }}</td>
</tr>
<tr>
<td>{{ $gettext('Domain alias count') }}</td>
<td>{{ statistics.domain_alias_count }}</td>
</tr>
<tr>
<td>{{ $gettext('Account count') }}</td>
<td>{{ statistics.account_count }}</td>
</tr>
<tr>
<td>{{ $gettext('Alias count') }}</td>
<td>{{ statistics.alias_count }}</td>
</tr>
</tbody>
</v-table>
</v-card-text>
</v-card>
</template>
<script setup>
import { ref } from 'vue'
import adminApi from '@/api/admin'
const statistics = ref({})
const response = await adminApi.getStatistics()
statistics.value = response.data
</script>
+10
View File
@@ -7,7 +7,12 @@
<v-row>
<v-col sm="12" md="6">
<NewsFeedWidget />
<GlobalStatisticsWidget
v-if="authStore.authUser.role === constants.SUPER_ADMIN"
class="mt-4"
/>
</v-col>
<v-col sm="12" md="6"> </v-col>
</v-row>
</div>
</template>
@@ -15,10 +20,15 @@
<script setup>
import { onMounted, ref } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useAuthStore } from '@/stores'
import GlobalStatisticsWidget from '@/components/admin/dashboard/GlobalStatisticsWidget'
import NewsFeedWidget from '@/components/admin/dashboard/NewsFeedWidget'
import parametersApi from '@/api/parameters'
import constants from '@/constants.json'
const { $gettext } = useGettext()
const authStore = useAuthStore()
const welcomeMsg = ref('')
+8
View File
@@ -692,3 +692,11 @@ class ThemeSerializer(serializers.Serializer):
theme_primary_color_light = serializers.CharField(default="#3688F9")
theme_secondary_color = serializers.CharField(default="#F18429")
theme_label_color = serializers.CharField(default="#616161")
class StatisticsSerializer(serializers.Serializer):
domain_count = serializers.IntegerField()
domain_alias_count = serializers.IntegerField()
account_count = serializers.IntegerField()
alias_count = serializers.IntegerField()
+18
View File
@@ -658,3 +658,21 @@ class NewsFeedAPIViewTestCase(ModoAPITestCase):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn("modoboa", response.json()[0]["link"])
class StatisticsAPIViewTestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls):
"""Create test data."""
super().setUpTestData()
factories.populate_database()
def test_get_statistics(self):
url = reverse("v2:statistics")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
stats = response.json()
self.assertEqual(stats["domain_count"], 2)
self.assertEqual(stats["account_count"], 5)
self.assertEqual(stats["alias_count"], 3)
+5
View File
@@ -42,6 +42,11 @@ urlpatterns += [
views.NewsFeedAPIView.as_view(),
name="news-feed",
),
path(
"admin/statistics/",
views.StatisticsAPIView.as_view(),
name="statistics",
),
path("capabilities/", views.CapabilitiesAPIView.as_view(), name="capabilities"),
path("theme/", views.ThemeAPIView.as_view(), name="theme"),
]
+20 -1
View File
@@ -14,7 +14,8 @@ from drf_spectacular.utils import extend_schema
from rest_framework import permissions, response
from rest_framework.views import APIView
from modoboa.core import signals
from modoboa.admin import models as admin_models
from modoboa.core import models, signals
from modoboa.core.utils import check_for_updates, get_capabilities
from modoboa.lib.permissions import IsSuperUser, IsPrivilegedUser
from modoboa.lib.throttle import (
@@ -226,3 +227,21 @@ class NewsFeedAPIView(APIView):
serializer = serializers.NewsFeedEntrySerializer(entries, many=True)
return response.Response(serializer.data)
class StatisticsAPIView(APIView):
"""Return some statistics about this modoboa instance."""
permission_classes = [permissions.IsAuthenticated, IsSuperUser]
throttle_classes = [UserLesserDdosUser]
@extend_schema(responses=serializers.StatisticsSerializer())
def get(self, request, *args, **kwargs):
data = {
"domain_count": admin_models.Domain.objects.count(),
"domain_alias_count": admin_models.DomainAlias.objects.count(),
"account_count": models.User.objects.count(),
"alias_count": admin_models.Alias.objects.filter(internal=False).count(),
}
serializer = serializers.StatisticsSerializer(data)
return response.Response(serializer.data)