fix(activity logs): decode base64 [BE-11418] (#172)

release/2.25
Ali 2024-11-28 08:54:32 +13:00 committed by GitHub
parent f2e7680bf3
commit 4f708309af
4 changed files with 118 additions and 40 deletions

View File

@ -5,82 +5,110 @@
library css for buttons is overriden by `.widget .widget-body button` library css for buttons is overriden by `.widget .widget-body button`
so we have to force margin: 0 so we have to force margin: 0
*/ */
.react-datetime-picker .react-calendar button { .react-daterange-picker__calendar .react-calendar button {
margin: 0 !important; margin: 0 !important;
} }
/* /*
Extending Calendar.css from react-datetime-picker Extending Calendar.css from react-daterange-picker__calendar
*/ */
.react-datetime-picker .react-calendar { .react-daterange-picker__calendar .react-calendar {
background: var(--bg-calendar-color); background: var(--bg-calendar-color);
color: var(--text-main-color); color: var(--text-main-color);
} }
/* calendar nav buttons */ /* calendar nav buttons */
.react-datetime-picker .react-calendar__navigation button:disabled { .react-daterange-picker__calendar .react-calendar__navigation button:disabled {
background-color: var(--bg-calendar-color); background: var(--bg-calendar-color);
@apply opacity-60; @apply opacity-60;
@apply brightness-95 th-dark:brightness-110; @apply brightness-95 th-dark:brightness-110;
} }
.react-datetime-picker .react-calendar__navigation button:enabled:hover, .react-daterange-picker__calendar .react-calendar__navigation button:enabled:hover,
.react-datetime-picker .react-calendar__navigation button:enabled:focus { .react-daterange-picker__calendar .react-calendar__navigation button:enabled:focus {
background-color: var(--bg-daterangepicker-color); background: var(--bg-daterangepicker-color);
} }
/* date tile */ /* date tile */
.react-datetime-picker .react-calendar__tile:disabled { .react-daterange-picker__calendar .react-calendar__tile:disabled {
background-color: var(--bg-calendar-color); background: var(--bg-calendar-color);
@apply opacity-60; @apply opacity-60;
@apply brightness-95 th-dark:brightness-110; @apply brightness-95 th-dark:brightness-110;
} }
.react-datetime-picker .react-calendar__tile:enabled:hover, .react-daterange-picker__calendar .react-calendar__tile:enabled:hover,
.react-datetime-picker .react-calendar__tile:enabled:focus { .react-daterange-picker__calendar .react-calendar__tile:enabled:focus {
background-color: var(--bg-daterangepicker-hover); background: var(--bg-daterangepicker-hover);
} }
/* today's date tile */ /* today's date tile */
.react-datetime-picker .react-calendar__tile--now { .react-daterange-picker__calendar .react-calendar__tile--now {
/* use background color to avoid white on yellow in dark/high contrast modes */
@apply th-highcontrast:text-[color:var(--bg-calendar-color)] th-dark:text-[color:var(--bg-calendar-color)]; @apply th-highcontrast:text-[color:var(--bg-calendar-color)] th-dark:text-[color:var(--bg-calendar-color)];
border-radius: 0.25rem !important;
} }
.react-datetime-picker .react-calendar__tile--now:enabled:hover, .react-daterange-picker__calendar .react-calendar__tile--now:enabled:hover,
.react-datetime-picker .react-calendar__tile--now:enabled:focus { .react-daterange-picker__calendar .react-calendar__tile--now:enabled:focus {
background: var(--bg-daterangepicker-hover); background: var(--bg-daterangepicker-hover);
color: var(--text-daterangepicker-hover); color: var(--text-daterangepicker-hover);
} }
/* probably date tile in range */ /* probably date tile in range */
.react-datetime-picker .react-calendar__tile--hasActive { .react-daterange-picker__calendar .react-calendar__tile--hasActive {
background: var(--bg-daterangepicker-end-date); background: var(--bg-daterangepicker-end-date);
color: var(--text-daterangepicker-end-date); color: var(--text-daterangepicker-end-date);
} }
.react-datetime-picker .react-calendar__tile--hasActive:enabled:hover, .react-daterange-picker__calendar .react-calendar__tile--hasActive:enabled:hover,
.react-datetime-picker .react-calendar__tile--hasActive:enabled:focus { .react-daterange-picker__calendar .react-calendar__tile--hasActive:enabled:focus {
background: var(--bg-daterangepicker-hover); background: var(--bg-daterangepicker-hover);
color: var(--text-daterangepicker-hover); color: var(--text-daterangepicker-hover);
} }
/* selected date tile */ .react-daterange-picker__calendar .react-calendar__tile--active:enabled:hover,
.react-datetime-picker .react-calendar__tile--active { .react-daterange-picker__calendar .react-calendar__tile--active:enabled:focus {
background: var(--bg-daterangepicker-active);
color: var(--text-daterangepicker-active);
}
.react-datetime-picker .react-calendar__tile--active:enabled:hover,
.react-datetime-picker .react-calendar__tile--active:enabled:focus {
background: var(--bg-daterangepicker-hover); background: var(--bg-daterangepicker-hover);
color: var(--text-daterangepicker-hover); color: var(--text-daterangepicker-hover);
} }
.react-daterange-picker__calendar
.react-calendar__month-view__days__day:hover:not(.react-daterange-picker__calendar .react-calendar__tile--hoverEnd):not(
.react-daterange-picker__calendar .react-calendar__tile--hoverStart
):not(.react-calendar__tile--active) {
border-radius: 0.25rem !important;
}
/* on range select hover */ /* on range select hover */
.react-datetime-picker .react-calendar--selectRange .react-calendar__tile--hover { .react-daterange-picker__calendar .react-calendar--selectRange .react-calendar__tile--hover {
background-color: var(--bg-daterangepicker-in-range); background: var(--bg-daterangepicker-in-range);
color: var(--text-daterangepicker-in-range); color: var(--text-daterangepicker-in-range);
} }
/* /*
Extending DateTimePicker.css from react-datetime-picker Extending DateTimePicker.css from react-daterange-picker__calendar
*/ */
.react-datetime-picker .react-datetime-picker--disabled { .react-daterange-picker__calendar .react-daterange-picker__calendar--disabled {
@apply opacity-40; @apply opacity-40;
} }
/* selected date tile */
.react-daterange-picker__calendar .react-calendar__tile--active {
background: var(--bg-daterangepicker-active) !important;
color: var(--text-daterangepicker-active) !important;
}
.react-daterange-picker__calendar .react-calendar__tile--rangeStart:not(.react-calendar__tile--rangeEnd),
.react-daterange-picker__calendar .react-calendar__tile--hoverStart {
border-top-left-radius: 0.25rem;
border-bottom-left-radius: 0.25rem;
}
.react-daterange-picker__calendar .react-calendar__tile--rangeEnd:not(.react-calendar__tile--rangeStart),
.react-daterange-picker__calendar .react-calendar__tile--hoverEnd {
border-top-right-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
}
.react-daterange-picker__calendar .react-calendar__month-view__days__day--weekend {
color: inherit;
}
.react-calendar__tile--active.react-calendar__month-view__days__day--weekend {
color: var(--text-daterangepicker-active);
}

View File

@ -1,8 +1,19 @@
export interface ActivityLog { interface BaseActivityLog {
timestamp: number; timestamp: number;
action: string; action: string;
context: string; context: string;
id: number; id: number;
payload: object;
username: string; username: string;
} }
export interface ActivityLogResponse extends BaseActivityLog {
payload: string;
}
export interface ActivityLog extends BaseActivityLog {
payload: string | object;
}
export interface ActivityLogsResponse {
logs: Array<ActivityLogResponse>;
totalCount: number;
}

View File

@ -4,7 +4,7 @@ import axios, { parseAxiosError } from '@/portainer/services/axios';
import { isBE } from '../../feature-flags/feature-flags.service'; import { isBE } from '../../feature-flags/feature-flags.service';
import { ActivityLog } from './types'; import { ActivityLogResponse, ActivityLogsResponse } from './types';
export const sortKeys = ['Context', 'Action', 'Timestamp', 'Username'] as const; export const sortKeys = ['Context', 'Action', 'Timestamp', 'Username'] as const;
export type SortKey = (typeof sortKeys)[number]; export type SortKey = (typeof sortKeys)[number];
@ -30,19 +30,18 @@ export function useActivityLogs(query: Query) {
queryKey: ['activityLogs', query] as const, queryKey: ['activityLogs', query] as const,
queryFn: () => fetchActivityLogs(query), queryFn: () => fetchActivityLogs(query),
keepPreviousData: true, keepPreviousData: true,
select: (data) => ({
...data,
logs: decorateLogs(data.logs),
}),
}); });
} }
interface ActivityLogsResponse {
logs: Array<ActivityLog>;
totalCount: number;
}
async function fetchActivityLogs(query: Query): Promise<ActivityLogsResponse> { async function fetchActivityLogs(query: Query): Promise<ActivityLogsResponse> {
try { try {
if (!isBE) { if (!isBE) {
return { return {
logs: [{}, {}, {}, {}, {}] as Array<ActivityLog>, logs: [{}, {}, {}, {}, {}] as Array<ActivityLogResponse>,
totalCount: 5, totalCount: 5,
}; };
} }
@ -56,3 +55,40 @@ async function fetchActivityLogs(query: Query): Promise<ActivityLogsResponse> {
throw parseAxiosError(err, 'Failed loading user activity logs csv'); throw parseAxiosError(err, 'Failed loading user activity logs csv');
} }
} }
/**
* Decorates logs with the payload parsed from base64
*/
function decorateLogs(logs?: ActivityLogResponse[]) {
if (!logs || logs.length === 0) {
return [];
}
return logs.map((log) => ({
...log,
payload: parseBase64AsObject(log.payload),
}));
}
function parseBase64AsObject(value: string): string | object {
if (!value) {
return value;
}
try {
return JSON.parse(safeAtob(value));
} catch (err) {
return safeAtob(value);
}
}
function safeAtob(value: string) {
if (!value) {
return value;
}
try {
return window.atob(value);
} catch (err) {
// If the payload is not base64 encoded, return the original value
return value;
}
}

View File

@ -14,6 +14,9 @@ export default defineConfig({
}, },
bail: 2, bail: 2,
include: ['./app/**/*.test.ts', './app/**/*.test.tsx'], include: ['./app/**/*.test.ts', './app/**/*.test.tsx'],
env: {
PORTAINER_EDITION: 'CE',
},
}, },
plugins: [svgr({ include: /\?c$/ }), tsconfigPaths()], plugins: [svgr({ include: /\?c$/ }), tsconfigPaths()],
}); });