mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 01:39:34 +08:00
updates
This commit is contained in:
10
main.go
10
main.go
@@ -131,7 +131,7 @@ func main() {
|
||||
}
|
||||
|
||||
func serverUpdateTicker() {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
for range ticker.C {
|
||||
updateServers()
|
||||
}
|
||||
@@ -220,9 +220,11 @@ func setInactive(record *models.Record) {
|
||||
delete(serverConnections, record.Id)
|
||||
}
|
||||
// set inactive
|
||||
record.Set("status", "down")
|
||||
if err := app.Dao().SaveRecord(record); err != nil {
|
||||
app.Logger().Error("Failed to update record: ", "err", err.Error())
|
||||
if record.Get("status") != "down" {
|
||||
record.Set("status", "down")
|
||||
if err := app.Dao().SaveRecord(record); err != nil {
|
||||
app.Logger().Error("Failed to update record: ", "err", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -44,7 +44,6 @@ export function AddServerButton() {
|
||||
}
|
||||
// get public key
|
||||
pb.send('/getkey', {}).then(({ key }) => {
|
||||
console.log('key', key)
|
||||
$publicKey.set(key)
|
||||
})
|
||||
}, [open])
|
||||
@@ -77,12 +76,12 @@ export function AddServerButton() {
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" className="flex gap-1">
|
||||
<Plus className="h-4 w-4 mr-auto" />
|
||||
Add <span className="hidden sm:inline">Server</span>
|
||||
Add <span className="hidden sm:inline">System</span>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add New Server</DialogTitle>
|
||||
<DialogTitle>Add New System</DialogTitle>
|
||||
<DialogDescription>
|
||||
The agent must be running on the server to connect. Copy the{' '}
|
||||
<code className="bg-muted px-1 rounded-sm">docker-compose.yml</code> for the agent
|
||||
@@ -153,7 +152,7 @@ export function AddServerButton() {
|
||||
>
|
||||
Copy docker compose
|
||||
</Button>
|
||||
<Button>Add server</Button>
|
||||
<Button>Add system</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
|
@@ -1,56 +1,38 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { UserAuthForm } from '@/components/user-auth-form'
|
||||
import { ChevronLeft } from 'lucide-react'
|
||||
import { Logo } from './logo'
|
||||
|
||||
export default function () {
|
||||
return (
|
||||
<div className="container relative hidden h-screen flex-col items-center justify-center md:grid lg:max-w-none lg:grid-cols-2 lg:px-0">
|
||||
<div className="lg:p-8">
|
||||
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
||||
<div className="flex flex-col space-y-2 text-center">
|
||||
{/* <img
|
||||
className="mx-auto h-10 w-10 mb-2"
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Numelon_Logo.png/240px-Numelon_Logo.png"
|
||||
alt=""
|
||||
/> */}
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Welcome back</h1>
|
||||
<div className="relative h-screen grid lg:max-w-none lg:grid-cols-2 lg:px-0">
|
||||
<div className="grid items-center">
|
||||
<div className="flex flex-col justify-center space-y-6 w-full px-4 max-w-[22em] mx-auto">
|
||||
<div className="text-center">
|
||||
<h1 className="mb-4">
|
||||
<Logo className="h-6 fill-foreground mx-auto" />
|
||||
<div className="sr-only">Qoma</div>
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enter your email to sign in to your account
|
||||
</p>
|
||||
</div>
|
||||
<UserAuthForm />
|
||||
<p className="px-8 text-center text-sm text-muted-foreground">
|
||||
<a href="/register" className="hover:text-brand underline underline-offset-4">
|
||||
Don't have an account? Sign Up
|
||||
{/* todo: add forgot password section to readme and link to section
|
||||
reset w/ command or link to pb reset */}
|
||||
<a
|
||||
href="https://github.com/henrygd/qoma"
|
||||
className="hover:text-brand underline underline-offset-4"
|
||||
>
|
||||
Forgot password?
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex">
|
||||
<div
|
||||
className="absolute inset-0 bg-background bg-cover opacity-80"
|
||||
style={{
|
||||
backgroundImage: `url(https://directus.cloud/assets/waves.2b156907.svg)`,
|
||||
}}
|
||||
></div>
|
||||
<div className="relative z-20 flex gap-2 items-center text-lg font-medium ml-auto">
|
||||
placeholder
|
||||
<img
|
||||
className={'w-6 h-6'}
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Numelon_Logo.png/240px-Numelon_Logo.png"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="relative z-20 mt-auto">
|
||||
<blockquote className="space-y-2">
|
||||
<p className="text-lg">
|
||||
“This library has saved me countless hours of work and helped me deliver stunning
|
||||
designs to my clients faster than ever before.”
|
||||
</p>
|
||||
<footer className="text-sm">Sofia Davis</footer>
|
||||
</blockquote>
|
||||
</div> */}
|
||||
<div className="relative hidden h-full bg-primary lg:block">
|
||||
<img
|
||||
className="absolute inset-0 h-full w-full object-cover bg-primary"
|
||||
src="/penguin-and-egg.avif"
|
||||
></img>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
export function Logo({ className }: { className?: string }) {
|
||||
return (
|
||||
// audiowide
|
||||
<svg viewBox="0 0 299 82" className={className}>
|
||||
<svg viewBox="0 0 300 82" className={className}>
|
||||
<path d="M202.1 36v35h-12.6V36a7.5 7.5 0 0 0-.2-1.5q-.1-.8-.5-1.4a3.9 3.9 0 0 0-.7-.9q-1-1-3-1.3a9.2 9.2 0 0 0-.9 0h-16.5V71H155V24.5a6.3 6.3 0 0 1 .4-2.2 6 6 0 0 1 .1-.3q.5-1.1 1.4-2 .9-.8 2-1.3a6.4 6.4 0 0 1 2.5-.5h23a17.2 17.2 0 0 1 3 .3 22.8 22.8 0 0 1 2.6.6q3 1 5.8 3v-3.9h17.1a17.7 17.7 0 0 1 2.6.2 21.3 21.3 0 0 1 1.2.2 19.5 19.5 0 0 1 3.9 1.3 21.5 21.5 0 0 1 .1 0q2 .9 3.8 2.3a15 15 0 0 1 3.2 3.3 16.3 16.3 0 0 1 0 0 16.3 16.3 0 0 1 1.6 3 19.7 19.7 0 0 1 .6 1.6q.8 2.6.8 5.9v35H218V36a8 8 0 0 0-.1-1.5q-.3-1.4-1.1-2.3-1.3-1.3-3.9-1.3h-11.4q.6 2.4.6 5Zm94.8-.3v17.8a17.3 17.3 0 0 1-.3 3 23.1 23.1 0 0 1-.7 2.7 17 17 0 0 1-3 5.7 15.9 15.9 0 0 1-3.6 3.3 19.6 19.6 0 0 1-1.8 1Q284.4 71 280 71a25.4 25.4 0 0 1-.6 0h-22.9a17.3 17.3 0 0 1-3-.3 23.1 23.1 0 0 1-2.7-.6 17 17 0 0 1-5.7-3 15.9 15.9 0 0 1-3.3-3.7 19.6 19.6 0 0 1-1-1.8q-1.7-3.1-1.8-7.5a25.4 25.4 0 0 1 0-.6 17.3 17.3 0 0 1 .3-3 23.4 23.4 0 0 1 .6-2.7q1-3 3-5.7a15.9 15.9 0 0 1 3.7-3.3 19.6 19.6 0 0 1 1.8-1q3.1-1.7 7.5-1.8a25.4 25.4 0 0 1 .6 0h22.9V48h-22.9a6.8 6.8 0 0 0-1.6.2 4.6 4.6 0 0 0-2.4 1.4A5.5 5.5 0 0 0 251 53a7.3 7.3 0 0 0 0 .6 6 6 0 0 0 .2 1.7 4.4 4.4 0 0 0 1.4 2.2 5.8 5.8 0 0 0 3.9 1.4h22.8a6.9 6.9 0 0 0 1.6-.2 4.6 4.6 0 0 0 2.4-1.4 5.4 5.4 0 0 0 1.4-3.2 7.2 7.2 0 0 0 0-.7V35.7a6.5 6.5 0 0 0-.2-1.7 4.8 4.8 0 0 0-1.3-2.3q-1.6-1.4-4-1.4h-27.8v-12h27.9a17.3 17.3 0 0 1 3 .2 23.1 23.1 0 0 1 2.7.6 17 17 0 0 1 5.6 3 15.9 15.9 0 0 1 3.4 3.7 19.6 19.6 0 0 1 1 1.8q1.7 3.1 1.8 7.5a25.4 25.4 0 0 1 0 .6ZM75.7 29.3v13.4a32.8 32.8 0 0 1-.8 7.1 29.5 29.5 0 0 1-.5 2 29 29 0 0 1-3.1 7 26.9 26.9 0 0 1-.6 1 27 27 0 0 1-5.7 6 27 27 0 0 1-7.4 4.2l14.8 11.8H54L41.8 72H29.3a32.1 32.1 0 0 1-8.2-1 28.7 28.7 0 0 1-3.5-1.2q-5.3-2.2-9.3-6a28.2 28.2 0 0 1-6-9.4A29.6 29.6 0 0 1 0 45.2a35.1 35.1 0 0 1-.1-2.5V29.3A31.8 31.8 0 0 1 1 21a28.5 28.5 0 0 1 1.2-3.4 28.1 28.1 0 0 1 5.2-8.3 26.7 26.7 0 0 1 1-1 28 28 0 0 1 9.1-6 31.8 31.8 0 0 1 .1-.1A29.9 29.9 0 0 1 27.4.1a35 35 0 0 1 1.9-.1h17.2a31.6 31.6 0 0 1 8.2 1 28.2 28.2 0 0 1 3.4 1.2 28.1 28.1 0 0 1 9.3 6 27.5 27.5 0 0 1 6 9 31.3 31.3 0 0 1 0 .4 30.1 30.1 0 0 1 2.2 9.6 35.4 35.4 0 0 1 0 2.1Zm68.5 6.7v17.2a22.2 22.2 0 0 1-.2 3 17 17 0 0 1-.6 2.9 18.6 18.6 0 0 1-1.2 2.8 15.3 15.3 0 0 1-1 1.7 15 15 0 0 1-3.1 3.4 14.2 14.2 0 0 1 0 0 18.5 18.5 0 0 1-3.8 2.3 19.5 19.5 0 0 1-4 1.3 20.8 20.8 0 0 1-2.4.3 17 17 0 0 1-1.5.1h-22.9q-2.6 0-5.7-1-3.2-.9-5.8-3a16.3 16.3 0 0 1-3.4-3.7 20 20 0 0 1-1-1.8 14.7 14.7 0 0 1-1.4-3.8q-.4-1.7-.4-3.6a26.1 26.1 0 0 1 0-1V36q0-4.4 1.4-7.6a13.2 13.2 0 0 1 .3-.7 18.6 18.6 0 0 1 2.4-3.5 15.6 15.6 0 0 1 2-2q2.7-2.1 5.9-3a24.2 24.2 0 0 1 2.8-.7q1.5-.3 3-.3h22.8a22.7 22.7 0 0 1 3.7.3q2.2.4 4 1.2a13.5 13.5 0 0 1 .6.3 18 18 0 0 1 3.4 2.2 15 15 0 0 1 2.1 2.2q2.1 2.7 3 5.8a23.3 23.3 0 0 1 .8 3 17.3 17.3 0 0 1 .2 2.8ZM63 42.7V29.3a20.4 20.4 0 0 0-.4-4 16.6 16.6 0 0 0-.8-2.8 15.4 15.4 0 0 0-2.5-4.2 14.3 14.3 0 0 0-.9-1 15 15 0 0 0-4.8-3.2 17.2 17.2 0 0 0-.4-.2 17.5 17.5 0 0 0-4.9-1.1 21 21 0 0 0-1.8-.1H29.3a20 20 0 0 0-4 .4 16.6 16.6 0 0 0-2.8.8q-3 1.2-5.2 3.4a14.8 14.8 0 0 0-3.3 5 17 17 0 0 0-.1.2 17.3 17.3 0 0 0-1 4.4 21.6 21.6 0 0 0-.2 2.4v13.4a20.4 20.4 0 0 0 .4 4 16.6 16.6 0 0 0 .8 2.8 15 15 0 0 0 2.7 4.5 14.3 14.3 0 0 0 .7.7 15.2 15.2 0 0 0 5 3.3 17.5 17.5 0 0 0 .2 0 17.4 17.4 0 0 0 4.7 1.2 21.3 21.3 0 0 0 2.1 0h17a20 20 0 0 0 4.2-.3A16.6 16.6 0 0 0 53 58q3.1-1.2 5.3-3.4a14.8 14.8 0 0 0 3.3-5 17 17 0 0 0 0-.2A17.3 17.3 0 0 0 63 45a21.6 21.6 0 0 0 0-2.4Zm68.5 10.5V36a8.4 8.4 0 0 0 0-1.5l-.5-1.4a3.7 3.7 0 0 0-.8-1 4.2 4.2 0 0 0-1.7-1l-1.4-.3a8.9 8.9 0 0 0-.7 0h-22.8q-1.8 0-3 .6a4 4 0 0 0-.8.7q-1.2 1.1-1.3 3.2a8.7 8.7 0 0 0 0 .6v17.2q0 1.8.7 3a4 4 0 0 0 .6.8 4.2 4.2 0 0 0 1.7 1l1.5.3a8.9 8.9 0 0 0 .6 0h22.8a8 8 0 0 0 1.5-.1l1.4-.5a3.7 3.7 0 0 0 1-.7 4.2 4.2 0 0 0 1-1.7q.2-.7.2-1.5a8.9 8.9 0 0 0 0-.6Z" />
|
||||
</svg>
|
||||
)
|
||||
|
@@ -55,6 +55,7 @@ import {
|
||||
PauseCircleIcon,
|
||||
PlayCircleIcon,
|
||||
Trash2Icon,
|
||||
BellIcon,
|
||||
} from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { $servers, pb, navigate } from '@/lib/stores'
|
||||
@@ -65,7 +66,7 @@ import { cn, copyToClipboard } from '@/lib/utils'
|
||||
function CellFormatter(info: CellContext<SystemRecord, unknown>) {
|
||||
const val = info.getValue() as number
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex gap-2 items-center tabular-nums tracking-tight">
|
||||
<span className="grow min-w-10 block bg-muted h-4 relative rounded-sm overflow-hidden">
|
||||
<span
|
||||
className={cn(
|
||||
@@ -103,12 +104,14 @@ export default function () {
|
||||
const columns: ColumnDef<SystemRecord>[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
// size: 70,
|
||||
// size: 200,
|
||||
size: 200,
|
||||
minSize: 0,
|
||||
accessorKey: 'name',
|
||||
cell: (info) => {
|
||||
const { status } = info.row.original
|
||||
return (
|
||||
<span className="flex gap-0.5 items-center text-base">
|
||||
<span className="flex gap-0.5 items-center text-base md:pr-5">
|
||||
<span
|
||||
className={cn('w-2 h-2 left-0 rounded-full', {
|
||||
'bg-green-500': status === 'up',
|
||||
@@ -147,18 +150,25 @@ export default function () {
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
size: 32,
|
||||
maxSize: 32,
|
||||
size: 120,
|
||||
// minSize: 0,
|
||||
cell: ({ row }) => {
|
||||
const { id, name, status, host } = row.original
|
||||
return (
|
||||
<div className={'flex justify-end'}>
|
||||
<div className={'flex justify-end items-center gap-1'}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size={'icon'}
|
||||
onClick={() => alert('notifications coming soon')}
|
||||
>
|
||||
<BellIcon className="h-[1.2em] w-[1.2em] pointer-events-none" />
|
||||
</Button>
|
||||
<AlertDialog>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<Button variant="ghost" size={'icon'}>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<MoreHorizontal className="w-5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
@@ -241,6 +251,11 @@ export default function () {
|
||||
sorting,
|
||||
columnFilters,
|
||||
},
|
||||
defaultColumn: {
|
||||
minSize: 0,
|
||||
size: Number.MAX_SAFE_INTEGER,
|
||||
maxSize: Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -294,8 +309,13 @@ export default function () {
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
style={{ width: `${cell.column.getSize()}px` }}
|
||||
className={'overflow-hidden relative'}
|
||||
style={{
|
||||
width:
|
||||
cell.column.getSize() === Number.MAX_SAFE_INTEGER
|
||||
? 'auto'
|
||||
: cell.column.getSize(),
|
||||
}}
|
||||
className={'overflow-hidden relative py-3'}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
|
@@ -19,11 +19,11 @@ export const navigate = (urlString: string) => {
|
||||
$router.open(urlString)
|
||||
}
|
||||
|
||||
export const $servers = atom([] as SystemRecord[])
|
||||
|
||||
export const $authenticated = atom(pb.authStore.isValid)
|
||||
pb.authStore.onChange(() => {
|
||||
$authenticated.set(pb.authStore.isValid)
|
||||
})
|
||||
|
||||
export const $servers = atom([] as SystemRecord[])
|
||||
|
||||
export const $publicKey = atom('')
|
||||
|
@@ -40,18 +40,24 @@ const App = () => {
|
||||
// update favicon
|
||||
useEffect(() => {
|
||||
if (!authenticated || !servers.length) {
|
||||
console.log('no auth favicon')
|
||||
updateFavicon('/favicon.svg')
|
||||
} else {
|
||||
const cleanup = () => {
|
||||
updateFavicon('/favicon.svg')
|
||||
}
|
||||
let up = false
|
||||
for (const server of servers) {
|
||||
if (server.status === 'down') {
|
||||
console.log('down', server)
|
||||
updateFavicon('/favicon-red.svg')
|
||||
break
|
||||
return cleanup
|
||||
} else if (server.status === 'up') {
|
||||
up = true
|
||||
}
|
||||
}
|
||||
updateFavicon(up ? '/favicon-green.svg' : '/favicon.svg')
|
||||
return cleanup
|
||||
}
|
||||
return () => {
|
||||
updateFavicon('/favicon.svg')
|
||||
|
@@ -11,7 +11,7 @@ module.exports = {
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '2rem',
|
||||
padding: '1rem',
|
||||
screens: {
|
||||
'2xl': '1400px',
|
||||
},
|
||||
@@ -22,6 +22,9 @@ module.exports = {
|
||||
// display: ['Inter', 'sans-serif'],
|
||||
},
|
||||
extend: {
|
||||
screens: {
|
||||
xs: '425px',
|
||||
},
|
||||
colors: {
|
||||
green: {
|
||||
50: '#EBF9F0',
|
||||
|
Reference in New Issue
Block a user