pre release refactoring + update changelog

This commit is contained in:
henrygd
2025-10-25 16:34:32 -04:00
parent a9657f9c00
commit 74b78e96b3
6 changed files with 39 additions and 16 deletions

View File

@@ -46,7 +46,7 @@ func (sm *SmartManager) Refresh() error {
scanErr := sm.ScanDevices() scanErr := sm.ScanDevices()
if scanErr != nil { if scanErr != nil {
slog.Warn("smartctl scan failed", "err", scanErr) slog.Debug("smartctl scan failed", "err", scanErr)
} }
devices := sm.devicesSnapshot() devices := sm.devicesSnapshot()
@@ -56,7 +56,7 @@ func (sm *SmartManager) Refresh() error {
continue continue
} }
if err := sm.CollectSmart(deviceInfo); err != nil { if err := sm.CollectSmart(deviceInfo); err != nil {
slog.Info("smartctl collect failed for device, skipping", "device", deviceInfo.Name, "err", err) slog.Debug("smartctl collect failed, skipping", "device", deviceInfo.Name, "err", err)
collectErr = err collectErr = err
} }
} }
@@ -220,7 +220,7 @@ func (sm *SmartManager) parseScan(output []byte) bool {
scan := &scanOutput{} scan := &scanOutput{}
if err := json.Unmarshal(output, scan); err != nil { if err := json.Unmarshal(output, scan); err != nil {
slog.Warn("Failed to parse smartctl scan JSON", "err", err) slog.Debug("Failed to parse smartctl scan JSON", "err", err)
return false return false
} }
@@ -260,7 +260,7 @@ func (sm *SmartManager) parseSmartForSata(output []byte) (bool, int) {
} }
if data.SerialNumber == "" { if data.SerialNumber == "" {
slog.Warn("device has no serial number, skipping", "device", data.Device.Name) slog.Debug("device has no serial number, skipping", "device", data.Device.Name)
return false, data.Smartctl.ExitStatus return false, data.Smartctl.ExitStatus
} }
@@ -327,7 +327,7 @@ func (sm *SmartManager) parseSmartForNvme(output []byte) (bool, int) {
} }
if data.SerialNumber == "" { if data.SerialNumber == "" {
slog.Warn("device has no serial number, skipping", "device", data.Device.Name) slog.Debug("device has no serial number, skipping", "device", data.Device.Name)
return false, data.Smartctl.ExitStatus return false, data.Smartctl.ExitStatus
} }
@@ -386,7 +386,7 @@ func (sm *SmartManager) detectSmartctl() error {
if _, err := exec.LookPath("smartctl"); err == nil { if _, err := exec.LookPath("smartctl"); err == nil {
return nil return nil
} }
return fmt.Errorf("no smartctl found - install smartctl") return fmt.Errorf("smartctl not found")
} }
// NewSmartManager creates and initializes a new SmartManager // NewSmartManager creates and initializes a new SmartManager

View File

