diagnostics panel
This commit is contained in:
parent
746afaf573
commit
7e45773d02
|
@ -1,81 +0,0 @@
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
Layout,
|
|
||||||
Heading,
|
|
||||||
Body,
|
|
||||||
Divider,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
TextArea,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { CopyInput } from "@budibase/bbui"
|
|
||||||
import { auth, admin } from "stores/portal"
|
|
||||||
import { redirect } from "@roxi/routify"
|
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
|
||||||
import { API } from "api"
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
|
|
||||||
const BROWSER_MAP = {
|
|
||||||
edge: "Microsoft Edge",
|
|
||||||
"edg/": "Chromium Based Edge",
|
|
||||||
opr: "Opera",
|
|
||||||
chrome: "Chrome",
|
|
||||||
trident: "Internet Explorer",
|
|
||||||
firefox: "Firefox",
|
|
||||||
safari: "Safari",
|
|
||||||
}
|
|
||||||
|
|
||||||
let diagnosticInfo = ""
|
|
||||||
|
|
||||||
// Make sure page can't be visited directly in cloud
|
|
||||||
$: {
|
|
||||||
if ($admin.cloud) {
|
|
||||||
$redirect("../../portal")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchDiagnostics() {
|
|
||||||
const diagnostics = await API.fetchDiagnostics()
|
|
||||||
return {
|
|
||||||
browser: {
|
|
||||||
language: navigator.language || navigator.userLanguage,
|
|
||||||
userAgent: navigator.userAgent,
|
|
||||||
platform: navigator.platform,
|
|
||||||
vendor: navigator.vendor,
|
|
||||||
},
|
|
||||||
server: diagnostics,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
const info = await fetchDiagnostics()
|
|
||||||
diagnosticInfo = info
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if $auth.isAdmin}
|
|
||||||
<Layout noPadding>
|
|
||||||
<Layout gap="XS" noPadding>
|
|
||||||
<Heading size="M">Diagnostics</Heading>
|
|
||||||
<Body size="M">
|
|
||||||
<pre>
|
|
||||||
{JSON.stringify(diagnosticInfo, null, 2)}
|
|
||||||
</pre>
|
|
||||||
<!-- <CopyInput value={JSON.stringify(diagnosticInfo, null, 2)} /> -->
|
|
||||||
</Body>
|
|
||||||
</Layout>
|
|
||||||
<Divider />
|
|
||||||
<div>
|
|
||||||
<!-- <Button secondary on:click={refresh}>Refresh</Button> -->
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
pre {
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: pre;
|
|
||||||
word-wrap: break-word;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Layout,
|
||||||
|
Heading,
|
||||||
|
Body,
|
||||||
|
Helpers,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
notifications,
|
||||||
|
Icon,
|
||||||
|
Input,
|
||||||
|
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>
|
|
@ -58,6 +58,10 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
|
||||||
title: "Environment",
|
title: "Environment",
|
||||||
href: "/builder/portal/settings/environment",
|
href: "/builder/portal/settings/environment",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Diagnostics",
|
||||||
|
href: "/builder/portal/settings/diagnostics",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
if (!$admin.cloud) {
|
if (!$admin.cloud) {
|
||||||
settingsSubPages.push({
|
settingsSubPages.push({
|
||||||
|
@ -85,14 +89,6 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => {
|
||||||
title: "Audit Logs",
|
title: "Audit Logs",
|
||||||
href: "/builder/portal/account/auditLogs",
|
href: "/builder/portal/account/auditLogs",
|
||||||
})
|
})
|
||||||
accountSubPages.push({
|
|
||||||
title: "Diagnostics",
|
|
||||||
href: "/builder/portal/account/diagnostics",
|
|
||||||
})
|
|
||||||
accountSubPages.push({
|
|
||||||
title: "Logs",
|
|
||||||
href: "/builder/portal/account/debugLogs",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if ($admin.cloud && $auth?.user?.accountPortalAccess) {
|
if ($admin.cloud && $auth?.user?.accountPortalAccess) {
|
||||||
accountSubPages.push({
|
accountSubPages.push({
|
||||||
|
|
|
@ -124,11 +124,11 @@ export const buildAppEndpoints = API => ({
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets application diagnostics.
|
* Gets budibase platform debug information.
|
||||||
*/
|
*/
|
||||||
fetchDiagnostics: async () => {
|
fetchSystemDebugInfo: async () => {
|
||||||
return await API.get({
|
return await API.get({
|
||||||
url: `/api/dev/diagnostics`,
|
url: `/api/dev/debug`,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
import env from "../../environment"
|
import env from "../../environment"
|
||||||
|
import os from "os"
|
||||||
|
import process from "process"
|
||||||
import { checkSlashesInUrl } from "../../utilities"
|
import { checkSlashesInUrl } from "../../utilities"
|
||||||
import { request } from "../../utilities/workerRequests"
|
import { request } from "../../utilities/workerRequests"
|
||||||
import { clearLock as redisClearLock } from "../../utilities/redis"
|
import { clearLock as redisClearLock } from "../../utilities/redis"
|
||||||
|
@ -7,6 +9,7 @@ import { DocumentType } from "../../db/utils"
|
||||||
import { context, env as envCore } from "@budibase/backend-core"
|
import { context, env as envCore } from "@budibase/backend-core"
|
||||||
import { events, db as dbCore, cache } from "@budibase/backend-core"
|
import { events, db as dbCore, cache } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
|
||||||
async function redirect(ctx: any, method: string, path: string = "global") {
|
async function redirect(ctx: any, method: string, path: string = "global") {
|
||||||
const { devPath } = ctx.params
|
const { devPath } = ctx.params
|
||||||
const queryString = ctx.originalUrl.split("?")[1] || ""
|
const queryString = ctx.originalUrl.split("?")[1] || ""
|
||||||
|
@ -128,9 +131,9 @@ export async function getBudibaseVersion(ctx: any) {
|
||||||
await events.installation.versionChecked(version)
|
await events.installation.versionChecked(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function troubleshootingInfo(ctx: any) {
|
export async function systemDebugInfo(ctx: any) {
|
||||||
const os = require("os")
|
const { days, hours, minutes } = secondsToHMS(os.uptime())
|
||||||
const process = require("process")
|
const totalMemory = convertBytes(os.totalmem())
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
budibaseVersion: envCore.VERSION,
|
budibaseVersion: envCore.VERSION,
|
||||||
|
@ -140,8 +143,32 @@ export async function troubleshootingInfo(ctx: any) {
|
||||||
cpuArch: process.arch,
|
cpuArch: process.arch,
|
||||||
cpuCores: os.cpus().length,
|
cpuCores: os.cpus().length,
|
||||||
cpuInfo: os.cpus()[0].model,
|
cpuInfo: os.cpus()[0].model,
|
||||||
totalMemory: os.totalmem(),
|
totalMemory: `${totalMemory.gb}GB`,
|
||||||
freeMemory: os.freemem(),
|
uptime: `${days} day(s), ${hours} hour(s), ${minutes} minute(s)`,
|
||||||
uptime: os.uptime(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ router
|
||||||
controller.getBudibaseVersion
|
controller.getBudibaseVersion
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/dev/diagnostics",
|
"/api/dev/debug",
|
||||||
authorized(permissions.BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
controller.troubleshootingInfo
|
controller.systemDebugInfo
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/api/dev/:appId/lock",
|
"/api/dev/:appId/lock",
|
||||||
|
|
Loading…
Reference in New Issue