mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 01:39:34 +08:00
site updates
This commit is contained in:
@@ -2,20 +2,15 @@ import { useEffect } from 'react'
|
|||||||
import { $servers, pb } from '@/lib/stores'
|
import { $servers, pb } from '@/lib/stores'
|
||||||
import { DataTable } from '../server-table/data-table'
|
import { DataTable } from '../server-table/data-table'
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'
|
||||||
import { useStore } from '@nanostores/react'
|
|
||||||
import { SystemRecord } from '@/types'
|
import { SystemRecord } from '@/types'
|
||||||
|
import { updateServerList } from '@/lib/utils'
|
||||||
|
|
||||||
export function Home() {
|
export function Home() {
|
||||||
const servers = useStore($servers)
|
|
||||||
// const [systems, setSystems] = useState([] as SystemRecord[])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Home'
|
document.title = 'Home'
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(updateServerList, [])
|
||||||
console.log('servers', servers)
|
|
||||||
}, [servers])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
pb.collection<SystemRecord>('systems').subscribe('*', (e) => {
|
pb.collection<SystemRecord>('systems').subscribe('*', (e) => {
|
||||||
@@ -52,9 +47,9 @@ export function Home() {
|
|||||||
<>
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className={'mb-3'}>All Servers</CardTitle>
|
<CardTitle className={'mb-1.5'}>All Servers</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Press{' '}
|
Updated in real time. Press{' '}
|
||||||
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-0.5 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
|
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-0.5 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
|
||||||
<span className="text-xs">⌘</span>K
|
<span className="text-xs">⌘</span>K
|
||||||
</kbd>{' '}
|
</kbd>{' '}
|
||||||
|
@@ -1,36 +1,77 @@
|
|||||||
import { pb } from '@/lib/stores'
|
import { $servers, pb } from '@/lib/stores'
|
||||||
import { SystemRecord } from '@/types'
|
import { ContainerStatsRecord, SystemRecord } from '@/types'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useRoute } from 'wouter'
|
import { useRoute } from 'wouter'
|
||||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '../ui/card'
|
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '../ui/card'
|
||||||
|
import { useStore } from '@nanostores/react'
|
||||||
|
|
||||||
|
function timestampToBrowserTime(timestamp: string) {
|
||||||
|
const date = new Date(timestamp)
|
||||||
|
return date.toLocaleString()
|
||||||
|
}
|
||||||
|
|
||||||
export function ServerDetail() {
|
export function ServerDetail() {
|
||||||
|
const servers = useStore($servers)
|
||||||
const [_, params] = useRoute('/server/:name')
|
const [_, params] = useRoute('/server/:name')
|
||||||
const [server, setServer] = useState({} as SystemRecord)
|
const [server, setServer] = useState({} as SystemRecord)
|
||||||
|
const [containers, setContainers] = useState([] as ContainerStatsRecord[])
|
||||||
|
// const [serverId, setServerId] = useState('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = params!.name
|
document.title = params!.name
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
pb.collection<SystemRecord>('systems')
|
if ($servers.get().length === 0) {
|
||||||
.getFirstListItem(`name="${params!.name}"`)
|
console.log('skipping')
|
||||||
.then((record) => {
|
return
|
||||||
setServer(record)
|
}
|
||||||
|
console.log('running')
|
||||||
|
const matchingServer = servers.find((s) => s.name === params!.name) as SystemRecord
|
||||||
|
|
||||||
|
setServer(matchingServer)
|
||||||
|
|
||||||
|
console.log('matchingServer', matchingServer)
|
||||||
|
// pb.collection<SystemRecord>('systems')
|
||||||
|
// .getOne(serverId)
|
||||||
|
// .then((record) => {
|
||||||
|
// setServer(record)
|
||||||
|
// })
|
||||||
|
|
||||||
|
pb.collection<ContainerStatsRecord>('container_stats')
|
||||||
|
.getList(1, 2, {
|
||||||
|
filter: `system="${matchingServer.id}"`,
|
||||||
|
fields: 'created,stats',
|
||||||
|
sort: '-created',
|
||||||
})
|
})
|
||||||
}, [])
|
.then((records) => {
|
||||||
|
console.log('records', records)
|
||||||
|
setContainers(records.items)
|
||||||
|
})
|
||||||
|
}, [servers])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className={'mb-3'}>{server.name}</CardTitle>
|
<CardTitle className={'mb-3'}>{server.name}</CardTitle>
|
||||||
<CardDescription>5.342.34.234</CardDescription>
|
<CardDescription>
|
||||||
|
{server.ip} - last updated: {timestampToBrowserTime(server.updated)}
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<pre>{JSON.stringify(server, null, 2)}</pre>
|
<pre>{JSON.stringify(server, null, 2)}</pre>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className={'mb-3'}>Containers</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<pre>{JSON.stringify(containers, null, 2)}</pre>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -239,7 +239,17 @@ export function DataTable() {
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{table.getRowModel().rows?.length ? (
|
{table.getRowModel().rows?.length ? (
|
||||||
table.getRowModel().rows.map((row) => (
|
table.getRowModel().rows.map((row) => (
|
||||||
<TableRow key={row.original.id} data-state={row.getIsSelected() && 'selected'}>
|
<TableRow
|
||||||
|
key={row.original.id}
|
||||||
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={(e) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
if (target.tagName !== 'BUTTON' && !target.hasAttribute('role')) {
|
||||||
|
navigate(`/server/${row.original.name}`)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
key={cell.id}
|
key={cell.id}
|
||||||
|
@@ -23,7 +23,7 @@ const ThemeProviderContext = createContext<ThemeProviderState>(initialState)
|
|||||||
export function ThemeProvider({
|
export function ThemeProvider({
|
||||||
children,
|
children,
|
||||||
defaultTheme = 'system',
|
defaultTheme = 'system',
|
||||||
storageKey = 'vite-ui-theme',
|
storageKey = 'ui-theme',
|
||||||
...props
|
...props
|
||||||
}: ThemeProviderProps) {
|
}: ThemeProviderProps) {
|
||||||
const [theme, setTheme] = useState<Theme>(
|
const [theme, setTheme] = useState<Theme>(
|
||||||
@@ -62,10 +62,4 @@ export function ThemeProvider({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTheme = () => {
|
export const useTheme = () => useContext(ThemeProviderContext)
|
||||||
const context = useContext(ThemeProviderContext)
|
|
||||||
|
|
||||||
if (context === undefined) throw new Error('useTheme must be used within a ThemeProvider')
|
|
||||||
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import { toast } from '@/components/ui/use-toast'
|
import { toast } from '@/components/ui/use-toast'
|
||||||
import { type ClassValue, clsx } from 'clsx'
|
import { type ClassValue, clsx } from 'clsx'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
import { $servers, pb } from './stores'
|
||||||
|
import { SystemRecord } from '@/types'
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
@@ -21,3 +23,11 @@ export async function copyToClipboard(content: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateServerList = () => {
|
||||||
|
pb.collection<SystemRecord>('systems')
|
||||||
|
.getFullList({ sort: '+name' })
|
||||||
|
.then((records) => {
|
||||||
|
$servers.set(records)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -5,15 +5,14 @@ import { Route, Switch } from 'wouter'
|
|||||||
import { Home } from './components/routes/home.tsx'
|
import { Home } from './components/routes/home.tsx'
|
||||||
import { ThemeProvider } from './components/theme-provider.tsx'
|
import { ThemeProvider } from './components/theme-provider.tsx'
|
||||||
import LoginPage from './components/login.tsx'
|
import LoginPage from './components/login.tsx'
|
||||||
import { $authenticated, $servers, pb } from './lib/stores.ts'
|
import { $authenticated } from './lib/stores.ts'
|
||||||
import { ServerDetail } from './components/routes/server.tsx'
|
import { ServerDetail } from './components/routes/server.tsx'
|
||||||
import { ModeToggle } from './components/mode-toggle.tsx'
|
import { ModeToggle } from './components/mode-toggle.tsx'
|
||||||
import { CommandPalette } from './components/command-palette.tsx'
|
import { CommandPalette } from './components/command-palette.tsx'
|
||||||
import { cn } from './lib/utils.ts'
|
import { cn, updateServerList } from './lib/utils.ts'
|
||||||
import { buttonVariants } from './components/ui/button.tsx'
|
import { buttonVariants } from './components/ui/button.tsx'
|
||||||
import { Github } from 'lucide-react'
|
import { Github } from 'lucide-react'
|
||||||
import { useStore } from '@nanostores/react'
|
import { useStore } from '@nanostores/react'
|
||||||
import { SystemRecord } from './types'
|
|
||||||
import { Toaster } from './components/ui/toaster.tsx'
|
import { Toaster } from './components/ui/toaster.tsx'
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
@@ -24,13 +23,7 @@ const App = () => {
|
|||||||
|
|
||||||
const Main = () => {
|
const Main = () => {
|
||||||
// get servers
|
// get servers
|
||||||
useEffect(() => {
|
useEffect(updateServerList, [])
|
||||||
pb.collection<SystemRecord>('systems')
|
|
||||||
.getFullList({ sort: '+name' })
|
|
||||||
.then((records) => {
|
|
||||||
$servers.set(records)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mt-7 mb-14">
|
<div className="container mt-7 mb-14">
|
||||||
|
12
site/src/types.d.ts
vendored
12
site/src/types.d.ts
vendored
@@ -17,3 +17,15 @@ export interface SystemStats {
|
|||||||
memPct: number
|
memPct: number
|
||||||
memUsed: number
|
memUsed: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ContainerStatsRecord extends RecordModel {
|
||||||
|
system: string
|
||||||
|
stats: ContainerStats
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ContainerStats {
|
||||||
|
name: string
|
||||||
|
cpu: number
|
||||||
|
mem: number
|
||||||
|
mempct: number
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user