@@ -26,7 +26,7 @@ export default memo(function AlertsButton({ system }: { system: SystemRecord })
/> />
</Button> </Button>
</SheetTrigger> </SheetTrigger>
<SheetContent className="max-h-full overflow-auto w-145 !max-w-full p-4 sm:p-6"> <SheetContent className="max-h-full overflow-auto w-150 !max-w-full p-4 sm:p-6">
{opened && <AlertDialogContent system={system} />} {opened && <AlertDialogContent system={system} />}
</SheetContent> </SheetContent>
</Sheet> </Sheet>

View File

@@ -1003,15 +1003,16 @@ export default memo(function SystemDetail({ id }: { id: string }) {
</div> </div>
)} )}
{compareSemVer(chartData.agentVersion, parseSemVer("0.15.0")) >= 0 && (
<LazySmartTable systemId={system.id} />
)}
{containerData.length > 0 && compareSemVer(chartData.agentVersion, parseSemVer("0.14.0")) >= 0 && ( {containerData.length > 0 && compareSemVer(chartData.agentVersion, parseSemVer("0.14.0")) >= 0 && (
<LazyContainersTable systemId={id} /> <LazyContainersTable systemId={id} />
)} )}
<LazySmartTable systemId={system.id} />
</div> </div>
{/* add space for tooltip if more than 12 containers */} {/* add space for tooltip if lots of sensors */}
{bottomSpacing > 0 && <span className="block" style={{ height: bottomSpacing }} />} {bottomSpacing > 0 && <span className="block" style={{ height: bottomSpacing }} />}
</> </>
) )
@@ -1144,7 +1145,7 @@ const ContainersTable = lazy(() => import("../containers-table/containers-table"
function LazyContainersTable({ systemId }: { systemId: string }) { function LazyContainersTable({ systemId }: { systemId: string }) {
const { isIntersecting, ref } = useIntersectionObserver({ rootMargin: "90px" }) const { isIntersecting, ref } = useIntersectionObserver({ rootMargin: "90px" })
return ( return (
<div ref={ref}> <div ref={ref} className={cn(isIntersecting && "contents")}>
{isIntersecting && <ContainersTable systemId={systemId} />} {isIntersecting && <ContainersTable systemId={systemId} />}
</div> </div>
) )
@@ -1153,9 +1154,9 @@ function LazyContainersTable({ systemId }: { systemId: string }) {
const SmartTable = lazy(() => import("./system/smart-table")) const SmartTable = lazy(() => import("./system/smart-table"))
function LazySmartTable({ systemId }: { systemId: string }) { function LazySmartTable({ systemId }: { systemId: string }) {
const { isIntersecting, ref } = useIntersectionObserver() const { isIntersecting, ref } = useIntersectionObserver({ rootMargin: "90px" })
return ( return (
<div ref={ref}> <div ref={ref} className={cn(isIntersecting && "contents")}>
{isIntersecting && <SmartTable systemId={systemId} />} {isIntersecting && <SmartTable systemId={systemId} />}
</div> </div>
) )

View File

@@ -252,7 +252,7 @@ export const columns: ColumnDef<DiskInfo>[] = [
] ]
export default function DisksTable({ systemId }: { systemId: string }) { export default function DisksTable({ systemId }: { systemId: string }) {
const [sorting, setSorting] = React.useState<SortingState>([{ id: "device", desc: false }]) // const [sorting, setSorting] = React.useState<SortingState>([{ id: "device", desc: false }])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]) const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
const [rowSelection, setRowSelection] = React.useState({}) const [rowSelection, setRowSelection] = React.useState({})
const [smartData, setSmartData] = React.useState<Record<string, SmartData> | undefined>(undefined) const [smartData, setSmartData] = React.useState<Record<string, SmartData> | undefined>(undefined)
@@ -284,19 +284,23 @@ export default function DisksTable({ systemId }: { systemId: string }) {
const table = useReactTable({ const table = useReactTable({
data: diskData, data: diskData,
columns: columns, columns: columns,
onSortingChange: setSorting, // onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters, onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(), getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(), getFilteredRowModel: getFilteredRowModel(),
onRowSelectionChange: setRowSelection, onRowSelectionChange: setRowSelection,
state: { state: {
sorting, // sorting,
columnFilters, columnFilters,
rowSelection, rowSelection,
}, },
}) })
if (!diskData.length && !columnFilters.length) {
return null
}
return ( return (
<div> <div>
<Card className="p-6 @container w-full"> <Card className="p-6 @container w-full">

View File

@@ -367,6 +367,12 @@ export function formatDuration(
.join(" ") .join(" ")
} }
/** Parse semver string into major, minor, and patch numbers
* @example
* const semVer = "1.2.3"
* const { major, minor, patch } = parseSemVer(semVer)
* console.log(major, minor, patch) // 1, 2, 3
*/
export const parseSemVer = (semVer = ""): SemVer => { export const parseSemVer = (semVer = ""): SemVer => {
// if (semVer.startsWith("v")) { // if (semVer.startsWith("v")) {
// semVer = semVer.slice(1) // semVer = semVer.slice(1)

View File

@@ -1,3 +1,15 @@
## 0.15.0
- Add initial S.M.A.R.T. support for disk health monitoring. (#962)
- Add `CONTAINER_DETAILS` environment variable to control access to container logs and info APIs. (#1305)
- Improve temperature chart by allowing y-axis to start above 0 for better readability. (#1307)
- Add `henrygd/beszel-agent:alpine` Docker image and include `smartmontools` in all non-base agent images.
- Improve battery detection logic. (#1287)
## 0.14.1 ## 0.14.1
- Add `MFA_OTP` environment variable to enable email-based one-time password for users and/or superusers. - Add `MFA_OTP` environment variable to enable email-based one-time password for users and/or superusers.