diff --git a/packages/backend-core/src/middleware/downloadBody.ts b/packages/backend-core/src/middleware/downloadBody.ts new file mode 100644 index 0000000000..a7050e08c5 --- /dev/null +++ b/packages/backend-core/src/middleware/downloadBody.ts @@ -0,0 +1,22 @@ +import { Ctx } from "@budibase/types" + +/** + * Expects a standard "query" query string property which is the JSON body + * of the request, which has to be sent via query string due to the requirement + * of making an endpoint a GET request e.g. downloading a file stream. + */ +export default function (ctx: Ctx, next: any) { + const queryString = ctx.request.query?.query as string | undefined + if (!queryString) { + return next() + } + const decoded = decodeURIComponent(queryString) + let json + try { + json = JSON.parse(decoded) + } catch (err) { + return next() + } + ctx.request.body = json + return next() +} diff --git a/packages/backend-core/src/middleware/index.ts b/packages/backend-core/src/middleware/index.ts index de609f9a3e..0c4c2f6b94 100644 --- a/packages/backend-core/src/middleware/index.ts +++ b/packages/backend-core/src/middleware/index.ts @@ -17,4 +17,5 @@ export { default as builderOrAdmin } from "./builderOrAdmin" export { default as builderOnly } from "./builderOnly" export { default as logging } from "./logging" export { default as errorHandling } from "./errorHandling" +export { default as downloadBody } from "./downloadBody" export * as joiValidator from "./joi-validator" diff --git a/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte b/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte index 125a67a03d..944ed87d1a 100644 --- a/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte +++ b/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte @@ -21,13 +21,13 @@ import UserRenderer from "./_components/UserRenderer.svelte" import TimeRenderer from "./_components/TimeRenderer.svelte" import AppColumnRenderer from "./_components/AppColumnRenderer.svelte" - import download from "downloadjs" + import { cloneDeep } from "lodash" const schema = { date: { width: "0.8fr" }, user: { width: "0.5fr" }, - app: { width: "1fr", fieldName: "name" }, - name: { width: "1fr" }, + app: { width: "0.75fr", fieldName: "name" }, + name: { width: "1.5fr" }, view: { width: "auto", borderLeft: true, displayName: "" }, } @@ -134,8 +134,7 @@ $auditLogs.logs.bookmark ) } catch (error) { - console.log(error) - notifications.error("Error getting audit logs") + notifications.error(`Error getting audit logs - ${error}`) } } @@ -164,24 +163,13 @@ } const viewDetails = detail => { - console.log(detail) selectedLog = detail sidePanelVisible = true } - async function exportView() { - try { - const data = await API.exportView({ - viewName: view, - format: exportFormat, - }) - } catch (error) { - notifications.error(`Unable to export ${exportFormat.toUpperCase()} data`) - } - } const downloadLogs = async () => { try { - let response = await auditLogs.downloadLogs({ + window.location = auditLogs.getDownloadUrl({ startDate, endDate, fullSearch: logSearchTerm, @@ -189,8 +177,6 @@ appIds: selectedApps, events: selectedEvents, }) - - // DO SOMETHING HERE??????????? } catch (error) { notifications.error(`Error downloading logs: ` + error.message) } @@ -205,6 +191,20 @@ notifications.success("Copied") } + function cleanupMetadata(log) { + const cloned = cloneDeep(log) + cloned.userId = cloned.user._id + if (cloned.app) { + cloned.appId = cloned.app.appId + } + // remove props that are confused/not returned in download + delete cloned._id + delete cloned._rev + delete cloned.app + delete cloned.user + return cloned + } + onMount(async () => { await auditLogs.getEventDefinitions() await licensing.init() @@ -277,15 +277,15 @@ /> +
+ +
+
downloadLogs()} style="padding-bottom: var(--spacing-s)" > - -
- -
- +
@@ -352,7 +352,7 @@ disabled minHeight={"300px"} height={"100%"} - value={JSON.stringify(selectedLog.metadata, null, 2)} + value={JSON.stringify(cleanupMetadata(selectedLog), null, 2)} /> diff --git a/packages/builder/src/stores/portal/auditLogs.js b/packages/builder/src/stores/portal/auditLogs.js index 165e4b5ec5..9abf8ec11b 100644 --- a/packages/builder/src/stores/portal/auditLogs.js +++ b/packages/builder/src/stores/portal/auditLogs.js @@ -28,15 +28,15 @@ export function createAuditLogsStore() { }) } - async function downloadLogs(opts = {}) { - return await API.downloadLogs(opts) + function getDownloadUrl(opts = {}) { + return API.getDownloadUrl(opts) } return { subscribe, search, getEventDefinitions, - downloadLogs, + getDownloadUrl, } } diff --git a/packages/frontend-core/src/api/auditLogs.js b/packages/frontend-core/src/api/auditLogs.js index 08e14328a1..b8ad16a722 100644 --- a/packages/frontend-core/src/api/auditLogs.js +++ b/packages/frontend-core/src/api/auditLogs.js @@ -8,7 +8,7 @@ const buildOpts = ({ events, }) => { const opts = {} - + if (bookmark) { opts.bookmark = bookmark } @@ -56,10 +56,8 @@ export const buildAuditLogsEndpoints = API => ({ }) }, - downloadLogs: async opts => { - return await API.post({ - url: `/api/global/auditlogs/download`, - body: buildOpts(opts), - }) + getDownloadUrl: opts => { + const query = encodeURIComponent(JSON.stringify(opts)) + return `/api/global/auditlogs/download?query=${query}` }, }) diff --git a/packages/worker/src/api/routes/global/tests/auditLogs.spec.ts b/packages/worker/src/api/routes/global/tests/auditLogs.spec.ts new file mode 100644 index 0000000000..5e7ed31263 --- /dev/null +++ b/packages/worker/src/api/routes/global/tests/auditLogs.spec.ts @@ -0,0 +1,5 @@ +import { mocks } from "@budibase/backend-core/tests" + +mocks.licenses.useEnvironmentVariables() + +describe("/api/global/auditlogs", () => {})