diff --git a/.gitattributes b/.gitattributes index 4fbd1fc..06ae4a4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -*.tsx linguist-language=Go \ No newline at end of file +*.ts linguist-language=Go \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4a6a8fc..5f4022e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ data temp .vscode beszel -beszel_data \ No newline at end of file +beszel_data +dist \ No newline at end of file diff --git a/agent/.goreleaser.yml b/agent/.goreleaser.yml new file mode 100644 index 0000000..dbb7720 --- /dev/null +++ b/agent/.goreleaser.yml @@ -0,0 +1,41 @@ +# version: 1 + +project_name: beszel-agent + +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + ignore: + - goos: darwin + goarch: 386 + - goos: linux + goarch: arm + - goos: linux + goarch: 386 + - goos: windows + goarch: arm64 + - goos: windows + goarch: 386 + +archives: + - format: tar.gz + name_template: >- + {{ .ProjectName }}_ + {{- .Os }}_ + {{- .Arch }} + # use zip for windows archives + format_overrides: + - goos: windows + format: zip + +changelog: + disable: true + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/agent/docker-compose.yml b/agent/docker-compose.yml new file mode 100644 index 0000000..263dff3 --- /dev/null +++ b/agent/docker-compose.yml @@ -0,0 +1,11 @@ +services: + agent: + image: 'henrygd/qoma-agent' + container_name: 'qoma-agent' + restart: unless-stopped + ports: + - '45876:45876' + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + - KEY="ssh-ed25519 YOUR_PUBLIC_KEY" diff --git a/agent/dockerfile b/agent/dockerfile new file mode 100644 index 0000000..23ba9a5 --- /dev/null +++ b/agent/dockerfile @@ -0,0 +1,20 @@ +FROM --platform=$BUILDPLATFORM golang:alpine as builder + +WORKDIR /app + +# Download Go modules +COPY go.mod go.sum ./ +RUN go mod download + +COPY *.go ./ + +# Build +ARG TARGETOS TARGETARCH +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /agent . + +# ? ------------------------- +FROM scratch + +COPY --from=builder /agent /agent + +ENTRYPOINT ["/agent"] \ No newline at end of file diff --git a/agent/go.mod b/agent/go.mod new file mode 100644 index 0000000..0e8b5b4 --- /dev/null +++ b/agent/go.mod @@ -0,0 +1,21 @@ +module beszel-agent + +go 1.22.4 + +require ( + github.com/gliderlabs/ssh v0.3.7 + github.com/shirou/gopsutil/v4 v4.24.6 +) + +require ( + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/sys v0.22.0 // indirect +) diff --git a/agent/go.sum b/agent/go.sum new file mode 100644 index 0000000..6b67b31 --- /dev/null +++ b/agent/go.sum @@ -0,0 +1,42 @@ +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI= +github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64= +github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/agent/main.go b/agent/main.go new file mode 100644 index 0000000..b705e0a --- /dev/null +++ b/agent/main.go @@ -0,0 +1,399 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "io" + "log" + "math" + "net" + "net/http" + "os" + "strings" + "sync" + "time" + + sshServer "github.com/gliderlabs/ssh" + + "github.com/shirou/gopsutil/v4/cpu" + "github.com/shirou/gopsutil/v4/disk" + "github.com/shirou/gopsutil/v4/host" + "github.com/shirou/gopsutil/v4/mem" + psutilNet "github.com/shirou/gopsutil/v4/net" +) + +var containerCpuMap = make(map[string][2]uint64) +var containerCpuMutex = &sync.Mutex{} + +var diskIoStats = DiskIoStats{ + Read: 0, + Write: 0, + Time: time.Now(), + Filesystem: "", +} + +var netIoStats = NetIoStats{ + BytesRecv: 0, + BytesSent: 0, + Time: time.Now(), + Name: "", +} + +// Create a custom HTTP transport +var transport = &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + d := net.Dialer{ + Timeout: 5 * time.Second, + } + // Connect to the Unix socket + return d.DialContext(ctx, "unix", "/var/run/docker.sock") + }, +} + +type SystemData struct { + Stats SystemStats `json:"stats"` + Info SystemInfo `json:"info"` + Containers []ContainerStats `json:"container"` +} + +type SystemInfo struct { + Cores int `json:"c"` + Threads int `json:"t"` + CpuModel string `json:"m"` + // Os string `json:"o"` + Uptime uint64 `json:"u"` + Cpu float64 `json:"cpu"` + MemPct float64 `json:"mp"` + DiskPct float64 `json:"dp"` +} + +type SystemStats struct { + Cpu float64 `json:"cpu"` + Mem float64 `json:"m"` + MemUsed float64 `json:"mu"` + MemPct float64 `json:"mp"` + MemBuffCache float64 `json:"mb"` + Disk float64 `json:"d"` + DiskUsed float64 `json:"du"` + DiskPct float64 `json:"dp"` + DiskRead float64 `json:"dr"` + DiskWrite float64 `json:"dw"` + NetworkSent float64 `json:"ns"` + NetworkRecv float64 `json:"nr"` +} + +type ContainerStats struct { + Name string `json:"n"` + Cpu float64 `json:"c"` + Mem float64 `json:"m"` + // MemPct float64 `json:"mp"` +} + +func getSystemStats() (SystemInfo, SystemStats) { + c, _ := cpu.Percent(0, false) + v, _ := mem.VirtualMemory() + d, _ := disk.Usage("/") + + cpuPct := twoDecimals(c[0]) + memPct := twoDecimals(v.UsedPercent) + diskPct := twoDecimals(d.UsedPercent) + + systemStats := SystemStats{ + Cpu: cpuPct, + Mem: bytesToGigabytes(v.Total), + MemUsed: bytesToGigabytes(v.Used), + MemBuffCache: bytesToGigabytes(v.Total - v.Free - v.Used), + MemPct: memPct, + Disk: bytesToGigabytes(d.Total), + DiskUsed: bytesToGigabytes(d.Used), + DiskPct: diskPct, + } + + systemInfo := SystemInfo{ + Cpu: cpuPct, + MemPct: memPct, + DiskPct: diskPct, + } + + // add disk stats + if io, err := disk.IOCounters(diskIoStats.Filesystem); err == nil { + for _, d := range io { + // add to systemStats + secondsElapsed := time.Since(diskIoStats.Time).Seconds() + readPerSecond := float64(d.ReadBytes-diskIoStats.Read) / secondsElapsed + systemStats.DiskRead = bytesToMegabytes(readPerSecond) + writePerSecond := float64(d.WriteBytes-diskIoStats.Write) / secondsElapsed + systemStats.DiskWrite = bytesToMegabytes(writePerSecond) + // update diskIoStats + diskIoStats.Time = time.Now() + diskIoStats.Read = d.ReadBytes + diskIoStats.Write = d.WriteBytes + } + } + + // add network stats + if netIO, err := psutilNet.IOCounters(true); err == nil { + bytesSent := uint64(0) + bytesRecv := uint64(0) + for _, v := range netIO { + if skipNetworkInterface(v.Name) { + continue + } + // log.Printf("%+v: %+v recv, %+v sent\n", v.Name, v.BytesRecv, v.BytesSent) + bytesSent += v.BytesSent + bytesRecv += v.BytesRecv + } + // add to systemStats + secondsElapsed := time.Since(netIoStats.Time).Seconds() + sentPerSecond := float64(bytesSent-netIoStats.BytesSent) / secondsElapsed + recvPerSecond := float64(bytesRecv-netIoStats.BytesRecv) / secondsElapsed + systemStats.NetworkSent = bytesToMegabytes(sentPerSecond) + systemStats.NetworkRecv = bytesToMegabytes(recvPerSecond) + // update netIoStats + netIoStats.BytesSent = bytesSent + netIoStats.BytesRecv = bytesRecv + netIoStats.Time = time.Now() + } + + // add host stats + if info, err := host.Info(); err == nil { + systemInfo.Uptime = info.Uptime + // systemInfo.Os = info.OS + } + // add cpu stats + if info, err := cpu.Info(); err == nil { + systemInfo.CpuModel = info[0].ModelName + } + if cores, err := cpu.Counts(false); err == nil { + systemInfo.Cores = cores + } + if threads, err := cpu.Counts(true); err == nil { + systemInfo.Threads = threads + } + + return systemInfo, systemStats + +} + +func getDockerStats() ([]ContainerStats, error) { + client := &http.Client{ + Transport: transport, + } + // Create a new HTTP request + req, err := http.NewRequest("GET", "http://localhost/containers/json", nil) + if err != nil { + return []ContainerStats{}, err + } + // Perform the request + resp, err := client.Do(req) + if err != nil { + return []ContainerStats{}, err + } + defer resp.Body.Close() + + var containers []Container + if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil { + panic(err) + } + + var wg sync.WaitGroup + var containerStats []ContainerStats + + for _, ctr := range containers { + wg.Add(1) + go func() { + defer wg.Done() + cstats, err := getContainerStats(ctr) + if err != nil { + log.Printf("Error getting container stats: %+v\n", err) + return + } + containerStats = append(containerStats, cstats) + }() + } + + // clean up old containers from map + validNames := make(map[string]struct{}, len(containers)) + for _, ctr := range containers { + validNames[ctr.Names[0][1:]] = struct{}{} + } + for name := range containerCpuMap { + if _, exists := validNames[name]; !exists { + delete(containerCpuMap, name) + } + } + + wg.Wait() + + return containerStats, nil +} + +func getContainerStats(ctr Container) (ContainerStats, error) { + // stats, _ := apiClient.ContainerStats(context.Background(), ctr.ID, false) + client := &http.Client{ + Transport: transport, + } + // Create a new HTTP request + req, err := http.NewRequest("GET", "http://localhost/containers/"+ctr.ID+"/stats?stream=0&one-shot=1", nil) + if err != nil { + return ContainerStats{}, err + } + // Perform the request + resp, err := client.Do(req) + if err != nil { + return ContainerStats{}, err + } + defer resp.Body.Close() + + var statsJson CStats + if err := json.NewDecoder(resp.Body).Decode(&statsJson); err != nil { + panic(err) + } + + name := ctr.Names[0][1:] + + // memory + usedMemory := statsJson.MemoryStats.Usage - statsJson.MemoryStats.Cache + // pctMemory := float64(usedMemory) / float64(statsJson.MemoryStats.Limit) * 100 + + // cpu + // add default values to containerCpu if it doesn't exist + containerCpuMutex.Lock() + defer containerCpuMutex.Unlock() + if _, ok := containerCpuMap[name]; !ok { + containerCpuMap[name] = [2]uint64{0, 0} + } + cpuDelta := statsJson.CPUStats.CPUUsage.TotalUsage - containerCpuMap[name][0] + systemDelta := statsJson.CPUStats.SystemUsage - containerCpuMap[name][1] + cpuPct := float64(cpuDelta) / float64(systemDelta) * 100 + if cpuPct > 100 { + return ContainerStats{}, errors.New("cpu pct is greater than 100") + } + containerCpuMap[name] = [2]uint64{statsJson.CPUStats.CPUUsage.TotalUsage, statsJson.CPUStats.SystemUsage} + + cStats := ContainerStats{ + Name: name, + Cpu: twoDecimals(cpuPct), + Mem: bytesToMegabytes(float64(usedMemory)), + // MemPct: maxDecimals(pctMemory, 2), + } + return cStats, nil +} + +func gatherStats() SystemData { + systemInfo, systemStats := getSystemStats() + stats := SystemData{ + Stats: systemStats, + Info: systemInfo, + Containers: []ContainerStats{}, + } + containerStats, err := getDockerStats() + if err == nil { + stats.Containers = containerStats + } + // fmt.Printf("%+v\n", stats) + return stats +} + +func startServer(port string, pubKey []byte) { + sshServer.Handle(func(s sshServer.Session) { + stats := gatherStats() + var jsonStats []byte + jsonStats, _ = json.Marshal(stats) + io.WriteString(s, string(jsonStats)) + s.Exit(0) + }) + + log.Printf("Starting SSH server on port %s", port) + if err := sshServer.ListenAndServe(":"+port, nil, sshServer.NoPty(), + sshServer.PublicKeyAuth(func(ctx sshServer.Context, key sshServer.PublicKey) bool { + data := []byte(pubKey) + allowed, _, _, _, _ := sshServer.ParseAuthorizedKey(data) + return sshServer.KeysEqual(key, allowed) + }), + ); err != nil { + log.Fatal(err) + } +} + +func main() { + var pubKey []byte + if pubKeyEnv, exists := os.LookupEnv("KEY"); exists { + pubKey = []byte(pubKeyEnv) + } else { + pubKey = []byte("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJgPK8kpPOwPFIq6BIa7Bu/xwrjt5VRQCz3az3Glt4jp") + // log.Fatal("KEY environment variable is not set") + } + + if filesystem, exists := os.LookupEnv("FILESYSTEM"); exists { + diskIoStats.Filesystem = filesystem + } else { + diskIoStats.Filesystem = findDefaultFilesystem() + } + + initializeDiskIoStats() + initializeNetIoStats() + + if port, exists := os.LookupEnv("PORT"); exists { + startServer(port, pubKey) + } else { + startServer("45876", pubKey) + } +} + +func bytesToMegabytes(b float64) float64 { + return twoDecimals(b / 1048576) +} + +func bytesToGigabytes(b uint64) float64 { + return twoDecimals(float64(b) / 1073741824) +} + +func twoDecimals(value float64) float64 { + return math.Round(value*100) / 100 +} + +func findDefaultFilesystem() string { + if partitions, err := disk.Partitions(false); err == nil { + for _, v := range partitions { + if v.Mountpoint == "/" { + log.Printf("Using filesystem: %+v\n", v.Device) + return v.Device + } + } + } + return "" +} + +func skipNetworkInterface(name string) bool { + return strings.HasPrefix(name, "lo") || strings.HasPrefix(name, "docker") || strings.HasPrefix(name, "br-") || strings.HasPrefix(name, "veth") +} + +func initializeDiskIoStats() { + if io, err := disk.IOCounters(diskIoStats.Filesystem); err == nil { + for _, d := range io { + diskIoStats.Time = time.Now() + diskIoStats.Read = d.ReadBytes + diskIoStats.Write = d.WriteBytes + } + } +} + +func initializeNetIoStats() { + if netIO, err := psutilNet.IOCounters(true); err == nil { + bytesSent := uint64(0) + bytesRecv := uint64(0) + for _, v := range netIO { + if skipNetworkInterface(v.Name) { + continue + } + log.Printf("Found network interface: %+v (%+v recv, %+v sent)\n", v.Name, v.BytesRecv, v.BytesSent) + bytesSent += v.BytesSent + bytesRecv += v.BytesRecv + } + netIoStats.BytesSent = bytesSent + netIoStats.BytesRecv = bytesRecv + netIoStats.Time = time.Now() + } +} diff --git a/agent/types.go b/agent/types.go new file mode 100644 index 0000000..bd34eeb --- /dev/null +++ b/agent/types.go @@ -0,0 +1,116 @@ +package main + +import "time" + +type Container struct { + ID string `json:"Id"` + Names []string + Image string + ImageID string + Command string + Created int64 + // Ports []Port + SizeRw int64 `json:",omitempty"` + SizeRootFs int64 `json:",omitempty"` + Labels map[string]string + State string + Status string + HostConfig struct { + NetworkMode string `json:",omitempty"` + Annotations map[string]string `json:",omitempty"` + } + // NetworkSettings *SummaryNetworkSettings + // Mounts []MountPoint +} + +type CStats struct { + // Common stats + Read time.Time `json:"read"` + PreRead time.Time `json:"preread"` + + // Linux specific stats, not populated on Windows. + // PidsStats PidsStats `json:"pids_stats,omitempty"` + // BlkioStats BlkioStats `json:"blkio_stats,omitempty"` + + // Windows specific stats, not populated on Linux. + NumProcs uint32 `json:"num_procs"` + // StorageStats StorageStats `json:"storage_stats,omitempty"` + + // Shared stats + CPUStats CPUStats `json:"cpu_stats,omitempty"` + PreCPUStats CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous" + MemoryStats MemoryStats `json:"memory_stats,omitempty"` +} + +type CPUStats struct { + // CPU Usage. Linux and Windows. + CPUUsage CPUUsage `json:"cpu_usage"` + + // System Usage. Linux only. + SystemUsage uint64 `json:"system_cpu_usage,omitempty"` + + // Online CPUs. Linux only. + OnlineCPUs uint32 `json:"online_cpus,omitempty"` + + // Throttling Data. Linux only. + // ThrottlingData ThrottlingData `json:"throttling_data,omitempty"` +} + +type CPUUsage struct { + // Total CPU time consumed. + // Units: nanoseconds (Linux) + // Units: 100's of nanoseconds (Windows) + TotalUsage uint64 `json:"total_usage"` + + // Total CPU time consumed per core (Linux). Not used on Windows. + // Units: nanoseconds. + PercpuUsage []uint64 `json:"percpu_usage,omitempty"` + + // Time spent by tasks of the cgroup in kernel mode (Linux). + // Time spent by all container processes in kernel mode (Windows). + // Units: nanoseconds (Linux). + // Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers. + UsageInKernelmode uint64 `json:"usage_in_kernelmode"` + + // Time spent by tasks of the cgroup in user mode (Linux). + // Time spent by all container processes in user mode (Windows). + // Units: nanoseconds (Linux). + // Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers + UsageInUsermode uint64 `json:"usage_in_usermode"` +} + +type MemoryStats struct { + + // current res_counter usage for memory + Usage uint64 `json:"usage,omitempty"` + Cache uint64 `json:"cache,omitempty"` + // maximum usage ever recorded. + MaxUsage uint64 `json:"max_usage,omitempty"` + // TODO(vishh): Export these as stronger types. + // all the stats exported via memory.stat. + Stats map[string]uint64 `json:"stats,omitempty"` + // number of times memory usage hits limits. + Failcnt uint64 `json:"failcnt,omitempty"` + Limit uint64 `json:"limit,omitempty"` + + // committed bytes + Commit uint64 `json:"commitbytes,omitempty"` + // peak committed bytes + CommitPeak uint64 `json:"commitpeakbytes,omitempty"` + // private working set + PrivateWorkingSet uint64 `json:"privateworkingset,omitempty"` +} + +type DiskIoStats struct { + Read uint64 + Write uint64 + Time time.Time + Filesystem string +} + +type NetIoStats struct { + BytesRecv uint64 + BytesSent uint64 + Time time.Time + Name string +} diff --git a/hub/.goreleaser.yml b/hub/.goreleaser.yml new file mode 100644 index 0000000..eece23a --- /dev/null +++ b/hub/.goreleaser.yml @@ -0,0 +1,41 @@ +# version: 1 + +project_name: beszel + +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + ignore: + - goos: darwin + goarch: 386 + - goos: linux + goarch: arm + - goos: linux + goarch: 386 + - goos: windows + goarch: arm64 + - goos: windows + goarch: 386 + +archives: + - format: tar.gz + name_template: >- + {{ .ProjectName }}_ + {{- .Os }}_ + {{- .Arch }} + # use zip for windows archives + format_overrides: + - goos: windows + format: zip + +changelog: + disable: true + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/dockerfile b/hub/dockerfile similarity index 88% rename from dockerfile rename to hub/dockerfile index 5125319..fb50343 100644 --- a/dockerfile +++ b/hub/dockerfile @@ -11,7 +11,7 @@ COPY migrations ./migrations # Build ARG TARGETOS TARGETARCH -RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /server . +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-w -s" -o /beszel . # ? ------------------------- FROM alpine:latest diff --git a/go.mod b/hub/go.mod similarity index 100% rename from go.mod rename to hub/go.mod diff --git a/go.sum b/hub/go.sum similarity index 100% rename from go.sum rename to hub/go.sum diff --git a/main.go b/hub/main.go similarity index 100% rename from main.go rename to hub/main.go diff --git a/migrations/1720568457_collections_snapshot.go b/hub/migrations/1720568457_collections_snapshot.go similarity index 100% rename from migrations/1720568457_collections_snapshot.go rename to hub/migrations/1720568457_collections_snapshot.go diff --git a/migrations/initial-settings.go b/hub/migrations/initial-settings.go similarity index 100% rename from migrations/initial-settings.go rename to hub/migrations/initial-settings.go diff --git a/records.go b/hub/records.go similarity index 100% rename from records.go rename to hub/records.go diff --git a/site/.gitignore b/hub/site/.gitignore similarity index 100% rename from site/.gitignore rename to hub/site/.gitignore diff --git a/site/bun.lockb b/hub/site/bun.lockb similarity index 100% rename from site/bun.lockb rename to hub/site/bun.lockb diff --git a/site/components.json b/hub/site/components.json similarity index 100% rename from site/components.json rename to hub/site/components.json diff --git a/site/embed.go b/hub/site/embed.go similarity index 100% rename from site/embed.go rename to hub/site/embed.go diff --git a/site/index.html b/hub/site/index.html similarity index 100% rename from site/index.html rename to hub/site/index.html diff --git a/site/package.json b/hub/site/package.json similarity index 100% rename from site/package.json rename to hub/site/package.json diff --git a/site/postcss.config.js b/hub/site/postcss.config.js similarity index 100% rename from site/postcss.config.js rename to hub/site/postcss.config.js diff --git a/site/public/static/apple.svg b/hub/site/public/static/apple.svg similarity index 100% rename from site/public/static/apple.svg rename to hub/site/public/static/apple.svg diff --git a/site/public/static/bitbucket.svg b/hub/site/public/static/bitbucket.svg similarity index 100% rename from site/public/static/bitbucket.svg rename to hub/site/public/static/bitbucket.svg diff --git a/site/public/static/discord.svg b/hub/site/public/static/discord.svg similarity index 100% rename from site/public/static/discord.svg rename to hub/site/public/static/discord.svg diff --git a/site/public/static/facebook.svg b/hub/site/public/static/facebook.svg similarity index 100% rename from site/public/static/facebook.svg rename to hub/site/public/static/facebook.svg diff --git a/site/public/static/favicon-green.svg b/hub/site/public/static/favicon-green.svg similarity index 100% rename from site/public/static/favicon-green.svg rename to hub/site/public/static/favicon-green.svg diff --git a/site/public/static/favicon-red.svg b/hub/site/public/static/favicon-red.svg similarity index 100% rename from site/public/static/favicon-red.svg rename to hub/site/public/static/favicon-red.svg diff --git a/site/public/static/favicon.svg b/hub/site/public/static/favicon.svg similarity index 100% rename from site/public/static/favicon.svg rename to hub/site/public/static/favicon.svg diff --git a/site/public/static/gitea.svg b/hub/site/public/static/gitea.svg similarity index 100% rename from site/public/static/gitea.svg rename to hub/site/public/static/gitea.svg diff --git a/site/public/static/gitee.svg b/hub/site/public/static/gitee.svg similarity index 100% rename from site/public/static/gitee.svg rename to hub/site/public/static/gitee.svg diff --git a/site/public/static/github.svg b/hub/site/public/static/github.svg similarity index 100% rename from site/public/static/github.svg rename to hub/site/public/static/github.svg diff --git a/site/public/static/gitlab.svg b/hub/site/public/static/gitlab.svg similarity index 100% rename from site/public/static/gitlab.svg rename to hub/site/public/static/gitlab.svg diff --git a/site/public/static/google.svg b/hub/site/public/static/google.svg similarity index 100% rename from site/public/static/google.svg rename to hub/site/public/static/google.svg diff --git a/site/public/static/instagram.svg b/hub/site/public/static/instagram.svg similarity index 100% rename from site/public/static/instagram.svg rename to hub/site/public/static/instagram.svg diff --git a/site/public/static/lock.svg b/hub/site/public/static/lock.svg similarity index 100% rename from site/public/static/lock.svg rename to hub/site/public/static/lock.svg diff --git a/site/public/static/oidc.svg b/hub/site/public/static/oidc.svg similarity index 100% rename from site/public/static/oidc.svg rename to hub/site/public/static/oidc.svg diff --git a/site/public/static/patreon.svg b/hub/site/public/static/patreon.svg similarity index 100% rename from site/public/static/patreon.svg rename to hub/site/public/static/patreon.svg diff --git a/site/public/static/spotify.svg b/hub/site/public/static/spotify.svg similarity index 100% rename from site/public/static/spotify.svg rename to hub/site/public/static/spotify.svg diff --git a/site/public/static/strava.svg b/hub/site/public/static/strava.svg similarity index 100% rename from site/public/static/strava.svg rename to hub/site/public/static/strava.svg diff --git a/site/public/static/twitch.svg b/hub/site/public/static/twitch.svg similarity index 100% rename from site/public/static/twitch.svg rename to hub/site/public/static/twitch.svg diff --git a/site/public/static/twitter.svg b/hub/site/public/static/twitter.svg similarity index 100% rename from site/public/static/twitter.svg rename to hub/site/public/static/twitter.svg diff --git a/site/src/components/add-server.tsx b/hub/site/src/components/add-server.tsx similarity index 100% rename from site/src/components/add-server.tsx rename to hub/site/src/components/add-server.tsx diff --git a/site/src/components/charts/bandwidth-chart.tsx b/hub/site/src/components/charts/bandwidth-chart.tsx similarity index 100% rename from site/src/components/charts/bandwidth-chart.tsx rename to hub/site/src/components/charts/bandwidth-chart.tsx diff --git a/site/src/components/charts/chart-time-select.tsx b/hub/site/src/components/charts/chart-time-select.tsx similarity index 100% rename from site/src/components/charts/chart-time-select.tsx rename to hub/site/src/components/charts/chart-time-select.tsx diff --git a/site/src/components/charts/container-cpu-chart.tsx b/hub/site/src/components/charts/container-cpu-chart.tsx similarity index 100% rename from site/src/components/charts/container-cpu-chart.tsx rename to hub/site/src/components/charts/container-cpu-chart.tsx diff --git a/site/src/components/charts/container-mem-chart.tsx b/hub/site/src/components/charts/container-mem-chart.tsx similarity index 100% rename from site/src/components/charts/container-mem-chart.tsx rename to hub/site/src/components/charts/container-mem-chart.tsx diff --git a/site/src/components/charts/cpu-chart.tsx b/hub/site/src/components/charts/cpu-chart.tsx similarity index 100% rename from site/src/components/charts/cpu-chart.tsx rename to hub/site/src/components/charts/cpu-chart.tsx diff --git a/site/src/components/charts/disk-chart.tsx b/hub/site/src/components/charts/disk-chart.tsx similarity index 100% rename from site/src/components/charts/disk-chart.tsx rename to hub/site/src/components/charts/disk-chart.tsx diff --git a/site/src/components/charts/disk-io-chart.tsx b/hub/site/src/components/charts/disk-io-chart.tsx similarity index 100% rename from site/src/components/charts/disk-io-chart.tsx rename to hub/site/src/components/charts/disk-io-chart.tsx diff --git a/site/src/components/charts/mem-chart.tsx b/hub/site/src/components/charts/mem-chart.tsx similarity index 100% rename from site/src/components/charts/mem-chart.tsx rename to hub/site/src/components/charts/mem-chart.tsx diff --git a/site/src/components/command-palette.tsx b/hub/site/src/components/command-palette.tsx similarity index 100% rename from site/src/components/command-palette.tsx rename to hub/site/src/components/command-palette.tsx diff --git a/site/src/components/login/auth-form.tsx b/hub/site/src/components/login/auth-form.tsx similarity index 100% rename from site/src/components/login/auth-form.tsx rename to hub/site/src/components/login/auth-form.tsx diff --git a/site/src/components/login/forgot-pass-form.tsx b/hub/site/src/components/login/forgot-pass-form.tsx similarity index 100% rename from site/src/components/login/forgot-pass-form.tsx rename to hub/site/src/components/login/forgot-pass-form.tsx diff --git a/site/src/components/login/login.tsx b/hub/site/src/components/login/login.tsx similarity index 100% rename from site/src/components/login/login.tsx rename to hub/site/src/components/login/login.tsx diff --git a/site/src/components/logo.tsx b/hub/site/src/components/logo.tsx similarity index 100% rename from site/src/components/logo.tsx rename to hub/site/src/components/logo.tsx diff --git a/site/src/components/mode-toggle.tsx b/hub/site/src/components/mode-toggle.tsx similarity index 100% rename from site/src/components/mode-toggle.tsx rename to hub/site/src/components/mode-toggle.tsx diff --git a/site/src/components/router.tsx b/hub/site/src/components/router.tsx similarity index 100% rename from site/src/components/router.tsx rename to hub/site/src/components/router.tsx diff --git a/site/src/components/routes/home.tsx b/hub/site/src/components/routes/home.tsx similarity index 100% rename from site/src/components/routes/home.tsx rename to hub/site/src/components/routes/home.tsx diff --git a/site/src/components/routes/server.tsx b/hub/site/src/components/routes/server.tsx similarity index 100% rename from site/src/components/routes/server.tsx rename to hub/site/src/components/routes/server.tsx diff --git a/site/src/components/server-table/columns.tsx b/hub/site/src/components/server-table/columns.tsx similarity index 100% rename from site/src/components/server-table/columns.tsx rename to hub/site/src/components/server-table/columns.tsx diff --git a/site/src/components/server-table/systems-table.tsx b/hub/site/src/components/server-table/systems-table.tsx similarity index 100% rename from site/src/components/server-table/systems-table.tsx rename to hub/site/src/components/server-table/systems-table.tsx diff --git a/site/src/components/spinner.tsx b/hub/site/src/components/spinner.tsx similarity index 100% rename from site/src/components/spinner.tsx rename to hub/site/src/components/spinner.tsx diff --git a/site/src/components/table-alerts.tsx b/hub/site/src/components/table-alerts.tsx similarity index 100% rename from site/src/components/table-alerts.tsx rename to hub/site/src/components/table-alerts.tsx diff --git a/site/src/components/theme-provider.tsx b/hub/site/src/components/theme-provider.tsx similarity index 100% rename from site/src/components/theme-provider.tsx rename to hub/site/src/components/theme-provider.tsx diff --git a/site/src/components/ui/alert-dialog.tsx b/hub/site/src/components/ui/alert-dialog.tsx similarity index 100% rename from site/src/components/ui/alert-dialog.tsx rename to hub/site/src/components/ui/alert-dialog.tsx diff --git a/site/src/components/ui/button.tsx b/hub/site/src/components/ui/button.tsx similarity index 100% rename from site/src/components/ui/button.tsx rename to hub/site/src/components/ui/button.tsx diff --git a/site/src/components/ui/card.tsx b/hub/site/src/components/ui/card.tsx similarity index 100% rename from site/src/components/ui/card.tsx rename to hub/site/src/components/ui/card.tsx diff --git a/site/src/components/ui/chart.tsx b/hub/site/src/components/ui/chart.tsx similarity index 100% rename from site/src/components/ui/chart.tsx rename to hub/site/src/components/ui/chart.tsx diff --git a/site/src/components/ui/command.tsx b/hub/site/src/components/ui/command.tsx similarity index 100% rename from site/src/components/ui/command.tsx rename to hub/site/src/components/ui/command.tsx diff --git a/site/src/components/ui/dialog.tsx b/hub/site/src/components/ui/dialog.tsx similarity index 100% rename from site/src/components/ui/dialog.tsx rename to hub/site/src/components/ui/dialog.tsx diff --git a/site/src/components/ui/dropdown-menu.tsx b/hub/site/src/components/ui/dropdown-menu.tsx similarity index 100% rename from site/src/components/ui/dropdown-menu.tsx rename to hub/site/src/components/ui/dropdown-menu.tsx diff --git a/site/src/components/ui/input.tsx b/hub/site/src/components/ui/input.tsx similarity index 100% rename from site/src/components/ui/input.tsx rename to hub/site/src/components/ui/input.tsx diff --git a/site/src/components/ui/label.tsx b/hub/site/src/components/ui/label.tsx similarity index 100% rename from site/src/components/ui/label.tsx rename to hub/site/src/components/ui/label.tsx diff --git a/site/src/components/ui/select.tsx b/hub/site/src/components/ui/select.tsx similarity index 100% rename from site/src/components/ui/select.tsx rename to hub/site/src/components/ui/select.tsx diff --git a/site/src/components/ui/separator.tsx b/hub/site/src/components/ui/separator.tsx similarity index 100% rename from site/src/components/ui/separator.tsx rename to hub/site/src/components/ui/separator.tsx diff --git a/site/src/components/ui/switch.tsx b/hub/site/src/components/ui/switch.tsx similarity index 100% rename from site/src/components/ui/switch.tsx rename to hub/site/src/components/ui/switch.tsx diff --git a/site/src/components/ui/table.tsx b/hub/site/src/components/ui/table.tsx similarity index 100% rename from site/src/components/ui/table.tsx rename to hub/site/src/components/ui/table.tsx diff --git a/site/src/components/ui/toast.tsx b/hub/site/src/components/ui/toast.tsx similarity index 100% rename from site/src/components/ui/toast.tsx rename to hub/site/src/components/ui/toast.tsx diff --git a/site/src/components/ui/toaster.tsx b/hub/site/src/components/ui/toaster.tsx similarity index 100% rename from site/src/components/ui/toaster.tsx rename to hub/site/src/components/ui/toaster.tsx diff --git a/site/src/components/ui/tooltip.tsx b/hub/site/src/components/ui/tooltip.tsx similarity index 100% rename from site/src/components/ui/tooltip.tsx rename to hub/site/src/components/ui/tooltip.tsx diff --git a/site/src/components/ui/use-toast.ts b/hub/site/src/components/ui/use-toast.ts similarity index 100% rename from site/src/components/ui/use-toast.ts rename to hub/site/src/components/ui/use-toast.ts diff --git a/site/src/index.css b/hub/site/src/index.css similarity index 100% rename from site/src/index.css rename to hub/site/src/index.css diff --git a/site/src/lib/stores.ts b/hub/site/src/lib/stores.ts similarity index 100% rename from site/src/lib/stores.ts rename to hub/site/src/lib/stores.ts diff --git a/site/src/lib/utils.ts b/hub/site/src/lib/utils.ts similarity index 100% rename from site/src/lib/utils.ts rename to hub/site/src/lib/utils.ts diff --git a/site/src/main.tsx b/hub/site/src/main.tsx similarity index 100% rename from site/src/main.tsx rename to hub/site/src/main.tsx diff --git a/site/src/types.d.ts b/hub/site/src/types.d.ts similarity index 100% rename from site/src/types.d.ts rename to hub/site/src/types.d.ts diff --git a/site/src/vite-env.d.ts b/hub/site/src/vite-env.d.ts similarity index 100% rename from site/src/vite-env.d.ts rename to hub/site/src/vite-env.d.ts diff --git a/site/tailwind.config.js b/hub/site/tailwind.config.js similarity index 100% rename from site/tailwind.config.js rename to hub/site/tailwind.config.js diff --git a/site/tsconfig.app.json b/hub/site/tsconfig.app.json similarity index 100% rename from site/tsconfig.app.json rename to hub/site/tsconfig.app.json diff --git a/site/tsconfig.json b/hub/site/tsconfig.json similarity index 100% rename from site/tsconfig.json rename to hub/site/tsconfig.json diff --git a/site/tsconfig.node.json b/hub/site/tsconfig.node.json similarity index 100% rename from site/tsconfig.node.json rename to hub/site/tsconfig.node.json diff --git a/site/vite.config.ts b/hub/site/vite.config.ts similarity index 100% rename from site/vite.config.ts rename to hub/site/vite.config.ts diff --git a/types.go b/hub/types.go similarity index 100% rename from types.go rename to hub/types.go diff --git a/update.go b/hub/update.go similarity index 100% rename from update.go rename to hub/update.go