Merge pull request #10858 from Budibase/labday/debug-panel

Labday/debug panel
This commit is contained in:
Martin McKeaveney 2023-07-18 12:25:37 +01:00 committed by GitHub
commit 8c5177facb
9 changed files with 237 additions and 0 deletions

View File

@ -0,0 +1,84 @@
<script>
import {
Layout,
Heading,
Body,
Helpers,
Divider,
notifications,
Icon,
TextArea,
} from "@budibase/bbui"
import { auth, admin } from "stores/portal"
import { redirect } from "@roxi/routify"
import { API } from "api"
import { onMount } from "svelte"
let diagnosticInfo = ""
// Make sure page can't be visited directly in cloud
$: {
if ($admin.cloud) {
$redirect("../../portal")
}
}
async function fetchSystemDebugInfo() {
const diagnostics = await API.fetchSystemDebugInfo()
diagnosticInfo = {
browser: {
language: navigator.language || navigator.userLanguage,
userAgent: navigator.userAgent,
platform: navigator.platform,
vendor: navigator.vendor,
},
server: diagnostics,
}
}
const copyToClipboard = async () => {
await Helpers.copyToClipboard(JSON.stringify(diagnosticInfo, undefined, 2))
notifications.success("Copied")
}
onMount(async () => {
await fetchSystemDebugInfo()
})
</script>
{#if $auth.isAdmin && diagnosticInfo}
<Layout noPadding>
<Layout gap="XS">
<Heading size="M">Diagnostics</Heading>
Please include this diagnostic information in support requests and github issues
by clicking the button on the top right to copy to clipboard.
<Divider />
<Body size="M">
<section>
<div on:click={copyToClipboard} class="copy-icon">
<Icon name="Copy" size="M" />
</div>
<TextArea
height="45vh"
disabled
value={JSON.stringify(diagnosticInfo, undefined, 2)}
/>
</section>
</Body>
</Layout>
</Layout>
{/if}
<style>
section {
position: relative;
}
.copy-icon {
z-index: 1;
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
}
</style>

View File

@ -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",

View File

@ -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

View File

@ -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<void, GetDiagnosticsResponse>
) {
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 }
}

View File

@ -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

View File

@ -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

View File

@ -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`,
})
})
})
})

View File

@ -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
}

View File

@ -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"