diff --git a/packages/builder/src/pages/builder/portal/settings/diagnostics.svelte b/packages/builder/src/pages/builder/portal/settings/diagnostics.svelte
new file mode 100644
index 0000000000..bba0b4ea29
--- /dev/null
+++ b/packages/builder/src/pages/builder/portal/settings/diagnostics.svelte
@@ -0,0 +1,84 @@
+
+
+{#if $auth.isAdmin && diagnosticInfo}
+
+
+ Diagnostics
+ Please include this diagnostic information in support requests and github issues
+ by clicking the button on the top right to copy to clipboard.
+
+
+
+
+
+
+{/if}
+
+
diff --git a/packages/builder/src/stores/portal/menu.js b/packages/builder/src/stores/portal/menu.js
index 04151f66d4..c66c98d00f 100644
--- a/packages/builder/src/stores/portal/menu.js
+++ b/packages/builder/src/stores/portal/menu.js
@@ -64,6 +64,10 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
title: "Version",
href: "/builder/portal/settings/version",
})
+ settingsSubPages.push({
+ title: "Diagnostics",
+ href: "/builder/portal/settings/diagnostics",
+ })
}
menu.push({
title: "Settings",
diff --git a/packages/frontend-core/src/api/app.js b/packages/frontend-core/src/api/app.js
index ce18bcc0c5..982066f05a 100644
--- a/packages/frontend-core/src/api/app.js
+++ b/packages/frontend-core/src/api/app.js
@@ -123,6 +123,15 @@ export const buildAppEndpoints = API => ({
})
},
+ /**
+ * Gets budibase platform debug information.
+ */
+ fetchSystemDebugInfo: async () => {
+ return await API.get({
+ url: `/api/debug/diagnostics`,
+ })
+ },
+
/**
* Syncs an app with the production database.
* @param appId the ID of the app to sync
diff --git a/packages/server/src/api/controllers/debug.ts b/packages/server/src/api/controllers/debug.ts
new file mode 100644
index 0000000000..6ed52973cd
--- /dev/null
+++ b/packages/server/src/api/controllers/debug.ts
@@ -0,0 +1,48 @@
+import os from "os"
+import process from "process"
+import { env } from "@budibase/backend-core"
+import { GetDiagnosticsResponse, UserCtx } from "@budibase/types"
+
+export async function systemDebugInfo(
+ ctx: UserCtx
+) {
+ const { days, hours, minutes } = secondsToHMS(os.uptime())
+ const totalMemory = convertBytes(os.totalmem())
+
+ ctx.body = {
+ budibaseVersion: env.VERSION,
+ hosting: env.DEPLOYMENT_ENVIRONMENT,
+ nodeVersion: process.version,
+ platform: process.platform,
+ cpuArch: process.arch,
+ cpuCores: os.cpus().length,
+ cpuInfo: os.cpus()[0].model,
+ totalMemory: `${totalMemory.gb}GB`,
+ uptime: `${days} day(s), ${hours} hour(s), ${minutes} minute(s)`,
+ }
+}
+
+function secondsToHMS(seconds: number) {
+ const MINUTE_IN_SECONDS = 60
+ const HOUR_IN_SECONDS = 3600
+ const DAY_IN_SECONDS = HOUR_IN_SECONDS * 24
+
+ const minutes = Math.floor((seconds / MINUTE_IN_SECONDS) % 60)
+ const hours = Math.floor((seconds / HOUR_IN_SECONDS) % 24)
+ const days = Math.floor(seconds / DAY_IN_SECONDS)
+
+ return {
+ days,
+ hours,
+ minutes,
+ seconds,
+ }
+}
+
+function convertBytes(bytes: number) {
+ const kb = bytes / 1024
+ const mb = kb / 1024
+ const gb = mb / 1024
+
+ return { gb, mb, kb }
+}
diff --git a/packages/server/src/api/routes/debug.ts b/packages/server/src/api/routes/debug.ts
new file mode 100644
index 0000000000..5a7b9f242e
--- /dev/null
+++ b/packages/server/src/api/routes/debug.ts
@@ -0,0 +1,14 @@
+import Router from "@koa/router"
+import * as controller from "../controllers/debug"
+import authorized from "../../middleware/authorized"
+import { permissions } from "@budibase/backend-core"
+
+const router: Router = new Router()
+
+router.get(
+ "/api/debug/diagnostics",
+ authorized(permissions.BUILDER),
+ controller.systemDebugInfo
+)
+
+export default router
diff --git a/packages/server/src/api/routes/index.ts b/packages/server/src/api/routes/index.ts
index f5374465d0..22d0a20c9b 100644
--- a/packages/server/src/api/routes/index.ts
+++ b/packages/server/src/api/routes/index.ts
@@ -25,6 +25,7 @@ import devRoutes from "./dev"
import migrationRoutes from "./migrations"
import pluginRoutes from "./plugin"
import opsRoutes from "./ops"
+import debugRoutes from "./debug"
import Router from "@koa/router"
import { api as pro } from "@budibase/pro"
@@ -63,6 +64,7 @@ export const mainRoutes: Router[] = [
migrationRoutes,
pluginRoutes,
opsRoutes,
+ debugRoutes,
scheduleRoutes,
environmentVariableRoutes,
// these need to be handled last as they still use /api/:tableId
diff --git a/packages/server/src/api/routes/tests/debug.spec.ts b/packages/server/src/api/routes/tests/debug.spec.ts
new file mode 100644
index 0000000000..23ee43fc73
--- /dev/null
+++ b/packages/server/src/api/routes/tests/debug.spec.ts
@@ -0,0 +1,64 @@
+const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
+const setup = require("./utilities")
+import os from "os"
+
+jest.mock("process", () => ({
+ arch: "arm64",
+ version: "v14.20.1",
+ platform: "darwin",
+}))
+
+describe("/component", () => {
+ let request = setup.getRequest()
+ let config = setup.getConfig()
+
+ afterAll(setup.afterAll)
+
+ beforeAll(async () => {
+ await config.init()
+ os.cpus = () => [
+ {
+ model: "test",
+ speed: 12323,
+ times: {
+ user: 0,
+ nice: 0,
+ sys: 0,
+ idle: 0,
+ irq: 0,
+ },
+ },
+ ]
+ os.uptime = () => 123123123123
+ os.totalmem = () => 10000000000
+ })
+
+ describe("/api/debug", () => {
+ it("should return debug information to the frontend", async () => {
+ const res = await request
+ .get(`/api/debug/diagnostics`)
+ .set(config.defaultHeaders())
+ .expect("Content-Type", /json/)
+ .expect(200)
+ expect(res.body).toEqual({
+ budibaseVersion: "0.0.0",
+ cpuArch: "arm64",
+ cpuCores: 1,
+ cpuInfo: "test",
+ hosting: "docker-compose",
+ nodeVersion: "v14.20.1",
+ platform: "darwin",
+ totalMemory: "9.313225746154785GB",
+ uptime: "1425036 day(s), 3 hour(s), 32 minute(s)",
+ })
+ })
+
+ it("should apply authorization to endpoint", async () => {
+ await checkBuilderEndpoint({
+ config,
+ method: "GET",
+ url: `/api/debug/diagnostics`,
+ })
+ })
+ })
+})
diff --git a/packages/types/src/api/web/debug.ts b/packages/types/src/api/web/debug.ts
new file mode 100644
index 0000000000..ea6ee8829f
--- /dev/null
+++ b/packages/types/src/api/web/debug.ts
@@ -0,0 +1,11 @@
+export interface GetDiagnosticsResponse {
+ budibaseVersion: string
+ hosting: string
+ nodeVersion: string
+ platform: string
+ cpuArch: string
+ cpuCores: number
+ cpuInfo: string
+ totalMemory: string
+ uptime: string
+}
diff --git a/packages/types/src/api/web/index.ts b/packages/types/src/api/web/index.ts
index 6d3b4124f5..0e0527eb7f 100644
--- a/packages/types/src/api/web/index.ts
+++ b/packages/types/src/api/web/index.ts
@@ -2,6 +2,7 @@ export * from "./analytics"
export * from "./auth"
export * from "./user"
export * from "./errors"
+export * from "./debug"
export * from "./schedule"
export * from "./system"
export * from "./app"