From d55206af820628ed7c30a8a7afb6cbd2e70e0024 Mon Sep 17 00:00:00 2001 From: "Othman H. Suseno" Date: Thu, 18 Dec 2025 12:33:08 +0700 Subject: [PATCH] still working on the UI error --- internal/httpapp/api_handlers.go | 26 ++++++++++++++++++-------- internal/zfs/service.go | 24 +++++++++++++++++++++++- web/templates/storage.html | 18 +++++++++++++++--- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/internal/httpapp/api_handlers.go b/internal/httpapp/api_handlers.go index 6649d2b..b1125c3 100644 --- a/internal/httpapp/api_handlers.go +++ b/internal/httpapp/api_handlers.go @@ -76,19 +76,29 @@ func (a *App) handleCreatePool(w http.ResponseWriter, r *http.Request) { req.Options = make(map[string]string) } - if err := a.zfs.CreatePool(req.Name, req.VDEVs, req.Options); err != nil { + err := a.zfs.CreatePool(req.Name, req.VDEVs, req.Options) + + // Always check if pool exists, even if creation reported an error + // Sometimes pool is created despite errors (e.g., mountpoint issues) + pool, getErr := a.zfs.GetPool(req.Name) + if getErr == nil { + // Pool exists - return success even if creation reported an error + if err != nil { + log.Printf("create pool reported error but pool exists: %v", err) + } + writeJSON(w, http.StatusCreated, pool) + return + } + + // Pool doesn't exist - return the error + if err != nil { log.Printf("create pool error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } - pool, err := a.zfs.GetPool(req.Name) - if err != nil { - writeJSON(w, http.StatusCreated, map[string]string{"message": "pool created", "name": req.Name}) - return - } - - writeJSON(w, http.StatusCreated, pool) + // No error but pool doesn't exist (shouldn't happen, but handle it) + writeJSON(w, http.StatusCreated, map[string]string{"message": "pool created", "name": req.Name}) } func (a *App) handleGetPool(w http.ResponseWriter, r *http.Request) { diff --git a/internal/zfs/service.go b/internal/zfs/service.go index 28722c2..7b7d44e 100644 --- a/internal/zfs/service.go +++ b/internal/zfs/service.go @@ -185,6 +185,10 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str options = make(map[string]string) } + // Add -f flag to force creation even if devices have existing filesystems + // This handles cases where devices are "in use" or contain "unknown filesystem" + args = append(args, "-f") + // If mountpoint is not explicitly set, use dedicated storage directory mountpoint := options["mountpoint"] if mountpoint == "" { @@ -218,8 +222,26 @@ func (s *Service) CreatePool(name string, vdevs []string, options map[string]str // Create the pool _, err := s.execCommand(s.zpoolPath, args...) + + // Even if creation reports an error, check if pool actually exists + // Sometimes ZFS reports errors but pool is still created if err != nil { - return err + // Check if pool exists despite the error + if existingPools, listErr := s.ListPools(); listErr == nil { + for _, pool := range existingPools { + if pool.Name == name { + // Pool exists! Log the original error but don't fail + log.Printf("warning: pool creation reported error but pool %s exists: %v", name, err) + err = nil // Clear error since pool was created + break + } + } + } + + // If pool doesn't exist and we still have an error, return it + if err != nil { + return err + } } // Pool created successfully - now try to set mountpoint and mount if needed diff --git a/web/templates/storage.html b/web/templates/storage.html index 1332446..54e6514 100644 --- a/web/templates/storage.html +++ b/web/templates/storage.html @@ -516,16 +516,28 @@ async function createPool(e) { }) }); + const data = await res.json().catch(() => null); + if (res.ok) { closeModal('create-pool-modal'); e.target.reset(); - loadPools(); + // Wait a moment for pool to be fully created, then refresh + await new Promise(resolve => setTimeout(resolve, 500)); + await loadPools(); alert('Pool created successfully'); } else { - const err = await res.json(); - alert(`Error: ${err.error || 'Failed to create pool'}`); + // Even if error, check if pool was actually created + // Sometimes creation reports error but pool exists + await new Promise(resolve => setTimeout(resolve, 1000)); + await loadPools(); + + const err = await res.json().catch(() => ({ error: 'Failed to create pool' })); + alert(`Error: ${err.error || 'Failed to create pool'}\n\nNote: Please check if the pool was created despite the error.`); } } catch (err) { + // On network error, still try to refresh to see if pool was created + await new Promise(resolve => setTimeout(resolve, 1000)); + await loadPools(); alert(`Error: ${err.message}`); } }