Added information page about components in UI v2

This commit is contained in:
Antoine Nguyen
2023-01-25 13:57:44 +01:00
parent 354ab68840
commit 2d9ae1c89b
12 changed files with 155 additions and 4 deletions
+7
View File
@@ -0,0 +1,7 @@
import repository from './repository'
export default {
getComponentsInformation () {
return repository.get('/admin/components')
}
}
@@ -0,0 +1,72 @@
<template>
<v-card class="mt-6">
<v-card-title>
<translate>Installed components</translate>
</v-card-title>
<v-card-text>
<v-alert
outlined
type="success"
text
border="left"
v-if="updatesAvailable"
>
<translate tag="p">One or more updates are available</translate>
<translate tag="p" class="text-body-2">
Check out the following list to find related components
</translate>
</v-alert>
<v-data-table
:headers="headers"
:items="components"
:item-class="getItemBackground"
hide-default-footer
>
<template v-slot:item.last_version="{ item }">
<template v-if="item.changelog_url">
<a :href="item.changelog_url" target="_blank">{{ item.last_version }}</a>
</template>
<template v-else>
{{ item.last_version }}
</template>
</template>
</v-data-table>
</v-card-text>
</v-card>
</template>
<script>
import adminApi from '@/api/admin'
export default {
computed: {
updatesAvailable () {
return this.components.filter(item => item.update).length > 0
}
},
data () {
return {
components: [],
headers: [
{ text: this.$gettext('Name'), value: 'name' },
{ text: this.$gettext('Installed version'), value: 'version' },
{ text: this.$gettext('Latest version'), value: 'last_version' },
{ text: this.$gettext('Description'), value: 'description' }
]
}
},
methods: {
getItemBackground (item) {
if (item.update) {
return 'green lighten-5'
}
return ''
}
},
mounted () {
adminApi.getComponentsInformation().then(resp => {
this.components = resp.data
})
}
}
</script>
@@ -165,7 +165,7 @@ export default {
headers: [
{ text: this.$gettext('Name'), value: 'name' },
{ text: this.$gettext('Aliases'), value: 'domainalias_count' },
{ text: this.$gettext('DNS status'), value: 'dns_global_status', sortable: false, align: 'center' },
{ text: this.$gettext('DNS status'), value: 'dns_global_status', sortable: false },
{ text: this.$gettext('Sending limit'), value: 'message_limit' },
{ text: this.$gettext('Quota'), value: 'allocated_quota_in_percent' },
{ text: this.$gettext('Actions'), value: 'actions', sortable: false, align: 'right' }
@@ -201,6 +201,12 @@ export default {
text: this.$gettext('Settings'),
children: [],
roles: ['SuperAdmins']
},
{
icon: 'mdi-information',
text: this.$gettext('Information'),
roles: ['SuperAdmins'],
to: { name: 'Information' }
}
],
userSettingsMenuItems: [
+9
View File
@@ -136,6 +136,15 @@ const routes = [
allowedRoles: ['DomainAdmins', 'SuperAdmins']
}
},
{
path: '/information',
name: 'Information',
component: () => import('../views/admin/Information.vue'),
meta: {
requiresAuth: true,
allowedRoles: ['SuperAdmins']
}
},
{
path: '/user/api',
name: 'APISetup',
+19
View File
@@ -0,0 +1,19 @@
<template>
<div>
<v-toolbar flat>
<v-toolbar-title><translate>Information</translate></v-toolbar-title>
</v-toolbar>
<components-information-table />
</div>
</template>
<script>
import ComponentsInformationTable from '@/components/admin/ComponentsInformationTable'
export default {
components: {
ComponentsInformationTable
}
}
</script>
@@ -35,7 +35,7 @@
</v-btn>
</div>
<div>
<a @click="returnLogin" class="float-right primary--text"><translate>Return to login?</translate></a>
<a @click="returnLogin" class="float-right primary--text"><translate>Return to login</translate></a>
</div>
</v-col>
<confirm-dialog ref="dialog_response">
+12
View File
@@ -468,3 +468,15 @@ class PasswordRecoveryConfirmSerializer(serializers.Serializer):
user.set_password(self.validated_data["new_password1"])
user.save()
return True
class ModoboaComponentSerializer(serializers.Serializer):
"""Serializer used for information endpoint."""
label = serializers.CharField()
name = serializers.CharField()
version = serializers.CharField()
last_version = serializers.CharField(required=False)
description = serializers.CharField()
update = serializers.BooleanField(default=False)
changelog_url = serializers.URLField(required=False)
+8
View File
@@ -441,3 +441,11 @@ class LanguageViewSetTestCase(ModoAPITestCase):
url = reverse("v2:language-list")
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
class ComponentAPITestCase(ModoAPITestCase):
def test_information(self):
url = reverse("v2:components_information")
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
+2
View File
@@ -28,4 +28,6 @@ urlpatterns += [
name="password_reset_confirm_v2"),
path('sms_totp/', views.PasswordResetSmsTOTP.as_view(),
name="sms_totp"),
path('admin/components/', views.ComponentsInformationAPIView.as_view(),
name="components_information"),
]
+16 -1
View File
@@ -8,12 +8,14 @@ from django.utils.datastructures import MultiValueDictKeyError
from django.contrib.auth import login
from drf_spectacular.utils import extend_schema
from rest_framework import response, status
from rest_framework_simplejwt import views as jwt_views
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
from rest_framework.views import APIView
from modoboa.core.password_hashers import get_password_hasher
from modoboa.core.utils import check_for_updates
from modoboa.parameters import tools as param_tools
from smtplib import SMTPException
@@ -145,7 +147,8 @@ class PasswordResetConfirmView(APIView):
""" Get and set new user password. """
def post(self, request, *args, **kwargs):
serializer = serializers.PasswordRecoveryConfirmSerializer(data=request.data)
serializer = serializers.PasswordRecoveryConfirmSerializer(
data=request.data)
try:
serializer.is_valid(raise_exception=True)
except serializers.PasswordRequirementsFailure as e:
@@ -157,3 +160,15 @@ class PasswordResetConfirmView(APIView):
return response.Response(data, 400)
serializer.save()
return response.Response(status=200)
class ComponentsInformationAPIView(APIView):
"""Retrieve information about installed components."""
@extend_schema(responses=serializers.ModoboaComponentSerializer(many=True))
def get(self, request, *args, **kwargs):
status, extensions = check_for_updates()
serializer = serializers.ModoboaComponentSerializer(
extensions, many=True
)
return response.Response(serializer.data)
+2 -1
View File
@@ -528,10 +528,11 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
self._add_visibilty_rules(
sms_backends.get_all_backend_visibility_rules()
)
available_schemes = get_dovecot_schemes()
self.fields["password_scheme"].choices = [
(hasher.name, ugettext_lazy(hasher.label))
for hasher in PasswordHasher.get_password_hashers()
if hasher().scheme in get_dovecot_schemes()
if hasher().scheme in available_schemes
]
def _add_dynamic_fields(self):