diff --git a/components/events/DateFilterGraph.vue b/components/events/DateFilterGraph.vue new file mode 100644 index 0000000..6554218 --- /dev/null +++ b/components/events/DateFilterGraph.vue @@ -0,0 +1,150 @@ + + + + + + + + Event Viewer + + + + + + + + + Start Date + + + + - + + + + + End Date + + + + + + + + + + + + {{ props.eventCount }} events + + + {{ props.eventCount }} event + + + + + + + + diff --git a/components/events/EventViewer.vue b/components/events/EventViewer.vue new file mode 100644 index 0000000..0c0d262 --- /dev/null +++ b/components/events/EventViewer.vue @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + FILTERS: ({{ appliedFilters.length }}) + + + + + + + + + + + + { + appliedFilters = []; + } + " + /> + + + + No events found. + + + + + + + + + + + + + {{ formatEventName(data.event_name) }} + + + + + + + + {{ data.body }} + + + + + + + + + + + + + + diff --git a/components/events/TagFilterSidePanel.vue b/components/events/TagFilterSidePanel.vue new file mode 100644 index 0000000..294a42c --- /dev/null +++ b/components/events/TagFilterSidePanel.vue @@ -0,0 +1,136 @@ + + + + + + + + + + Tag Filters + + + + + + + + + + + + + {{ item.label }} + + + + {{ item.label }} + + + + + + + + + diff --git a/components/header/MenuHeader.vue b/components/header/MenuHeader.vue index 470abe7..149e004 100644 --- a/components/header/MenuHeader.vue +++ b/components/header/MenuHeader.vue @@ -22,6 +22,11 @@ const items = ref([ icon: "pi pi-lightbulb", route: "/analyses", }, + { + label: "Events", + icon: "pi pi-list", + route: "/events", + }, { label: "Data Stores", icon: "pi pi-warehouse", diff --git a/composables/useAPIFetch.ts b/composables/useAPIFetch.ts index 0cd2e7e..13fc9c4 100644 --- a/composables/useAPIFetch.ts +++ b/composables/useAPIFetch.ts @@ -1,10 +1,11 @@ import type { AnalysisNode, - BodyCreateRouteToDatastoreKongProjectPost, DeleteProject, DetailedAnalysis, + EventLogResponse, LinkDataStoreProject, ListConsumers, + ListRoutes, ListServices, Project, ProjectNode, @@ -23,6 +24,14 @@ export function useAPIFetch( }); } +// Event endpoints +export function getEvents(opts?) { + return useAPIFetch("/events", { + ...opts, + method: "GET", + }); +} + // Hub endpoints export function getProjectNodes(opts?) { return useAPIFetch("/project-nodes", { @@ -91,10 +100,7 @@ export function deleteDataStore(dataStoreName: string, opts?) { }); } -export function createProject( - routeProps: BodyCreateRouteToDatastoreKongProjectPost, - opts?, -) { +export function createProject(routeProps: ListRoutes, opts?) { return useAPIFetch<{ data: LinkDataStoreProject }>(`/kong/project`, { ...opts, method: "POST", diff --git a/package.json b/package.json index f58c6ec..b22ef1c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.3.2", "license": "Apache-2.0", "description": "User interface for the FLAME Node software.", - "packageManager": "pnpm@10.17.1", + "packageManager": "pnpm@10.28.2", "type": "module", "scripts": { "start": "nuxt start", @@ -29,6 +29,7 @@ "@sidebase/nuxt-auth": "1.1.0", "@types/uuid": "^9.0.8", "@vueuse/core": "^11.3.0", + "chart.js": "^4.5.1", "globals": "^15.15.0", "next-auth": "~4.21.1", "nuxt": "^3.19.3", diff --git a/pages/events.vue b/pages/events.vue new file mode 100644 index 0000000..817ea91 --- /dev/null +++ b/pages/events.vue @@ -0,0 +1,9 @@ + + + + + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51a769d..e058b23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,9 @@ importers: '@vueuse/core': specifier: ^11.3.0 version: 11.3.0(vue@3.5.22(typescript@5.9.3)) + chart.js: + specifier: ^4.5.1 + version: 4.5.1 globals: specifier: ^15.15.0 version: 15.15.0 @@ -609,6 +612,9 @@ packages: engines: {node: '>= 12'} deprecated: Please upgrade to v15 or higher. All reported bugs in this version are fixed in newer releases, dependencies have been updated, and security has been improved. + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + '@kwsites/file-exists@1.1.1': resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} @@ -2046,6 +2052,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chart.js@4.5.1: + resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} + engines: {pnpm: '>=8'} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -5827,6 +5837,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@kurkle/color@0.3.4': {} + '@kwsites/file-exists@1.1.1': dependencies: debug: 4.4.3 @@ -7507,6 +7519,10 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chart.js@4.5.1: + dependencies: + '@kurkle/color': 0.3.4 + check-error@2.1.1: {} chokidar@3.6.0: diff --git a/server/routes/flame/api/auth/[...].ts b/server/routes/flame/api/auth/[...].ts index 42b7958..278cf1c 100644 --- a/server/routes/flame/api/auth/[...].ts +++ b/server/routes/flame/api/auth/[...].ts @@ -137,6 +137,36 @@ async function refreshAccessToken(token: any) { export default NuxtAuthHandler({ secret: useRuntimeConfig().authSecret, + events: { + async signIn({ account }) { + // After successful sign in + const hubAdapterApi = process.env.NUXT_PUBLIC_HUB_ADAPTER_URL; + if (!hubAdapterApi || !account?.access_token) return; + const signOutEndpoint = `${hubAdapterApi.replace(/\/$/, "")}/events/signin`; + try { + await fetch(signOutEndpoint, { + headers: { Authorization: `Bearer ${account.access_token}` }, + method: "POST", + }); + } catch (error) { + console.error("Failed to log sign-in event:", error); + } + }, + async signOut({ token }) { + // After successful sign out + const hubAdapterApi = process.env.NUXT_PUBLIC_HUB_ADAPTER_URL; + if (!hubAdapterApi || !token?.access_token) return; + const signOutEndpoint = `${hubAdapterApi.replace(/\/$/, "")}/events/signout`; + try { + await fetch(signOutEndpoint, { + headers: { Authorization: `Bearer ${token.access_token}` }, + method: "POST", + }); + } catch (error) { + console.error("Failed to log sign-out event:", error); + } + }, + }, callbacks: { /* on session retrieval */ async session({ session, token }) { diff --git a/test/components/header/MenuHeader.spec.ts b/test/components/header/MenuHeader.spec.ts index b9bdbe7..7ada4f1 100644 --- a/test/components/header/MenuHeader.spec.ts +++ b/test/components/header/MenuHeader.spec.ts @@ -8,7 +8,13 @@ describe("MenuHeader.vue", () => { async function menuHeaderChecks(authenticated: boolean) { const status = authenticated ? "authenticated" : "unauthenticated"; - const menuTitles = ["Home", "Projects", "Analyses", "Data Stores"]; + const menuTitles = [ + "Home", + "Projects", + "Analyses", + "Events", + "Data Stores", + ]; vi.stubGlobal("useAuth", () => ({ status: ref(status), diff --git a/types/eventTag.ts b/types/eventTag.ts new file mode 100644 index 0000000..4267ca5 --- /dev/null +++ b/types/eventTag.ts @@ -0,0 +1,21 @@ +export const EventServiceTag = { + Hub: "Hub", + HubAdapter: "Hub Adapter", + PodOrchestrator: "Pod Orchestrator", + Storage: "Storage", + Kong: "Kong", + Authentication: "Authentication", + Autostart: "Autostart", +}; + +export const EventLogLevelTag = { + Info: "Info", + Warning: "Warning", + Error: "Error", +}; + +export type EventServiceTag = + (typeof EventServiceTag)[keyof typeof EventServiceTag]; +export type EventLogLevelTag = + (typeof EventLogLevelTag)[keyof typeof EventLogLevelTag]; +export type EventTag = EventServiceTag | EventLogLevelTag;