From 533b51cb8c0f22ae004bdcf7dc9e8d627f665acb Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Fri, 6 Jun 2025 02:54:02 +0200 Subject: [PATCH] rework event volume Signed-off-by: Jens Langhammer --- authentik/events/api/events.py | 29 ++++++++--- schema.yml | 16 +++++- .../charts/AdminLoginAuthorizeChart.ts | 14 ++--- web/src/admin/events/EventVolumeChart.ts | 52 ++++++++++--------- 4 files changed, 73 insertions(+), 38 deletions(-) diff --git a/authentik/events/api/events.py b/authentik/events/api/events.py index 609161152f..762935cabe 100644 --- a/authentik/events/api/events.py +++ b/authentik/events/api/events.py @@ -4,15 +4,16 @@ from datetime import timedelta from json import loads import django_filters -from django.db.models.aggregates import Count +from django.db.models import Count from django.db.models.fields.json import KeyTextTransform, KeyTransform -from django.db.models.functions import ExtractDay, ExtractHour +from django.db.models.functions import ExtractDay, TruncDate from django.db.models.query_utils import Q +from django.utils.timezone import now from drf_spectacular.types import OpenApiTypes -from drf_spectacular.utils import OpenApiParameter, extend_schema +from drf_spectacular.utils import OpenApiParameter, extend_schema, inline_serializer from guardian.shortcuts import get_objects_for_user from rest_framework.decorators import action -from rest_framework.fields import DictField, IntegerField +from rest_framework.fields import CharField, DateField, DictField, IntegerField from rest_framework.request import Request from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet @@ -156,13 +157,29 @@ class EventViewSet(ModelViewSet): return Response(EventTopPerUserSerializer(instance=events, many=True).data) @extend_schema( - responses={200: CoordinateSerializer(many=True)}, + responses={ + 200: inline_serializer( + "EventVolume", + fields={ + "action": CharField(), + "day": DateField(), + "count": IntegerField(), + }, + many=True, + ) + }, ) @action(detail=False, methods=["GET"], pagination_class=None) def volume(self, request: Request) -> Response: """Get event volume for specified filters and timeframe""" queryset = self.filter_queryset(self.get_queryset()) - return Response(queryset.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)) + return Response( + queryset.filter(created__gte=now() - timedelta(days=14)) + .annotate(day=TruncDate("created")) + .values("day", "action") + .annotate(count=Count("pk")) + .order_by("-day", "action") + ) @extend_schema( responses={200: CoordinateSerializer(many=True)}, diff --git a/schema.yml b/schema.yml index e1c4b5fe0c..f7f49b387d 100644 --- a/schema.yml +++ b/schema.yml @@ -7540,7 +7540,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/Coordinate' + $ref: '#/components/schemas/EventVolume' description: '' '400': content: @@ -44986,6 +44986,20 @@ components: - application - counted_events - unique_users + EventVolume: + type: object + properties: + action: + type: string + day: + type: string + format: date + count: + type: integer + required: + - action + - count + - day EventsRequestedEnum: enum: - https://schemas.openid.net/secevent/caep/event-type/session-revoked diff --git a/web/src/admin/admin-overview/charts/AdminLoginAuthorizeChart.ts b/web/src/admin/admin-overview/charts/AdminLoginAuthorizeChart.ts index 59345a1e7c..eb1de9ec90 100644 --- a/web/src/admin/admin-overview/charts/AdminLoginAuthorizeChart.ts +++ b/web/src/admin/admin-overview/charts/AdminLoginAuthorizeChart.ts @@ -1,5 +1,5 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { AKChart, RGBAColor } from "@goauthentik/elements/charts/Chart"; +import { AKChart } from "@goauthentik/elements/charts/Chart"; import { ChartData } from "chart.js"; import { msg } from "@lit/localize"; @@ -18,8 +18,8 @@ export class AdminLoginAuthorizeChart extends AKChart { datasets: [ { label: msg("Authorizations"), - backgroundColor: new RGBAColor(43, 154, 243, 0.5).toString(), - borderColor: new RGBAColor(43, 154, 243, 1).toString(), + backgroundColor: "rgba(43, 154, 243, 0.5)", + borderColor: "rgba(43, 154, 243, 1)", spanGaps: true, fill: "origin", cubicInterpolationMode: "monotone", @@ -33,8 +33,8 @@ export class AdminLoginAuthorizeChart extends AKChart { }, { label: msg("Failed Logins"), - backgroundColor: new RGBAColor(201, 24, 11, 0.5).toString(), - borderColor: new RGBAColor(201, 24, 11, 1).toString(), + backgroundColor: "rgba(201, 24, 11, 0.5)", + borderColor: "rgba(201, 24, 11, 1)", spanGaps: true, fill: "origin", cubicInterpolationMode: "monotone", @@ -48,8 +48,8 @@ export class AdminLoginAuthorizeChart extends AKChart { }, { label: msg("Successful Logins"), - backgroundColor: new RGBAColor(62, 134, 53, 0.5).toString(), - borderColor: new RGBAColor(62, 134, 53, 1).toString(), + backgroundColor: "rgba(62, 134, 53, 0.5)", + borderColor: "rgba(62, 134, 53, 1)", spanGaps: true, fill: "origin", cubicInterpolationMode: "monotone", diff --git a/web/src/admin/events/EventVolumeChart.ts b/web/src/admin/events/EventVolumeChart.ts index 3ff3c49fd5..4287e64670 100644 --- a/web/src/admin/events/EventVolumeChart.ts +++ b/web/src/admin/events/EventVolumeChart.ts @@ -1,17 +1,17 @@ +import { actionToLabel } from "#common/labels"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { AKChart } from "@goauthentik/elements/charts/Chart"; +import { AKChart, getColorFromString } from "@goauthentik/elements/charts/Chart"; import { ChartData } from "chart.js"; -import { msg } from "@lit/localize"; import { CSSResult, TemplateResult, css, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import PFCard from "@patternfly/patternfly/components/Card/card.css"; -import { Coordinate, EventsApi, EventsEventsListRequest } from "@goauthentik/api"; +import { EventActions, EventVolume, EventsApi, EventsEventsListRequest } from "@goauthentik/api"; @customElement("ak-events-volume-chart") -export class EventVolumeChart extends AKChart { +export class EventVolumeChart extends AKChart { _query?: EventsEventsListRequest; @property({ attribute: false }) @@ -24,39 +24,43 @@ export class EventVolumeChart extends AKChart { return super.styles.concat( PFCard, css` - .pf-c-card__body { - height: 12rem; + .pf-c-card { + height: 20rem; } `, ); } - apiRequest(): Promise { + apiRequest(): Promise { return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList(this._query); } - getChartData(data: Coordinate[]): ChartData { - return { - datasets: [ - { - label: msg("Events"), - backgroundColor: "rgba(189, 229, 184, .5)", - spanGaps: true, - data: - data.map((cord) => { - return { - x: cord.xCord || 0, - y: cord.yCord || 0, - }; - }) || [], - }, - ], + getChartData(data: EventVolume[]): ChartData { + const datasets: ChartData = { + datasets: [], }; + // Get a list of all actions + const actions = new Set(data.map((v) => v.action)); + actions.forEach((action) => { + const actionData: { x: number; y: number }[] = []; + data.filter((v) => v.action === action).forEach((v) => { + actionData.push({ + x: v.day.getTime(), + y: v.count, + }); + }); + datasets.datasets.push({ + label: actionToLabel(action as EventActions), + backgroundColor: getColorFromString(action), + spanGaps: true, + data: actionData, + }); + }); + return datasets; } render(): TemplateResult { return html`
-
${msg("Event volume")}
${super.render()}
`; }