Skip to content

Commit

Permalink
fix(activity logs): decode base64 [BE-11418] (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
testA113 authored Nov 27, 2024
1 parent f2e7680 commit 4f70830
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 40 deletions.
90 changes: 59 additions & 31 deletions app/assets/css/react-datetime-picker-override.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,82 +5,110 @@
library css for buttons is overriden by `.widget .widget-body button`
so we have to force margin: 0
*/
.react-datetime-picker .react-calendar button {
.react-daterange-picker__calendar .react-calendar button {
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);
color: var(--text-main-color);
}

/* calendar nav buttons */
.react-datetime-picker .react-calendar__navigation button:disabled {
background-color: var(--bg-calendar-color);
.react-daterange-picker__calendar .react-calendar__navigation button:disabled {
background: var(--bg-calendar-color);
@apply opacity-60;
@apply brightness-95 th-dark:brightness-110;
}
.react-datetime-picker .react-calendar__navigation button:enabled:hover,
.react-datetime-picker .react-calendar__navigation button:enabled:focus {
background-color: var(--bg-daterangepicker-color);
.react-daterange-picker__calendar .react-calendar__navigation button:enabled:hover,
.react-daterange-picker__calendar .react-calendar__navigation button:enabled:focus {
background: var(--bg-daterangepicker-color);
}

/* date tile */
.react-datetime-picker .react-calendar__tile:disabled {
background-color: var(--bg-calendar-color);
.react-daterange-picker__calendar .react-calendar__tile:disabled {
background: var(--bg-calendar-color);
@apply opacity-60;
@apply brightness-95 th-dark:brightness-110;
}
.react-datetime-picker .react-calendar__tile:enabled:hover,
.react-datetime-picker .react-calendar__tile:enabled:focus {
background-color: var(--bg-daterangepicker-hover);
.react-daterange-picker__calendar .react-calendar__tile:enabled:hover,
.react-daterange-picker__calendar .react-calendar__tile:enabled:focus {
background: var(--bg-daterangepicker-hover);
}

/* today's date tile */
.react-datetime-picker .react-calendar__tile--now {
/* use background color to avoid white on yellow in dark/high contrast modes */
.react-daterange-picker__calendar .react-calendar__tile--now {
@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-datetime-picker .react-calendar__tile--now:enabled:focus {
.react-daterange-picker__calendar .react-calendar__tile--now:enabled:hover,
.react-daterange-picker__calendar .react-calendar__tile--now:enabled:focus {
background: var(--bg-daterangepicker-hover);
color: var(--text-daterangepicker-hover);
}

/* 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);
color: var(--text-daterangepicker-end-date);
}
.react-datetime-picker .react-calendar__tile--hasActive:enabled:hover,
.react-datetime-picker .react-calendar__tile--hasActive:enabled:focus {
.react-daterange-picker__calendar .react-calendar__tile--hasActive:enabled:hover,
.react-daterange-picker__calendar .react-calendar__tile--hasActive:enabled:focus {
background: var(--bg-daterangepicker-hover);
color: var(--text-daterangepicker-hover);
}

/* selected date tile */
.react-datetime-picker .react-calendar__tile--active {
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 {
.react-daterange-picker__calendar .react-calendar__tile--active:enabled:hover,
.react-daterange-picker__calendar .react-calendar__tile--active:enabled:focus {
background: var(--bg-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 */
.react-datetime-picker .react-calendar--selectRange .react-calendar__tile--hover {
background-color: var(--bg-daterangepicker-in-range);
.react-daterange-picker__calendar .react-calendar--selectRange .react-calendar__tile--hover {
background: var(--bg-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;
}

/* 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);
}
15 changes: 13 additions & 2 deletions app/react/portainer/logs/ActivityLogsView/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
export interface ActivityLog {
interface BaseActivityLog {
timestamp: number;
action: string;
context: string;
id: number;
payload: object;
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;
}
50 changes: 43 additions & 7 deletions app/react/portainer/logs/ActivityLogsView/useActivityLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import axios, { parseAxiosError } from '@/portainer/services/axios';

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 type SortKey = (typeof sortKeys)[number];
Expand All @@ -30,19 +30,18 @@ export function useActivityLogs(query: Query) {
queryKey: ['activityLogs', query] as const,
queryFn: () => fetchActivityLogs(query),
keepPreviousData: true,
select: (data) => ({
...data,
logs: decorateLogs(data.logs),
}),
});
}

interface ActivityLogsResponse {
logs: Array<ActivityLog>;
totalCount: number;
}

async function fetchActivityLogs(query: Query): Promise<ActivityLogsResponse> {
try {
if (!isBE) {
return {
logs: [{}, {}, {}, {}, {}] as Array<ActivityLog>,
logs: [{}, {}, {}, {}, {}] as Array<ActivityLogResponse>,
totalCount: 5,
};
}
Expand All @@ -56,3 +55,40 @@ async function fetchActivityLogs(query: Query): Promise<ActivityLogsResponse> {
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;
}
}
3 changes: 3 additions & 0 deletions vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export default defineConfig({
},
bail: 2,
include: ['./app/**/*.test.ts', './app/**/*.test.tsx'],
env: {
PORTAINER_EDITION: 'CE',
},
},
plugins: [svgr({ include: /\?c$/ }), tsconfigPaths()],
});

0 comments on commit 4f70830

Please sign in to comment.