1
0
Fork 0
mirror of synced 2024-07-07 15:25:52 +12:00

Merge branch 'feature/audit-logs' of github.com:Budibase/budibase into feature/audit-logs

This commit is contained in:
mike12345567 2023-02-17 18:16:57 +00:00
commit 49d2dc20d1
8 changed files with 188 additions and 50 deletions

View file

@ -32,7 +32,6 @@
export let autocomplete = false
export let sort = false
export let fetchTerm = null
$: console.log(fieldText)
const dispatch = createEventDispatcher()
let searchTerm = null

View file

@ -0,0 +1,12 @@
<script>
import dayjs from "dayjs"
export let row
import relativeTime from "dayjs/plugin/relativeTime"
dayjs.extend(relativeTime)
</script>
<div>
{dayjs(row.date).fromNow()}
</div>

View file

@ -3,4 +3,4 @@
export let row
</script>
<Avatar size="M" initials={"PC"} />
<Avatar size="M" initials={row.user[0]} />

View file

@ -2,26 +2,28 @@
import {
Layout,
Table,
Select,
Search,
Multiselect,
notifications,
Icon,
clickOutside,
CoreTextArea,
DatePicker,
} from "@budibase/bbui"
import { licensing, users, apps } from "stores/portal"
import { licensing, users, apps, auditLogs } from "stores/portal"
import LockedFeature from "../../_components/LockedFeature.svelte"
import { createPaginationStore } from "helpers/pagination"
import { getContext, setContext } from "svelte"
import Portal from "svelte-portal"
import { setContext } from "svelte"
import ViewDetailsRenderer from "./_components/ViewDetailsRenderer.svelte"
import UserRenderer from "./_components/UserRenderer.svelte"
import TimeRenderer from "./_components/TimeRenderer.svelte"
const sidePanel = getContext("side-panel")
const schema = {
name: {},
date: {},
user: { width: "auto" },
app: {},
event: {},
name: { width: "1fr" },
date: { width: "1.5fr" },
user: { width: "0.5fr" },
app: { width: "1fr" },
event: { width: "1fr" },
view: { width: "auto", borderLeft: true, displayName: "" },
}
@ -34,18 +36,29 @@
column: "user",
component: UserRenderer,
},
{
column: "date",
component: TimeRenderer,
},
]
let searchTerm = ""
let pageInfo = createPaginationStore()
let prevSearch = undefined
let userSearchTerm = ""
let logSearchTerm = ""
let userPageInfo = createPaginationStore()
let logsPageInfo = createPaginationStore()
let prevUserSearch = undefined
let prevLogSearch = undefined
let selectedUsers = []
let selectedApps = []
let selectedLog
let sidePanelVisible = false
let startDate, endDate
let data = [
{
name: "User created",
date: "2021-03-01 12:00:00",
date: "2023-02-14T10:19:52.021Z",
user: "Peter Clement",
app: "School Admin Panel",
event: "User added",
@ -56,30 +69,61 @@
},
]
$: fetchUsers(page, searchTerm)
$: page = $pageInfo.page
$: fetchUsers(userPage, userSearchTerm)
$: fetchLogs(logsPage, logSearchTerm)
$: userPage = $userPageInfo.page
$: logsPage = $logsPageInfo.page
$: enrichedList = enrich($users.data || [], selectedUsers)
$: sortedList = sort(enrichedList)
const fetchUsers = async (page, search) => {
if ($pageInfo.loading) {
const fetchUsers = async (userPage, search) => {
if ($userPageInfo.loading) {
return
}
// need to remove the page if they've started searching
if (search && !prevSearch) {
pageInfo.reset()
page = undefined
if (search && !prevUserSearch) {
userPageInfo.reset()
userPage = undefined
}
prevSearch = search
prevUserSearch = search
try {
pageInfo.loading()
await users.search({ page, email: search })
pageInfo.fetched($users.hasNextPage, $users.nextPage)
userPageInfo.loading()
await users.search({ userPage, email: search })
userPageInfo.fetched($users.hasNextPage, $users.nextPage)
} catch (error) {
notifications.error("Error getting user list")
}
}
const fetchLogs = async (logsPage, search) => {
if ($logsPageInfo.loading) {
return
}
// need to remove the page if they've started searching
if (search && !prevLogSearch) {
logsPageInfo.reset()
logsPage = undefined
}
prevLogSearch = search
try {
logsPageInfo.loading()
await auditLogs.search({
logsPage,
startDate,
endDate,
metadataSearch: search,
userIds: selectedUsers,
appIds: selectedApps,
})
logsPageInfo.fetched($auditLogs.hasNextPage, $auditLogs.nextPage)
} catch (error) {
console.log(error)
notifications.error("Error getting audit logs")
}
}
const enrich = (list, selected) => {
return list.map(item => {
return {
@ -106,7 +150,21 @@
const viewDetails = detail => {
selectedLog = detail
sidePanel.open()
sidePanelVisible = true
}
const downloadLogs = async () => {
try {
await auditLogs.download({
startDate,
endDate,
metadataSearch: logSearchTerm,
userIds: selectedUsers,
appIds: selectedApps,
})
} catch (error) {
notifications.error(`Error downloading logs: ` + error.message)
}
}
setContext("auditLogs", {
@ -123,14 +181,25 @@
$licensing.goToUpgradePage()
}}
>
<div class="datepicker" />
<div class="controls">
<div class="search">
<div class="select">
<Select placeholder="All" label="Activity" />
<div>
<DatePicker
range={true}
label="Date Range"
on:change={e => {
if (e.detail[0].length > 1) {
startDate = e.detail[0][0].toISOString()
endDate = e.detail[0][1].toISOString()
}
}}
/>
</div>
<div class="select">
<Multiselect
bind:fetchTerm={searchTerm}
bind:fetchTerm={userSearchTerm}
placeholder="All users"
label="Users"
autocomplete
@ -147,13 +216,18 @@
getOptionValue={app => app.appId}
getOptionLabel={app => app.name}
options={$apps}
bind:value={selectedApps}
/>
</div>
<div class="select">
<Multiselect placeholder="All events" label="Event" />
</div>
</div>
<div style="width: 200px;">
<div style="padding-bottom: var(--spacing-s)">
<Icon on:click={() => downloadLogs()} name="Download" />
</div>
<div style="max-width: 150px; ">
<Search placeholder="Search" value={""} />
</div>
</div>
@ -171,30 +245,83 @@
</LockedFeature>
{#if selectedLog}
<Portal target="#side-panel">
<div>hello</div>
</Portal>
<div
id="side-panel"
class:visible={sidePanelVisible}
use:clickOutside={() => {
sidePanelVisible = false
}}
>
<div class="side-panel-header">
Audit Logs
<Icon
icon="Close"
on:click={() => {
sidePanelVisible = false
}}
/>
</div>
<div style="padding-top: 10px; height: 95%">
<CoreTextArea
disabled={true}
minHeight={"300px"}
height={"100%"}
value={JSON.stringify(selectedLog.metadata, null, 2)}
/>
</div>
</div>
{/if}
<style>
.side-panel-header {
display: flex;
gap: var(--spacing-s);
justify-content: space-between;
align-items: center;
}
#side-panel {
position: absolute;
right: 0;
top: 0;
padding: 24px;
background: var(--background);
border-left: var(--border-light);
width: 320px;
max-width: calc(100vw - 48px - 48px);
overflow: auto;
overflow-x: hidden;
transform: translateX(100%);
transition: transform 130ms ease-in-out;
height: calc(100% - 48px);
z-index: 2;
}
#side-panel.visible {
transform: translateX(0);
}
.search :global(.spectrum-InputGroup) {
width: 100px;
}
.controls {
display: flex;
flex-direction: row;
gap: var(--spacing-l);
align-items: flex-end;
flex-wrap: wrap;
align-items: flex-end;
}
.select {
flex-basis: 130px;
width: 0;
min-width: 100px;
}
.search {
display: flex;
gap: var(--spacing-m);
align-items: flex-start;
flex: 1 1 auto;
max-width: 100%;
}
.select {
flex: 1 1 0;
max-width: 200px;
min-width: 80px;
display: flex;
gap: var(--spacing-xl);
align-items: flex-end;
}
</style>

View file

@ -55,7 +55,6 @@
notifications.error(`Error saving variable: ${err.message}`)
}
}
$: console.log($environment.variables)
</script>
<LockedFeature

View file

@ -34,4 +34,4 @@ export function createAuditLogsStore() {
}
}
export const environment = createAuditLogsStore()
export const auditLogs = createAuditLogsStore()

View file

@ -13,3 +13,4 @@ export { backups } from "./backups"
export { overview } from "./overview"
export { environment } from "./environment"
export { menu } from "./menu"
export { auditLogs } from "./auditLogs"

View file

@ -38,20 +38,20 @@ export const buildAuditLogsEndpoints = API => ({
*/
searchAuditLogs: async opts => {
return await API.post({
url: `/api/auditlogs/search`,
url: `/api/global/auditlogs/search`,
body: buildOpts(opts),
})
},
getEventDefinitions: async () => {
return await API.get({
url: `/api/auditlogs/definitions`,
url: `/api/global/auditlogs/definitions`,
})
},
downloadLogs: async opts => {
return await API.post({
url: `/api/auditlogs/definitions`,
url: `/api/global/auditlogs/definitions`,
body: buildOpts(opts),
})
},