From 4067f9b793ee198e42112b26b4b4bb1382a31379 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Thu, 4 Dec 2025 12:35:17 -0500 Subject: [PATCH] Add DB management in settings and re-enable TPDB server path --- cmd/goondex/main.go | 2 +- internal/web/server.go | 44 ++++++++++++++++++++++- internal/web/templates/settings.html | 52 ++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/cmd/goondex/main.go b/cmd/goondex/main.go index f9c54ac..fe01ffc 100644 --- a/cmd/goondex/main.go +++ b/cmd/goondex/main.go @@ -480,7 +480,7 @@ var webCmd = &cobra.Command{ } defer database.Close() - server, err := web.NewServer(database, addr) + server, err := web.NewServer(database, addr, dbPath) if err != nil { return fmt.Errorf("failed to create web server: %w", err) } diff --git a/internal/web/server.go b/internal/web/server.go index cc4ec19..0723985 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -9,6 +9,7 @@ import ( "io/fs" "log" "net/http" + "os" "strconv" "strings" "time" @@ -33,9 +34,10 @@ type Server struct { db *db.DB templates *template.Template addr string + dbPath string } -func NewServer(database *db.DB, addr string) (*Server, error) { +func NewServer(database *db.DB, addr string, dbPath string) (*Server, error) { tmpl, err := template.ParseFS(content, "templates/*.html") if err != nil { return nil, fmt.Errorf("failed to parse templates: %w", err) @@ -45,6 +47,7 @@ func NewServer(database *db.DB, addr string) (*Server, error) { db: database, templates: tmpl, addr: addr, + dbPath: dbPath, }, nil } @@ -91,6 +94,7 @@ func (s *Server) Start() error { // Settings endpoints mux.HandleFunc("/api/settings/api-keys", s.handleAPISettingsKeys) + mux.HandleFunc("/api/settings/database", s.handleAPIDatabase) // API mux.HandleFunc("/api/import/performer", s.handleAPIImportPerformer) @@ -1369,6 +1373,7 @@ func (s *Server) handleSettingsPage(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ "PageTitle": "Settings", "ActivePage": "settings", + "DBPath": s.dbPath, } s.render(w, "settings.html", data) @@ -1427,3 +1432,40 @@ func (s *Server) handleAPISettingsKeys(w http.ResponseWriter, r *http.Request) { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } + +// Database management +func (s *Server) handleAPIDatabase(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + info := map[string]interface{}{ + "path": s.dbPath, + } + if stat, err := os.Stat(s.dbPath); err == nil { + info["size_bytes"] = stat.Size() + info["size_mb"] = float64(stat.Size()) / (1024 * 1024) + } + json.NewEncoder(w).Encode(APIResponse{ + Success: true, + Message: "OK", + Data: info, + }) + case http.MethodDelete: + // Close and recreate + if s.db != nil { + _ = s.db.Close() + } + _ = os.Remove(s.dbPath) + newDB, err := db.Open(s.dbPath) + if err != nil { + json.NewEncoder(w).Encode(APIResponse{Success: false, Message: fmt.Sprintf("Failed to recreate DB: %v", err)}) + return + } + s.db = newDB + json.NewEncoder(w).Encode(APIResponse{ + Success: true, + Message: "Database deleted and recreated.", + }) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} diff --git a/internal/web/templates/settings.html b/internal/web/templates/settings.html index 01b5686..e73ab49 100644 --- a/internal/web/templates/settings.html +++ b/internal/web/templates/settings.html @@ -34,6 +34,16 @@
+ +
+

Database Maintenance

+

Current database: {{.DBPath}}

+
+ + +
+
+
{{template "html-scripts" .}} @@ -90,6 +100,48 @@ el.style.display = msg ? 'block' : 'none'; } + async function loadDbInfo() { + try { + const res = await fetch('/api/settings/database'); + const result = await res.json(); + if (result.success && result.data) { + const d = result.data; + const el = document.getElementById('db-info'); + el.textContent = `Path: ${d.path || ''} | Size: ${ (d.size_mb || 0).toFixed ? (d.size_mb.toFixed(2) + ' MB') : 'n/a'}`; + el.classList.remove('error'); + el.style.display = 'block'; + } + } catch (err) { + const el = document.getElementById('db-info'); + el.textContent = 'Error loading DB info: ' + err.message; + el.classList.add('error'); + el.style.display = 'block'; + } + } + + async function confirmDeleteDb() { + if (!confirm('This will DELETE the database file and recreate an empty one. Continue?')) return; + try { + const res = await fetch('/api/settings/database', { method: 'DELETE' }); + const result = await res.json(); + const el = document.getElementById('db-info'); + if (result.success) { + el.textContent = result.message; + el.classList.remove('error'); + el.style.display = 'block'; + } else { + el.textContent = result.message || 'Failed to delete DB'; + el.classList.add('error'); + el.style.display = 'block'; + } + } catch (err) { + const el = document.getElementById('db-info'); + el.textContent = 'Error deleting DB: ' + err.message; + el.classList.add('error'); + el.style.display = 'block'; + } + } + document.addEventListener('DOMContentLoaded', loadApiKeys);