add event-to-color map
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -1,14 +1,14 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { AKChart } from "@goauthentik/elements/charts/Chart";
|
||||
import { ChartData, ChartDataset } from "chart.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import { EventActions, EventVolume, EventsApi } from "@goauthentik/api";
|
||||
import { EventChart } from "#elements/charts/EventChart";
|
||||
|
||||
@customElement("ak-charts-admin-login-authorization")
|
||||
export class AdminLoginAuthorizeChart extends AKChart<EventVolume[]> {
|
||||
export class AdminLoginAuthorizeChart extends EventChart {
|
||||
async apiRequest(): Promise<EventVolume[]> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
|
||||
actions: [
|
||||
@ -23,8 +23,6 @@ export class AdminLoginAuthorizeChart extends AKChart<EventVolume[]> {
|
||||
const optsMap = new Map<EventActions, Partial<ChartDataset>>();
|
||||
optsMap.set(EventActions.AuthorizeApplication, {
|
||||
label: msg("Authorizations"),
|
||||
backgroundColor: "rgba(43, 154, 243, 0.5)",
|
||||
borderColor: "rgba(43, 154, 243, 1)",
|
||||
spanGaps: true,
|
||||
fill: "origin",
|
||||
cubicInterpolationMode: "monotone",
|
||||
@ -32,8 +30,6 @@ export class AdminLoginAuthorizeChart extends AKChart<EventVolume[]> {
|
||||
});
|
||||
optsMap.set(EventActions.Login, {
|
||||
label: msg("Successful Logins"),
|
||||
backgroundColor: "rgba(62, 134, 53, 0.5)",
|
||||
borderColor: "rgba(62, 134, 53, 1)",
|
||||
spanGaps: true,
|
||||
fill: "origin",
|
||||
cubicInterpolationMode: "monotone",
|
||||
@ -41,8 +37,6 @@ export class AdminLoginAuthorizeChart extends AKChart<EventVolume[]> {
|
||||
});
|
||||
optsMap.set(EventActions.LoginFailed, {
|
||||
label: msg("Failed Logins"),
|
||||
backgroundColor: "rgba(201, 24, 11, 0.5)",
|
||||
borderColor: "rgba(201, 24, 11, 1)",
|
||||
spanGaps: true,
|
||||
fill: "origin",
|
||||
cubicInterpolationMode: "monotone",
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { AKChart } from "@goauthentik/elements/charts/Chart";
|
||||
import { ChartData } from "chart.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -11,9 +10,10 @@ import {
|
||||
EventsApi,
|
||||
EventsEventsVolumeListRequest,
|
||||
} from "@goauthentik/api";
|
||||
import { EventChart } from "#elements/charts/EventChart";
|
||||
|
||||
@customElement("ak-charts-admin-model-per-day")
|
||||
export class AdminModelPerDay extends AKChart<EventVolume[]> {
|
||||
export class AdminModelPerDay extends EventChart {
|
||||
@property()
|
||||
action: EventActions = EventActions.ModelCreated;
|
||||
|
||||
@ -38,7 +38,6 @@ export class AdminModelPerDay extends AKChart<EventVolume[]> {
|
||||
this.action,
|
||||
{
|
||||
label: this.label || msg("Objects created"),
|
||||
backgroundColor: "rgba(189, 229, 184, .5)",
|
||||
spanGaps: true,
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { AKChart } from "@goauthentik/elements/charts/Chart";
|
||||
import { ChartData } from "chart.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { EventActions, EventVolume, EventsApi } from "@goauthentik/api";
|
||||
import { EventChart } from "#elements/charts/EventChart";
|
||||
|
||||
@customElement("ak-charts-application-authorize")
|
||||
export class ApplicationAuthorizeChart extends AKChart<EventVolume[]> {
|
||||
export class ApplicationAuthorizeChart extends EventChart {
|
||||
@property({ attribute: "application-id" })
|
||||
applicationId!: string;
|
||||
|
||||
@ -26,7 +26,6 @@ export class ApplicationAuthorizeChart extends AKChart<EventVolume[]> {
|
||||
EventActions.AuthorizeApplication,
|
||||
{
|
||||
label: msg("Authorizations"),
|
||||
backgroundColor: "rgba(189, 229, 184, .5)",
|
||||
spanGaps: true,
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { AKChart } from "@goauthentik/elements/charts/Chart";
|
||||
import { ChartData } from "chart.js";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
@ -8,9 +7,10 @@ import { customElement, property } from "lit/decorators.js";
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
|
||||
import { EventVolume, EventsApi, EventsEventsListRequest } from "@goauthentik/api";
|
||||
import { EventChart } from "#elements/charts/EventChart";
|
||||
|
||||
@customElement("ak-events-volume-chart")
|
||||
export class EventVolumeChart extends AKChart<EventVolume[]> {
|
||||
export class EventVolumeChart extends EventChart {
|
||||
_query?: EventsEventsListRequest;
|
||||
|
||||
@property({ attribute: false })
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { AKChart } from "@goauthentik/elements/charts/Chart";
|
||||
import { ChartData } from "chart.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { EventActions, EventVolume, EventsApi } from "@goauthentik/api";
|
||||
import { EventChart } from "#elements/charts/EventChart";
|
||||
|
||||
@customElement("ak-charts-user")
|
||||
export class UserChart extends AKChart<EventVolume[]> {
|
||||
export class UserChart extends EventChart {
|
||||
@property()
|
||||
username?: string;
|
||||
|
||||
@ -30,7 +30,6 @@ export class UserChart extends AKChart<EventVolume[]> {
|
||||
EventActions.LoginFailed,
|
||||
{
|
||||
label: msg("Failed Logins"),
|
||||
backgroundColor: "rgba(201, 25, 11, .5)",
|
||||
spanGaps: true,
|
||||
},
|
||||
],
|
||||
@ -38,7 +37,6 @@ export class UserChart extends AKChart<EventVolume[]> {
|
||||
EventActions.Login,
|
||||
{
|
||||
label: msg("Successful Logins"),
|
||||
backgroundColor: "rgba(189, 229, 184, .5)",
|
||||
spanGaps: true,
|
||||
},
|
||||
],
|
||||
@ -46,7 +44,6 @@ export class UserChart extends AKChart<EventVolume[]> {
|
||||
EventActions.AuthorizeApplication,
|
||||
{
|
||||
label: msg("Application authorizations"),
|
||||
backgroundColor: "rgba(43, 154, 243, .5)",
|
||||
spanGaps: true,
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { actionToLabel } from "#common/labels";
|
||||
import { EVENT_REFRESH, EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
|
||||
import {
|
||||
APIError,
|
||||
@ -30,7 +29,7 @@ import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
|
||||
import { EventActions, EventVolume, UiThemeEnum } from "@goauthentik/api";
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
Chart.register(Legend, Tooltip);
|
||||
Chart.register(LineController, BarController, DoughnutController);
|
||||
@ -40,32 +39,6 @@ Chart.register(TimeScale, TimeSeriesScale, LinearScale, Filler);
|
||||
export const FONT_COLOUR_DARK_MODE = "#fafafa";
|
||||
export const FONT_COLOUR_LIGHT_MODE = "#151515";
|
||||
|
||||
export class RGBAColor {
|
||||
constructor(
|
||||
public r: number,
|
||||
public g: number,
|
||||
public b: number,
|
||||
public a: number = 1,
|
||||
) {}
|
||||
toString(): string {
|
||||
return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`;
|
||||
}
|
||||
}
|
||||
|
||||
export function getColorFromString(stringInput: string): RGBAColor {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < stringInput.length; i++) {
|
||||
hash = stringInput.charCodeAt(i) + ((hash << 5) - hash);
|
||||
hash = hash & hash;
|
||||
}
|
||||
const rgb = [0, 0, 0];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const value = (hash >> (i * 8)) & 255;
|
||||
rgb[i] = value;
|
||||
}
|
||||
return new RGBAColor(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
export abstract class AKChart<T> extends AKElement {
|
||||
abstract apiRequest(): Promise<T>;
|
||||
abstract getChartData(data: T): ChartData;
|
||||
@ -238,59 +211,4 @@ export abstract class AKChart<T> extends AKElement {
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
eventVolume(
|
||||
data: EventVolume[],
|
||||
options?: {
|
||||
optsMap?: Map<EventActions, Partial<ChartDataset>>;
|
||||
padToDays?: number;
|
||||
},
|
||||
): ChartData {
|
||||
const datasets: ChartData = {
|
||||
datasets: [],
|
||||
};
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.optsMap) {
|
||||
options.optsMap = new Map<EventActions, Partial<ChartDataset>>();
|
||||
}
|
||||
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.time.getTime(),
|
||||
y: v.count,
|
||||
});
|
||||
});
|
||||
// Check if we need to pad the data to reach a certain time window
|
||||
const earliestDate = data
|
||||
.filter((v) => v.action === action)
|
||||
.map((v) => v.time)
|
||||
.sort((a, b) => b.getTime() - a.getTime())
|
||||
.reverse();
|
||||
if (earliestDate.length > 0 && options.padToDays) {
|
||||
const earliestPadded = new Date(
|
||||
new Date().getTime() - options.padToDays * (1000 * 3600 * 24),
|
||||
);
|
||||
const daysDelta = Math.round(
|
||||
(earliestDate[0].getTime() - earliestPadded.getTime()) / (1000 * 3600 * 24),
|
||||
);
|
||||
if (daysDelta > 0) {
|
||||
actionData.push({
|
||||
x: earliestPadded.getTime(),
|
||||
y: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
datasets.datasets.push({
|
||||
data: actionData,
|
||||
label: actionToLabel(action),
|
||||
backgroundColor: getColorFromString(action).toString(),
|
||||
...options.optsMap?.get(action),
|
||||
});
|
||||
});
|
||||
return datasets;
|
||||
}
|
||||
}
|
||||
|
||||
122
web/src/elements/charts/EventChart.ts
Normal file
122
web/src/elements/charts/EventChart.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { actionToLabel } from "#common/labels";
|
||||
import { AKChart } from "#elements/charts/Chart";
|
||||
import { ChartData, ChartDataset } from "chart.js";
|
||||
|
||||
import { EventActions, EventVolume } from "@goauthentik/api";
|
||||
|
||||
|
||||
export function actionToColor(action: EventActions): string {
|
||||
switch (action) {
|
||||
case EventActions.AuthorizeApplication:
|
||||
return "#0060c0";
|
||||
case EventActions.ConfigurationError:
|
||||
return "#4cb140";
|
||||
case EventActions.EmailSent:
|
||||
return "#009596";
|
||||
case EventActions.FlowExecution:
|
||||
return "#f4c145";
|
||||
case EventActions.ImpersonationEnded:
|
||||
return "#a2d9d9";
|
||||
case EventActions.ImpersonationStarted:
|
||||
return "#a2d9d9";
|
||||
case EventActions.InvitationUsed:
|
||||
return "#8bc1f7";
|
||||
case EventActions.Login:
|
||||
return "#23511e";
|
||||
case EventActions.LoginFailed:
|
||||
return "#ec7a08";
|
||||
case EventActions.Logout:
|
||||
return "#f9e0a2";
|
||||
case EventActions.ModelCreated:
|
||||
return "#8f4700";
|
||||
case EventActions.ModelDeleted:
|
||||
return "#002f5d";
|
||||
case EventActions.ModelUpdated:
|
||||
return "#bde2b9";
|
||||
case EventActions.PasswordSet:
|
||||
return "#003737";
|
||||
case EventActions.PolicyException:
|
||||
return "#c58c00";
|
||||
case EventActions.PolicyExecution:
|
||||
return "#f4b678";
|
||||
case EventActions.PropertyMappingException:
|
||||
return "#519de9";
|
||||
case EventActions.SecretRotate:
|
||||
return "#38812f";
|
||||
case EventActions.SecretView:
|
||||
return "#73c5c5";
|
||||
case EventActions.SourceLinked:
|
||||
return "#f6d173";
|
||||
case EventActions.SuspiciousRequest:
|
||||
return "#c46100";
|
||||
case EventActions.SystemException:
|
||||
return "#004b95";
|
||||
case EventActions.SystemTaskException:
|
||||
return "#7cc674";
|
||||
case EventActions.SystemTaskExecution:
|
||||
return "#005f60";
|
||||
case EventActions.UpdateAvailable:
|
||||
return "#f0ab00";
|
||||
case EventActions.UserWrite:
|
||||
return "#ef9234";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
export abstract class EventChart extends AKChart<EventVolume[]> {
|
||||
eventVolume(
|
||||
data: EventVolume[],
|
||||
options?: {
|
||||
optsMap?: Map<EventActions, Partial<ChartDataset>>;
|
||||
padToDays?: number;
|
||||
},
|
||||
): ChartData {
|
||||
const datasets: ChartData = {
|
||||
datasets: [],
|
||||
};
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
if (!options.optsMap) {
|
||||
options.optsMap = new Map<EventActions, Partial<ChartDataset>>();
|
||||
}
|
||||
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.time.getTime(),
|
||||
y: v.count,
|
||||
});
|
||||
});
|
||||
// Check if we need to pad the data to reach a certain time window
|
||||
const earliestDate = data
|
||||
.filter((v) => v.action === action)
|
||||
.map((v) => v.time)
|
||||
.sort((a, b) => b.getTime() - a.getTime())
|
||||
.reverse();
|
||||
if (earliestDate.length > 0 && options.padToDays) {
|
||||
const earliestPadded = new Date(
|
||||
new Date().getTime() - options.padToDays * (1000 * 3600 * 24),
|
||||
);
|
||||
const daysDelta = Math.round(
|
||||
(earliestDate[0].getTime() - earliestPadded.getTime()) / (1000 * 3600 * 24),
|
||||
);
|
||||
if (daysDelta > 0) {
|
||||
actionData.push({
|
||||
x: earliestPadded.getTime(),
|
||||
y: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
datasets.datasets.push({
|
||||
data: actionData,
|
||||
label: actionToLabel(action),
|
||||
backgroundColor: actionToColor(action),
|
||||
...options.optsMap?.get(action),
|
||||
});
|
||||
});
|
||||
return datasets;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user