154 lines
5.2 KiB
TypeScript
154 lines
5.2 KiB
TypeScript
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config.js";
|
|
import { AKChart } from "@goauthentik/elements/charts/Chart";
|
|
import "@goauthentik/elements/forms/ConfirmationForm";
|
|
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
|
import { ChartData, ChartOptions } from "chart.js";
|
|
|
|
import { msg } from "@lit/localize";
|
|
import { customElement } from "lit/decorators.js";
|
|
|
|
import { ProvidersApi, SourcesApi, SyncStatus, SystemTaskStatusEnum } from "@goauthentik/api";
|
|
|
|
export interface SummarizedSyncStatus {
|
|
healthy: number;
|
|
failed: number;
|
|
unsynced: number;
|
|
total: number;
|
|
label: string;
|
|
}
|
|
|
|
@customElement("ak-admin-status-chart-sync")
|
|
export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
|
getChartType(): string {
|
|
return "doughnut";
|
|
}
|
|
|
|
getOptions(): ChartOptions {
|
|
return {
|
|
plugins: {
|
|
legend: {
|
|
display: false,
|
|
},
|
|
},
|
|
maintainAspectRatio: false,
|
|
};
|
|
}
|
|
|
|
async fetchStatus<T>(
|
|
listObjects: () => Promise<PaginatedResponse<T>>,
|
|
fetchSyncStatus: (element: T) => Promise<SyncStatus>,
|
|
label: string,
|
|
): Promise<SummarizedSyncStatus> {
|
|
const objects = await listObjects();
|
|
const metrics: { [key: string]: number } = {
|
|
healthy: 0,
|
|
failed: 0,
|
|
unsynced: 0,
|
|
};
|
|
await Promise.all(
|
|
objects.results.map(async (element) => {
|
|
// Each source should have 3 successful tasks, so the worst task overwrites
|
|
let objectKey = "healthy";
|
|
try {
|
|
const status = await fetchSyncStatus(element);
|
|
status.tasks.forEach((task) => {
|
|
if (task.status !== SystemTaskStatusEnum.Successful) {
|
|
objectKey = "failed";
|
|
}
|
|
const now = new Date().getTime();
|
|
const maxDelta = 3600000; // 1 hour
|
|
if (!status || now - task.finishTimestamp.getTime() > maxDelta) {
|
|
objectKey = "unsynced";
|
|
}
|
|
});
|
|
} catch {
|
|
objectKey = "unsynced";
|
|
}
|
|
metrics[objectKey] += 1;
|
|
}),
|
|
);
|
|
return {
|
|
healthy: metrics.healthy,
|
|
failed: metrics.failed,
|
|
unsynced: objects.pagination.count === 0 ? 1 : metrics.unsynced,
|
|
total: objects.pagination.count,
|
|
label: label,
|
|
};
|
|
}
|
|
|
|
async apiRequest(): Promise<SummarizedSyncStatus[]> {
|
|
const statuses = [
|
|
await this.fetchStatus(
|
|
() => {
|
|
return new ProvidersApi(DEFAULT_CONFIG).providersScimList();
|
|
},
|
|
(element) => {
|
|
return new ProvidersApi(DEFAULT_CONFIG).providersScimSyncStatusRetrieve({
|
|
id: element.pk,
|
|
});
|
|
},
|
|
msg("SCIM Provider"),
|
|
),
|
|
await this.fetchStatus(
|
|
() => {
|
|
return new ProvidersApi(DEFAULT_CONFIG).providersGoogleWorkspaceList();
|
|
},
|
|
(element) => {
|
|
return new ProvidersApi(
|
|
DEFAULT_CONFIG,
|
|
).providersGoogleWorkspaceSyncStatusRetrieve({
|
|
id: element.pk,
|
|
});
|
|
},
|
|
msg("Google Workspace Provider"),
|
|
),
|
|
await this.fetchStatus(
|
|
() => {
|
|
return new ProvidersApi(DEFAULT_CONFIG).providersMicrosoftEntraList();
|
|
},
|
|
(element) => {
|
|
return new ProvidersApi(
|
|
DEFAULT_CONFIG,
|
|
).providersMicrosoftEntraSyncStatusRetrieve({
|
|
id: element.pk,
|
|
});
|
|
},
|
|
msg("Microsoft Entra Provider"),
|
|
),
|
|
await this.fetchStatus(
|
|
() => {
|
|
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapList();
|
|
},
|
|
(element) => {
|
|
return new SourcesApi(DEFAULT_CONFIG).sourcesLdapSyncStatusRetrieve({
|
|
slug: element.slug,
|
|
});
|
|
},
|
|
msg("LDAP Source"),
|
|
),
|
|
];
|
|
this.centerText = statuses.reduce((total, el) => (total += el.total), 0).toString();
|
|
return statuses;
|
|
}
|
|
|
|
getChartData(data: SummarizedSyncStatus[]): ChartData {
|
|
return {
|
|
labels: [msg("Healthy"), msg("Failed"), msg("Unsynced / N/A")],
|
|
datasets: data.map((d) => {
|
|
return {
|
|
backgroundColor: ["#3e8635", "#C9190B", "#2b9af3"],
|
|
spanGaps: true,
|
|
data: [d.healthy, d.failed, d.unsynced],
|
|
label: d.label,
|
|
};
|
|
}),
|
|
};
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ak-admin-status-chart-sync": SyncStatusChart;
|
|
}
|
|
}
|