fix: nil slice -> empty slice in list API responses
Backend: - ListStorageJobs, ListSambaShares, ListNFSExports, ListUsers, ListDirty, ListApplyLog, ListWatchedMounts: initialize with make([]T, 0) instead of var x []T to avoid JSON null on empty. Fixes "Cannot read properties of null" crash on /storage. Frontend: - Storage.tsx: defensive setJobs(res.jobs ?? []) to guard against API returning null. Tests: - Add TestListStorageJobsEmptyReturnsSlice. Version: 0.7.1
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
BINARY=nasctl
|
BINARY=nasctl
|
||||||
VERSION?=0.7.0
|
VERSION?=0.7.1
|
||||||
GO?=go
|
GO?=go
|
||||||
LDFLAGS=-s -w -X github.com/darroyo/nasctl/internal/web.Version=$(VERSION) -X github.com/darroyo/nasctl/internal/web.Commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
LDFLAGS=-s -w -X github.com/darroyo/nasctl/internal/web.Version=$(VERSION) -X github.com/darroyo/nasctl/internal/web.Commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||||
BUILD_FLAGS=CGO_ENABLED=0
|
BUILD_FLAGS=CGO_ENABLED=0
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func (d *DB) ListDirty() ([]DirtyModule, error) {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var modules []DirtyModule
|
modules := make([]DirtyModule, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var module DirtyModule
|
var module DirtyModule
|
||||||
var markedAt string
|
var markedAt string
|
||||||
@@ -158,7 +158,7 @@ func (d *DB) ListApplyLog(limit int) ([]ApplyLogEntry, error) {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var entries []ApplyLogEntry
|
entries := make([]ApplyLogEntry, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var entry ApplyLogEntry
|
var entry ApplyLogEntry
|
||||||
var success int
|
var success int
|
||||||
@@ -182,7 +182,7 @@ func (d *DB) ListSambaShares() ([]SambaShare, error) {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var shares []SambaShare
|
shares := make([]SambaShare, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
share, err := scanSambaShare(rows)
|
share, err := scanSambaShare(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func (d *DB) ListNFSExports() ([]NFSExport, error) {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var exports []NFSExport
|
exports := make([]NFSExport, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
export, err := scanNFSExport(rows)
|
export, err := scanNFSExport(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ func (d *DB) ListStorageJobs(limit int) ([]StorageJob, error) {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var jobs []StorageJob
|
jobs := make([]StorageJob, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
j, err := scanStorageJob(rows)
|
j, err := scanStorageJob(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListStorageJobsEmptyReturnsSlice(t *testing.T) {
|
||||||
|
d, err := Open(":memory:")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("open db: %v", err)
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
if err := d.Migrate(); err != nil {
|
||||||
|
t.Fatalf("migrate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs, err := d.ListStorageJobs(50)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListStorageJobs: %v", err)
|
||||||
|
}
|
||||||
|
if jobs == nil {
|
||||||
|
t.Error("ListStorageJobs returned nil, want empty slice")
|
||||||
|
}
|
||||||
|
if len(jobs) != 0 {
|
||||||
|
t.Errorf("len(jobs) = %d, want 0", len(jobs))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ func (d *DB) ListUsers() ([]User, error) {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var users []User
|
users := make([]User, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
user, err := scanUser(rows)
|
user, err := scanUser(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (d *DB) ListWatchedMounts() ([]WatchedMount, error) {
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var mounts []WatchedMount
|
mounts := make([]WatchedMount, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
m, err := scanWatchedMount(rows)
|
m, err := scanWatchedMount(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default function Storage() {
|
|||||||
async function loadJobs() {
|
async function loadJobs() {
|
||||||
try {
|
try {
|
||||||
const res = await api.listStorageJobs(50);
|
const res = await api.listStorageJobs(50);
|
||||||
setJobs(res.jobs);
|
setJobs(res.jobs ?? []);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"root":["./src/app.tsx","./src/dirtycontext.tsx","./src/api.ts","./src/main.tsx","./src/components/dirtybanner.tsx","./src/components/filebrowsermodal.tsx","./src/components/layout.tsx","./src/components/modal.tsx","./src/components/pathfield.tsx","./src/pages/dashboard.tsx","./src/pages/files.tsx","./src/pages/log.tsx","./src/pages/login.tsx","./src/pages/nfs.tsx","./src/pages/samba.tsx","./src/pages/settings.tsx","./src/pages/storage.tsx","./src/pages/users.tsx"],"version":"5.9.3"}
|
||||||
Reference in New Issue
Block a user