From b148921ea17f225b06c6b53639f656b8e798e80e Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Thu, 6 Mar 2025 14:56:43 +0800 Subject: [PATCH 01/16] include audit section displaying audit messages --- src/Frontend/src/components/PageHeader.vue | 2 + .../src/components/audit/AuditList.vue | 86 +++++++++++++++++++ .../src/components/audit/AuditMenuItem.vue | 16 ++++ src/Frontend/src/router/config.ts | 6 ++ src/Frontend/src/router/routeLinks.ts | 12 +++ src/Frontend/src/stores/AuditStore.ts | 80 +++++++++++++++++ src/Frontend/src/views/AuditView.vue | 21 +++++ 7 files changed, 223 insertions(+) create mode 100644 src/Frontend/src/components/audit/AuditList.vue create mode 100644 src/Frontend/src/components/audit/AuditMenuItem.vue create mode 100644 src/Frontend/src/stores/AuditStore.ts create mode 100644 src/Frontend/src/views/AuditView.vue diff --git a/src/Frontend/src/components/PageHeader.vue b/src/Frontend/src/components/PageHeader.vue index b47af2f52..ca8a89adf 100644 --- a/src/Frontend/src/components/PageHeader.vue +++ b/src/Frontend/src/components/PageHeader.vue @@ -12,6 +12,7 @@ import EventsMenuItem from "@/components/events/EventsMenuItem.vue"; import DashboardMenuItem from "@/components/dashboard/DashboardMenuItem.vue"; import FeedbackButton from "@/components/FeedbackButton.vue"; import ThroughputMenuItem from "@/views/throughputreport/ThroughputMenuItem.vue"; +import AuditMenuItem from "./audit/AuditMenuItem.vue"; // prettier-ignore const menuItems = computed( @@ -19,6 +20,7 @@ const menuItems = computed( DashboardMenuItem, HeartbeatsMenuItem, ...(useIsMonitoringEnabled() ? [MonitoringMenuItem] : []), + AuditMenuItem, FailedMessagesMenuItem, CustomChecksMenuItem, EventsMenuItem, diff --git a/src/Frontend/src/components/audit/AuditList.vue b/src/Frontend/src/components/audit/AuditList.vue new file mode 100644 index 000000000..c795fa2a9 --- /dev/null +++ b/src/Frontend/src/components/audit/AuditList.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/src/Frontend/src/components/audit/AuditMenuItem.vue b/src/Frontend/src/components/audit/AuditMenuItem.vue new file mode 100644 index 000000000..92896f606 --- /dev/null +++ b/src/Frontend/src/components/audit/AuditMenuItem.vue @@ -0,0 +1,16 @@ + + + + + diff --git a/src/Frontend/src/router/config.ts b/src/Frontend/src/router/config.ts index 0f719f134..ffb86f995 100644 --- a/src/Frontend/src/router/config.ts +++ b/src/Frontend/src/router/config.ts @@ -8,6 +8,7 @@ import routeLinks from "@/router/routeLinks"; import CustomChecksView from "@/views/CustomChecksView.vue"; import HeartbeatsView from "@/views/HeartbeatsView.vue"; import ThroughputReportView from "@/views/ThroughputReportView.vue"; +import AuditView from "@/views/AuditView.vue"; export interface RouteItem { path: string; @@ -52,6 +53,11 @@ const config: RouteItem[] = [ }, ], }, + { + path: routeLinks.audit.root, + component: AuditView, + title: "Messages", + }, { path: routeLinks.failedMessage.root, component: FailedMessagesView, diff --git a/src/Frontend/src/router/routeLinks.ts b/src/Frontend/src/router/routeLinks.ts index 284da580b..75ebb6964 100644 --- a/src/Frontend/src/router/routeLinks.ts +++ b/src/Frontend/src/router/routeLinks.ts @@ -12,6 +12,17 @@ const heartbeatLinks = (root: string) => { }; }; +const auditLinks = (root: string) => { + // function createLink(template: string) { + // return { link: `${root}/${template}`, template: template }; + // } + + return { + root, + message: { link: (id: string) => `${root}/message/${id}`, template: "message/:id" }, + }; +}; + const failedMessagesLinks = (root: string) => { function createLink(template: string) { return { link: `${root}/${template}`, template: template }; @@ -93,6 +104,7 @@ const routeLinks = { dashboard: "/dashboard", heartbeats: heartbeatLinks("/heartbeats"), monitoring: monitoringLinks("/monitoring"), + audit: auditLinks("/audit"), failedMessage: failedMessagesLinks("/failed-messages"), customChecks: "/custom-checks", events: "/events", diff --git a/src/Frontend/src/stores/AuditStore.ts b/src/Frontend/src/stores/AuditStore.ts new file mode 100644 index 000000000..cf994446f --- /dev/null +++ b/src/Frontend/src/stores/AuditStore.ts @@ -0,0 +1,80 @@ +import { useTypedFetchFromServiceControl } from "@/composables/serviceServiceControlUrls"; +import { acceptHMRUpdate, defineStore } from "pinia"; +import { ref, watch } from "vue"; +import useAutoRefresh from "@/composables/autoRefresh"; +//import { SortDirection, type GroupPropertyType } from "@/resources/SortOptions"; +import type { SortInfo } from "@/components/SortInfo"; +import Message from "@/resources/Message"; + +export enum ColumnNames { + Status = "status", + MessageId = "messageId", + MessageType = "messageType", + TimeSent = "timeSent", + ProcessingTime = "processingTime", +} + +// const columnSortings = new Map GroupPropertyType>([ +// // [ColumnNames.Name, (endpoint) => endpoint.name], +// // [ColumnNames.InstancesDown, (endpoint) => endpoint.alive_count - endpoint.down_count], +// // [ColumnNames.InstancesTotal, (endpoint) => endpoint.alive_count + endpoint.down_count], +// // [ColumnNames.LastHeartbeat, (endpoint) => moment.utc(endpoint.heartbeat_information?.last_report_at ?? "1975-01-01T00:00:00")], +// // [ColumnNames.Tracked, (endpoint) => endpoint.track_instances], +// // [ColumnNames.TrackToggle, (endpoint) => endpoint.track_instances], +// ]); + +export const useAuditStore = defineStore("AuditStore", () => { + const sortByInstances = ref({ + property: ColumnNames.MessageId, + isAscending: true, + }); + + const messageFilterString = ref(""); + const itemsPerPage = ref(20); + const messages = ref([]); + // const sortedMessages = computed(() => + // [...messages.value].sort(getSortFunction(columnSortings.get(sortByInstances.value.property), sortByInstances.value.isAscending ? SortDirection.Ascending : SortDirection.Descending)) + // ); + // const filteredMessages = computed(() => sortedMessages.value.filter((message) => !messageFilterString.value || message.id.toLowerCase().includes(messageFilterString.value.toLowerCase()))); + watch(messageFilterString, (newValue) => { + setMessageFilterString(newValue); + }); + + const dataRetriever = useAutoRefresh(async () => { + try { + const [, data] = await useTypedFetchFromServiceControl("messages"); + messages.value = data; + } catch (e) { + messages.value = []; + throw e; + } + }, 30000); + + function setMessageFilterString(filter: string) { + messageFilterString.value = filter; + } + + function setItemsPerPage(value: number) { + itemsPerPage.value = value; + } + + const refresh = dataRetriever.executeAndResetTimer; + + // eslint-disable-next-line promise/catch-or-return,promise/prefer-await-to-then,promise/valid-params + refresh().then(); + + return { + refresh, + sortByInstances, + messages, + messageFilterString, + itemsPerPage, + setItemsPerPage, + }; +}); + +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useAuditStore, import.meta.hot)); +} + +export type AuditStore = ReturnType; diff --git a/src/Frontend/src/views/AuditView.vue b/src/Frontend/src/views/AuditView.vue new file mode 100644 index 000000000..7b68f1010 --- /dev/null +++ b/src/Frontend/src/views/AuditView.vue @@ -0,0 +1,21 @@ + + + From 105cc68c7304d67450c5a8d3243f6a6a9d53585a Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Tue, 11 Mar 2025 07:29:39 +0800 Subject: [PATCH 02/16] show status icons --- .../src/components/audit/AuditList.vue | 99 ++++++++++++++++++- src/Frontend/src/resources/Message.ts | 57 ++++++++++- 2 files changed, 152 insertions(+), 4 deletions(-) diff --git a/src/Frontend/src/components/audit/AuditList.vue b/src/Frontend/src/components/audit/AuditList.vue index c795fa2a9..6449e70b5 100644 --- a/src/Frontend/src/components/audit/AuditList.vue +++ b/src/Frontend/src/components/audit/AuditList.vue @@ -4,10 +4,46 @@ import { ColumnNames, useAuditStore } from "@/stores/AuditStore"; import { storeToRefs } from "pinia"; import { useRoute } from "vue-router"; import DataView from "../DataView.vue"; +import SortableColumn from "../SortableColumn.vue"; +import { MessageStatus } from "@/resources/Message"; const route = useRoute(); const store = useAuditStore(); const { messages, sortByInstances, itemsPerPage } = storeToRefs(store); + +function statusToName(messageStatus: MessageStatus) { + switch (messageStatus) { + case MessageStatus.Successful: + return "Successful"; + case MessageStatus.ResolvedSuccessfully: + return "Successful after retries"; + case MessageStatus.Failed: + return "Failed"; + case MessageStatus.ArchivedFailure: + return "Failed message deleted"; + case MessageStatus.RepeatedFailure: + return "Repeated Failures"; + case MessageStatus.RetryIssued: + return "Retry requested"; + } +} + +function statusToIcon(messageStatus: MessageStatus) { + switch (messageStatus) { + case MessageStatus.Successful: + return "fa successful"; + case MessageStatus.ResolvedSuccessfully: + return "fa resolved-successfully"; + case MessageStatus.Failed: + return "fa failed"; + case MessageStatus.ArchivedFailure: + return "fa archived"; + case MessageStatus.RepeatedFailure: + return "fa repeated-failure"; + case MessageStatus.RetryIssued: + return "fa retry-issued"; + } +}