Merge pull request #10858 from Budibase/labday/debug-panel
Labday/debug panel
This commit is contained in:
commit
8c5177facb
|
@ -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>
|
|
@ -64,6 +64,10 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
|
||||||
title: "Version",
|
title: "Version",
|
||||||
href: "/builder/portal/settings/version",
|
href: "/builder/portal/settings/version",
|
||||||
})
|
})
|
||||||
|
settingsSubPages.push({
|
||||||
|
title: "Diagnostics",
|
||||||
|
href: "/builder/portal/settings/diagnostics",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
menu.push({
|
menu.push({
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
|
|
|
@ -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.
|
* Syncs an app with the production database.
|
||||||
* @param appId the ID of the app to sync
|
* @param appId the ID of the app to sync
|
||||||
|
|
|
@ -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 }
|
||||||
|
}
|
|
@ -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
|
|
@ -25,6 +25,7 @@ import devRoutes from "./dev"
|
||||||
import migrationRoutes from "./migrations"
|
import migrationRoutes from "./migrations"
|
||||||
import pluginRoutes from "./plugin"
|
import pluginRoutes from "./plugin"
|
||||||
import opsRoutes from "./ops"
|
import opsRoutes from "./ops"
|
||||||
|
import debugRoutes from "./debug"
|
||||||
import Router from "@koa/router"
|
import Router from "@koa/router"
|
||||||
import { api as pro } from "@budibase/pro"
|
import { api as pro } from "@budibase/pro"
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ export const mainRoutes: Router[] = [
|
||||||
migrationRoutes,
|
migrationRoutes,
|
||||||
pluginRoutes,
|
pluginRoutes,
|
||||||
opsRoutes,
|
opsRoutes,
|
||||||
|
debugRoutes,
|
||||||
scheduleRoutes,
|
scheduleRoutes,
|
||||||
environmentVariableRoutes,
|
environmentVariableRoutes,
|
||||||
// these need to be handled last as they still use /api/:tableId
|
// these need to be handled last as they still use /api/:tableId
|
||||||
|
|
|
@ -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`,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -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
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ export * from "./analytics"
|
||||||
export * from "./auth"
|
export * from "./auth"
|
||||||
export * from "./user"
|
export * from "./user"
|
||||||
export * from "./errors"
|
export * from "./errors"
|
||||||
|
export * from "./debug"
|
||||||
export * from "./schedule"
|
export * from "./schedule"
|
||||||
export * from "./system"
|
export * from "./system"
|
||||||
export * from "./app"
|
export * from "./app"
|
||||||
|
|
Loading…
Reference in New Issue