diff --git a/web/messages/en/common.json b/web/messages/en/common.json index 299403d007..95fc8c9d37 100644 --- a/web/messages/en/common.json +++ b/web/messages/en/common.json @@ -27,6 +27,7 @@ "controls_keep": "Keep", "controls_submit": "Submit", "controls_cancel": "Cancel", + "controls_add_destination": "Add destination", "controls_save": "Save", "controls_save_changes": "Save changes", "controls_sign_in": "Sign in", diff --git a/web/messages/en/modal.json b/web/messages/en/modal.json index 21237ecf70..58f0504d99 100644 --- a/web/messages/en/modal.json +++ b/web/messages/en/modal.json @@ -5,6 +5,7 @@ "modal_delete_authorized_app_title": "Delete authorized app", "modal_delete_authorized_app_content_1": "Are you sure you want to remove this application? If you do, you won't be able to use it to log in with defguard anymore.", "modal_delete_authorized_app_content_2": "Please note: until you log out from this application, it will remain signed in with DefGuard.", + "modal_delete_logstream_destination": " Are you sure you want to delete this activity log destination? All related logs will stop being sent to this location. This action cannot be undone.", "modal_mfa_enable_email_title": "Email MFA setup", "modal_mfa_enable_email_verification": "Email verification", "modal_mfa_enable_email_content": "To setup your MFA enter the code that was sent to your account email: {email}", @@ -74,12 +75,23 @@ "modal_add_api_token_copy_warning": "Please copy and save the API token below now. You won't be able to see it again.", "modal_rename_api_title": "Rename API token", "modal_add_user_title": "Add new user", + "modal_add_vector_destination_title": "Add Vector destination", + "modal_add_logstash_destination_title": "Add Logstash destination", + "modal_logstash_destination_title": "Logstash", + "modal_vector_destination_title": "Vector", + "modal_edit_vector_destination_title": "Edit Vector destination", + "modal_edit_logstash_destination_title": "Edit Logstash destination", + "modal_edit_log_streaming_destination_title": "Edit destination", + "modal_select_log_streaming_destination_title": "Select destination", + "modal_add_log_streaming_destination_title": "Add destination", "modal_add_user_groups_title": "Assign user to group(s)", "modal_add_user_enroll_title": "Start enrollment for user", "modal_add_user_choice_enroll_title": "Add user with self-enrollment option", "modal_add_user_choice_enroll_content": "User will be able to configure their account during the setup of their desktop client (e.g., set their own password, etc.).", "modal_add_user_choice_manual_title": "Add user manually", "modal_add_user_choice_manual_content": "The admin creates the user account by providing the details and assigning a password on their behalf.", + "modal_add_logstash_destination": "Logstash is an activity log provider that collects, processes, and streams event data from multiple sources in real time.", + "modal_add_vector_destination": "Vector is a high-performance observability data pipeline that collects, transforms, and routes logs, metrics, and events.", "modal_add_user_form_enable_enroll_label": "Enable user secure remote self-enrollment.", "modal_add_user_form_enable_enroll_help": "By enabling this option, the user will be able to configure their account during the setup of their desktop client (e.g., set their own password, etc.). ", "modal_add_user_submit": "Add user", diff --git a/web/messages/en/settings.json b/web/messages/en/settings.json new file mode 100644 index 0000000000..d2cdd0f964 --- /dev/null +++ b/web/messages/en/settings.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "settings_activity_log_streaming_title": "Activity log streaming", + "settings_activity_log_streaming_description": "Monitor and export real-time activity logs from your Defguard instance. Stream events to external systems for auditing, analytics, or security monitoring.", + "settings_activity_log_streaming_no_upstreams": "You don't have any activity log upstreams.", + "settings_activity_log_streaming_no_upstreams_subtitle": "Click the button below to add an activity log provider and start streaming events.", + "settings_activity_log_streaming_add_log_streaming_button": "Add log streaming.", + "settings_activity_log_streaming_delete_log_streaming_title": "Delete destination confirmation", + "settings_activity_log_streaming_table_title": "All log streams", + "settings_activity_log_streaming_table_header_name": "Name", + "settings_activity_log_streaming_table_stream_type_name": "Destination" +} diff --git a/web/project.inlang/settings.json b/web/project.inlang/settings.json index 6206c1b159..a0d9217b71 100644 --- a/web/project.inlang/settings.json +++ b/web/project.inlang/settings.json @@ -1,9 +1,7 @@ { "$schema": "https://inlang.com/schema/project-settings", "baseLocale": "en", - "locales": [ - "en" - ], + "locales": ["en"], "modules": [ "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js" ], @@ -19,7 +17,8 @@ "./messages/{locale}/webhooks.json", "./messages/{locale}/groups.json", "./messages/{locale}/openid.json", - "./messages/{locale}/activity.json" + "./messages/{locale}/activity.json", + "./messages/{locale}/settings.json" ] } } diff --git a/web/src/pages/AliasesPage/AliasTable.tsx b/web/src/pages/AliasesPage/AliasTable.tsx index bbfcc1af7b..ab4b97da89 100644 --- a/web/src/pages/AliasesPage/AliasTable.tsx +++ b/web/src/pages/AliasesPage/AliasTable.tsx @@ -177,6 +177,7 @@ export const AliasTable = ({ data: rowData }: Props) => { enableRowSelection: false, enableExpanding: false, enableSorting: true, + columnResizeMode: 'onChange', getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), }); diff --git a/web/src/pages/settings/SettingsActivityLogStreamingPage/ActivityLogStreamTable.tsx b/web/src/pages/settings/SettingsActivityLogStreamingPage/ActivityLogStreamTable.tsx new file mode 100644 index 0000000000..9c210c514d --- /dev/null +++ b/web/src/pages/settings/SettingsActivityLogStreamingPage/ActivityLogStreamTable.tsx @@ -0,0 +1,104 @@ +import { + createColumnHelper, + getCoreRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { useMemo } from 'react'; +import { m } from '../../../paraglide/messages'; +import type { ActivityLogStream } from '../../../shared/api/types'; +import { IconButtonMenu } from '../../../shared/defguard-ui/components/IconButtonMenu/IconButtonMenu'; +import type { MenuItemsGroup } from '../../../shared/defguard-ui/components/Menu/types'; +import { tableEditColumnSize } from '../../../shared/defguard-ui/components/table/consts'; +import { TableBody } from '../../../shared/defguard-ui/components/table/TableBody/TableBody'; +import { TableCell } from '../../../shared/defguard-ui/components/table/TableCell/TableCell'; +import { openModal } from '../../../shared/hooks/modalControls/modalsSubjects'; +import { ModalName } from '../../../shared/hooks/modalControls/modalTypes'; + +type RowData = ActivityLogStream; + +const columnHelper = createColumnHelper(); + +type Props = { + data: RowData[]; +}; + +export const ActivityLogStreamTable = ({ data: rowData }: Props) => { + const columns = useMemo( + () => [ + columnHelper.accessor('name', { + header: m.settings_activity_log_streaming_table_header_name(), + minSize: 484, + enableSorting: true, + sortingFn: 'text', + cell: (info) => ( + + {info.getValue()} + + ), + }), + columnHelper.accessor('stream_type', { + header: m.settings_activity_log_streaming_table_stream_type_name(), + size: 220, + minSize: 100, + cell: (info) => ( + + + {info.getValue() === 'vector_http' + ? m.modal_vector_destination_title() + : m.modal_logstash_destination_title()} + + + ), + }), + columnHelper.display({ + id: 'edit', + enableResizing: false, + header: '', + enableSorting: false, + size: tableEditColumnSize, + cell: (info) => { + const row = info.row.original; + const menuItems: MenuItemsGroup[] = [ + { + items: [ + { + text: m.controls_edit(), + icon: 'edit', + onClick: () => { + openModal(ModalName.EditLogStreaming, row); + }, + }, + { + text: m.controls_delete(), + icon: 'delete', + variant: 'danger', + onClick: () => { + openModal(ModalName.DeleteLogStreaming, row); + }, + }, + ], + }, + ]; + return ( + + + + ); + }, + }), + ], + [], + ); + + const table = useReactTable({ + columns, + data: rowData, + enableRowSelection: false, + columnResizeMode: 'onChange', + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + }); + + return ; +}; diff --git a/web/src/pages/settings/SettingsActivityLogStreamingPage/SettingsActivityStreamingTab.tsx b/web/src/pages/settings/SettingsActivityLogStreamingPage/SettingsActivityStreamingTab.tsx new file mode 100644 index 0000000000..76d38a0147 --- /dev/null +++ b/web/src/pages/settings/SettingsActivityLogStreamingPage/SettingsActivityStreamingTab.tsx @@ -0,0 +1,66 @@ +import { useQuery } from '@tanstack/react-query'; +import { useMemo } from 'react'; +import { SettingsHeader } from '../../../shared/components/SettingsHeader/SettingsHeader'; +import { SettingsLayout } from '../../../shared/components/SettingsLayout/SettingsLayout'; +import { EmptyState } from '../../../shared/defguard-ui/components/EmptyState/EmptyState'; +import { businessPlanBadgeProps } from '../shared/consts'; +import './style.scss'; +import { m } from '../../../paraglide/messages'; +import { Button } from '../../../shared/defguard-ui/components/Button/Button'; +import type { ButtonProps } from '../../../shared/defguard-ui/components/Button/types'; +import { TableTop } from '../../../shared/defguard-ui/components/table/TableTop/TableTop'; +import { openModal } from '../../../shared/hooks/modalControls/modalsSubjects'; +import { ModalName } from '../../../shared/hooks/modalControls/modalTypes'; +import { getActivityLogStreamsQueryOptions } from '../../../shared/query'; +import { ActivityLogStreamTable } from './ActivityLogStreamTable'; +import { AddLogStreamingModal } from './modals/AddDestinationModal/AddLogStreamingModal'; +import { DeleteLogStreamingModal } from './modals/DeleteDestinationModal/DeleteLogStreamingModal'; +import { EditLogStreamingModal } from './modals/EditDestinationModal/EditLogStreamingModal'; + +export const SettingsActivityLogStreamingPage = () => { + const { data: streams } = useQuery(getActivityLogStreamsQueryOptions); + + const isEmpty = !streams || streams.length === 0; + + const addButtonProps = useMemo( + (): ButtonProps => ({ + text: m.settings_activity_log_streaming_add_log_streaming_button(), + iconLeft: 'file-add', + testId: 'add-activity-stream', + onClick: () => { + openModal(ModalName.AddLogStreaming); + }, + }), + [], + ); + + return ( + + + {isEmpty ? ( + + ) : ( + <> + +