mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 01:39:34 +08:00
change server verbiage to system
This commit is contained in:
@@ -19,7 +19,7 @@ import { useStore } from '@nanostores/react'
|
||||
import { copyToClipboard } from '@/lib/utils'
|
||||
import { SystemStats } from '@/types'
|
||||
|
||||
export function AddServerButton() {
|
||||
export function AddSystemButton() {
|
||||
const [open, setOpen] = useState(false)
|
||||
const port = useRef() as MutableRefObject<HTMLInputElement>
|
||||
const publicKey = useStore($publicKey)
|
@@ -1,7 +1,4 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
Database,
|
||||
DatabaseBackupIcon,
|
||||
Github,
|
||||
LayoutDashboard,
|
||||
@@ -30,7 +27,7 @@ import { navigate } from './router'
|
||||
|
||||
export default function CommandPalette() {
|
||||
const [open, setOpen] = useState(false)
|
||||
const servers = useStore($systems)
|
||||
const systems = useStore($systems)
|
||||
|
||||
useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
@@ -72,22 +69,26 @@ export default function CommandPalette() {
|
||||
<CommandShortcut>GitHub</CommandShortcut>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Servers">
|
||||
{servers.map((server) => (
|
||||
<CommandItem
|
||||
key={server.id}
|
||||
onSelect={() => {
|
||||
navigate(`/server/${server.name}`)
|
||||
setOpen((open) => !open)
|
||||
}}
|
||||
>
|
||||
<Server className="mr-2 h-4 w-4" />
|
||||
<span>{server.name}</span>
|
||||
<CommandShortcut>{server.host}</CommandShortcut>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
{systems.length > 0 && (
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Systems">
|
||||
{systems.map((system) => (
|
||||
<CommandItem
|
||||
key={system.id}
|
||||
onSelect={() => {
|
||||
navigate(`/system/${system.name}`)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Server className="mr-2 h-4 w-4" />
|
||||
<span>{system.name}</span>
|
||||
<CommandShortcut>{system.host}</CommandShortcut>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</>
|
||||
)}
|
||||
{isAdmin() && (
|
||||
<>
|
||||
<CommandSeparator />
|
||||
@@ -95,6 +96,7 @@ export default function CommandPalette() {
|
||||
<CommandItem
|
||||
keywords={['pocketbase']}
|
||||
onSelect={() => {
|
||||
setOpen(false)
|
||||
window.open('/_/', '_blank')
|
||||
}}
|
||||
>
|
||||
@@ -104,6 +106,7 @@ export default function CommandPalette() {
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
setOpen(false)
|
||||
window.open('/_/#/logs', '_blank')
|
||||
}}
|
||||
>
|
||||
@@ -113,6 +116,7 @@ export default function CommandPalette() {
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
onSelect={() => {
|
||||
setOpen(false)
|
||||
window.open('/_/#/settings/backups', '_blank')
|
||||
}}
|
||||
>
|
||||
@@ -123,6 +127,7 @@ export default function CommandPalette() {
|
||||
<CommandItem
|
||||
keywords={['oauth', 'oicd']}
|
||||
onSelect={() => {
|
||||
setOpen(false)
|
||||
window.open('/_/#/settings/auth-providers', '_blank')
|
||||
}}
|
||||
>
|
||||
@@ -133,6 +138,7 @@ export default function CommandPalette() {
|
||||
<CommandItem
|
||||
keywords={['email']}
|
||||
onSelect={() => {
|
||||
setOpen(false)
|
||||
window.open('/_/#/settings/mail', '_blank')
|
||||
}}
|
||||
>
|
||||
|
@@ -3,7 +3,7 @@ import { createRouter } from '@nanostores/router'
|
||||
export const $router = createRouter(
|
||||
{
|
||||
home: '/',
|
||||
server: '/server/:name',
|
||||
server: '/system/:name',
|
||||
'forgot-password': '/forgot-password',
|
||||
},
|
||||
{ links: false }
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Suspense, lazy, useEffect } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'
|
||||
|
||||
const SystemsTable = lazy(() => import('../server-table/systems-table'))
|
||||
const SystemsTable = lazy(() => import('../systems-table/systems-table'))
|
||||
|
||||
export default function () {
|
||||
useEffect(() => {
|
||||
|
@@ -20,14 +20,14 @@ const DiskIoChart = lazy(() => import('../charts/disk-io-chart'))
|
||||
const BandwidthChart = lazy(() => import('../charts/bandwidth-chart'))
|
||||
|
||||
export default function ServerDetail({ name }: { name: string }) {
|
||||
const servers = useStore($systems)
|
||||
const systems = useStore($systems)
|
||||
const updatedSystem = useStore($updatedSystem)
|
||||
const chartTime = useStore($chartTime)
|
||||
const [ticks, setTicks] = useState([] as number[])
|
||||
const [server, setServer] = useState({} as SystemRecord)
|
||||
const [containers, setContainers] = useState([] as ContainerStatsRecord[])
|
||||
|
||||
const [serverStats, setServerStats] = useState([] as SystemStatsRecord[])
|
||||
const [systemStats, setSystemStats] = useState([] as SystemStatsRecord[])
|
||||
const [cpuChartData, setCpuChartData] = useState([] as { time: number; cpu: number }[])
|
||||
const [memChartData, setMemChartData] = useState(
|
||||
[] as { time: number; mem: number; memUsed: number; memCache: number }[]
|
||||
@@ -57,7 +57,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
}, [name])
|
||||
|
||||
const resetCharts = useCallback(() => {
|
||||
setServerStats([])
|
||||
setSystemStats([])
|
||||
setCpuChartData([])
|
||||
setMemChartData([])
|
||||
setDiskChartData([])
|
||||
@@ -72,11 +72,11 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
if (server.id && server.name === name) {
|
||||
return
|
||||
}
|
||||
const matchingServer = servers.find((s) => s.name === name) as SystemRecord
|
||||
const matchingServer = systems.find((s) => s.name === name) as SystemRecord
|
||||
if (matchingServer) {
|
||||
setServer(matchingServer)
|
||||
}
|
||||
}, [name, server, servers])
|
||||
}, [name, server, systems])
|
||||
|
||||
// get stats
|
||||
useEffect(() => {
|
||||
@@ -95,7 +95,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
})
|
||||
.then((records) => {
|
||||
// console.log('sctats', records)
|
||||
setServerStats(records)
|
||||
setSystemStats(records)
|
||||
})
|
||||
}, [server, chartTime])
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
|
||||
// create cpu / mem / disk data for charts
|
||||
useEffect(() => {
|
||||
if (!serverStats.length) {
|
||||
if (!systemStats.length) {
|
||||
return
|
||||
}
|
||||
const cpuData = [] as typeof cpuChartData
|
||||
@@ -115,7 +115,7 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
const diskData = [] as typeof diskChartData
|
||||
const diskIoData = [] as typeof diskIoChartData
|
||||
const networkData = [] as typeof bandwidthChartData
|
||||
for (let { created, stats } of serverStats) {
|
||||
for (let { created, stats } of systemStats) {
|
||||
const time = new Date(created).getTime()
|
||||
cpuData.push({ time, cpu: stats.cpu })
|
||||
memData.push({
|
||||
@@ -133,17 +133,17 @@ export default function ServerDetail({ name }: { name: string }) {
|
||||
setDiskChartData(diskData)
|
||||
setDiskIoChartData(diskIoData)
|
||||
setBandwidthChartData(networkData)
|
||||
}, [serverStats])
|
||||
}, [systemStats])
|
||||
|
||||
useEffect(() => {
|
||||
if (!serverStats.length) {
|
||||
if (!systemStats.length) {
|
||||
return
|
||||
}
|
||||
const now = new Date()
|
||||
const startTime = chartTimeData[chartTime].getOffset(now)
|
||||
const scale = scaleTime([startTime.getTime(), now], [0, cpuChartData.length])
|
||||
setTicks(scale.ticks().map((d) => d.getTime()))
|
||||
}, [chartTime, serverStats])
|
||||
}, [chartTime, systemStats])
|
||||
|
||||
// get container stats
|
||||
useEffect(() => {
|
@@ -59,7 +59,7 @@ import {
|
||||
import { useMemo, useState } from 'react'
|
||||
import { $systems, pb } from '@/lib/stores'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { AddServerButton } from '../add-server'
|
||||
import { AddSystemButton } from '../add-system'
|
||||
import { cn, copyToClipboard, isReadOnlyUser } from '@/lib/utils'
|
||||
import AlertsButton from '../table-alerts'
|
||||
import { navigate } from '../router'
|
||||
@@ -133,7 +133,7 @@ export default function SystemsTable() {
|
||||
</span>
|
||||
)
|
||||
},
|
||||
header: ({ column }) => sortableHeader(column, 'Server', Server),
|
||||
header: ({ column }) => sortableHeader(column, 'System', Server),
|
||||
},
|
||||
{
|
||||
accessorKey: 'info.cpu',
|
||||
@@ -259,7 +259,7 @@ export default function SystemsTable() {
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<div className={cn('ml-auto flex gap-2', isReadOnlyUser() && 'hidden')}>
|
||||
<AddServerButton />
|
||||
<AddSystemButton />
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-md border overflow-hidden">
|
||||
@@ -291,7 +291,7 @@ export default function SystemsTable() {
|
||||
onClick={(e) => {
|
||||
const target = e.target as HTMLElement
|
||||
if (!target.closest('[data-nolink]') && e.currentTarget.contains(target)) {
|
||||
navigate(`/server/${row.original.name}`)
|
||||
navigate(`/system/${row.original.name}`)
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -314,7 +314,7 @@ export default function SystemsTable() {
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No servers found
|
||||
No systems found
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
@@ -27,7 +27,7 @@ export async function copyToClipboard(content: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export const updateServerList = () => {
|
||||
export const updateSystemList = () => {
|
||||
pb.collection<SystemRecord>('systems')
|
||||
.getFullList({ sort: '+name' })
|
||||
.then((records) => {
|
||||
|
@@ -11,7 +11,7 @@ import {
|
||||
updateAlerts,
|
||||
updateFavicon,
|
||||
updateRecordList,
|
||||
updateServerList,
|
||||
updateSystemList,
|
||||
} from './lib/utils.ts'
|
||||
import { buttonVariants } from './components/ui/button.tsx'
|
||||
import {
|
||||
@@ -44,16 +44,16 @@ import {
|
||||
} from './components/ui/dropdown-menu.tsx'
|
||||
import { AlertRecord, SystemRecord } from './types'
|
||||
import { $router, Link, navigate } from './components/router.tsx'
|
||||
import ServerDetail from './components/routes/server.tsx'
|
||||
import ServerDetail from './components/routes/system.tsx'
|
||||
|
||||
// const ServerDetail = lazy(() => import('./components/routes/server.tsx'))
|
||||
// const ServerDetail = lazy(() => import('./components/routes/system.tsx'))
|
||||
const CommandPalette = lazy(() => import('./components/command-palette.tsx'))
|
||||
const LoginPage = lazy(() => import('./components/login/login.tsx'))
|
||||
|
||||
const App = () => {
|
||||
const page = useStore($router)
|
||||
const authenticated = useStore($authenticated)
|
||||
const servers = useStore($systems)
|
||||
const systems = useStore($systems)
|
||||
|
||||
useEffect(() => {
|
||||
// change auth store on auth change
|
||||
@@ -61,7 +61,7 @@ const App = () => {
|
||||
$authenticated.set(pb.authStore.isValid)
|
||||
})
|
||||
// get servers / alerts
|
||||
updateServerList()
|
||||
updateSystemList()
|
||||
updateAlerts()
|
||||
// subscribe to real time updates for systems / alerts
|
||||
pb.collection<SystemRecord>('systems').subscribe('*', (e) => {
|
||||
@@ -79,15 +79,15 @@ const App = () => {
|
||||
|
||||
// update favicon
|
||||
useEffect(() => {
|
||||
if (!authenticated || !servers.length) {
|
||||
if (!authenticated || !systems.length) {
|
||||
updateFavicon('favicon.svg')
|
||||
} else {
|
||||
let up = false
|
||||
for (const server of servers) {
|
||||
if (server.status === 'down') {
|
||||
for (const system of systems) {
|
||||
if (system.status === 'down') {
|
||||
updateFavicon('favicon-red.svg')
|
||||
return () => updateFavicon('favicon.svg')
|
||||
} else if (server.status === 'up') {
|
||||
} else if (system.status === 'up') {
|
||||
up = true
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ const App = () => {
|
||||
return () => {
|
||||
updateFavicon('favicon.svg')
|
||||
}
|
||||
}, [authenticated, servers])
|
||||
}, [authenticated, systems])
|
||||
|
||||
if (!page) {
|
||||
return <h1 className="text-3xl text-center my-14">404</h1>
|
||||
|
35
readme.md
35
readme.md
@@ -28,23 +28,39 @@ A lightweight server resource monitoring hub with historical data, docker stats,
|
||||
|
||||
Beszel has two components: the hub and the agent.
|
||||
|
||||
The hub is a web application, built on top of [PocketBase](https://pocketbase.io/), that provides a dashboard to view and manage your connected systems.
|
||||
The hub is a web application that provides a dashboard to view and manage your connected systems. It's built on top of [PocketBase](https://pocketbase.io/).
|
||||
|
||||
The agent runs on each system you want to monitor. It creates a minimal SSH server through which it communicates system metrics to the hub.
|
||||
|
||||
[](https://hub.docker.com/r/henrygd/beszel-agent)
|
||||
[](https://hub.docker.com/r/henrygd/beszel)
|
||||
|
||||
## Getting started
|
||||
|
||||
If using the binary instead of docker, ignore 4-5 and run the agent using the binary instead.
|
||||
|
||||
1. Start the hub (see [Installation](#installation)). The binary command is `beszel serve`.
|
||||
2. Open http://localhost:8090 and create an admin user.
|
||||
3. Click "Add system." Enter the name and host of the system you want to monitor.
|
||||
4. Click "Copy docker compose" to copy the agent's docker-compose.yml file to your clipboard.
|
||||
5. On the agent system, create the compose file and run `docker compose up` to start the agent.
|
||||
6. Back in the hub, click the "Add system" button in the dialog to finish adding the system.
|
||||
|
||||
If all goes well, you should see the system flip to green. If it goes red, check the Logs page, and see [troubleshooting tips](#faq--troubleshooting).
|
||||
|
||||
## Installation
|
||||
|
||||
The hub and agent are distributed as single binary files, as well as docker images.
|
||||
You may choose to install the hub and agent as single binaries, or as docker images.
|
||||
|
||||
### Docker
|
||||
|
||||
**Hub**: See the example [docker-compose.yml](/hub/docker-compose.yml) file.
|
||||
|
||||
**Agent**: The hub provides compose content when adding a system to monitor, but you can also reference the example [docker-compose.yml](/agent/docker-compose.yml) file.
|
||||
**Agent**: The hub provides compose content for the agent, but you can also reference the example [docker-compose.yml](/agent/docker-compose.yml) file.
|
||||
|
||||
The agent uses the `host` network mode, which automatically exposes the port. So change the port using an environment variable if you need to. It's set up this way so that can access stats for your host network interfaces.
|
||||
The agent uses the host network mode so it can access network interface stats. This automatically exposes the port, so change the port using an environment variable if you need to.
|
||||
|
||||
If you don't want to use the host network, you may remove that line from the compose file and manually expose the port. This will prevent the network stats from populating.
|
||||
If you don't need network stats, remove that line from the compose file and map the port manually.
|
||||
|
||||
> **Note**: The docker version of the agent cannot automatically detect the filesystem to use for disk I/O stats, so include the `FILESYSTEM` environment variable if you want that to work ([instructions here](#finding-the-correct-filesystem)).
|
||||
|
||||
@@ -78,10 +94,11 @@ Use `beszel update` and `beszel-agent update` to update to the latest version.
|
||||
|
||||
### Agent
|
||||
|
||||
| Name | Default | Description |
|
||||
| ------------ | ------- | ------------------------------------------------ |
|
||||
| `FILESYSTEM` | unset | Filesystem / partition to use for disk I/O stats |
|
||||
| `PORT` | 45876 | Port to listen on |
|
||||
| Name | Default | Description |
|
||||
| ------------ | ------- | ---------------------------------------------------------- |
|
||||
| `FILESYSTEM` | unset | Filesystem / partition to use for disk I/O stats |
|
||||
| `KEY` | unset | Public SSH key to use for authentication. Provided in hub. |
|
||||
| `PORT` | 45876 | Port to listen on |
|
||||
|
||||
## OAuth / OIDC setup
|
||||
|
||||
|
Reference in New Issue
Block a user