taggart

Simple golang tagging filesystem webapp
Log | Files | Refs

commit 3c996227763eaf024263230cc506aa9be51c2736
parent bcc460f48ac8cd7d00e199484c8630bc6c6f4dae
Author: breadcat <breadcat@users.noreply.github.com>
Date:   Tue, 11 Nov 2025 15:39:26 +0000

Add support for DB backup and vacuum

Diffstat:
Mmain.go | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mtemplates/settings.html | 23+++++++++++++++++++++--
2 files changed, 149 insertions(+), 35 deletions(-)

diff --git a/main.go b/main.go @@ -16,6 +16,7 @@ import ( "path/filepath" "strconv" "strings" + "time" _ "github.com/mattn/go-sqlite3" ) @@ -1047,62 +1048,157 @@ func validateConfig(newConfig Config) error { } func settingsHandler(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodPost { - newConfig := Config{ - DatabasePath: strings.TrimSpace(r.FormValue("database_path")), - UploadDir: strings.TrimSpace(r.FormValue("upload_dir")), - ServerPort: strings.TrimSpace(r.FormValue("server_port")), - InstanceName: strings.TrimSpace(r.FormValue("instance_name")), - GallerySize: strings.TrimSpace(r.FormValue("gallery_size")), - ItemsPerPage: strings.TrimSpace(r.FormValue("items_per_page")), - } + switch r.Method { + case http.MethodPost: + action := r.FormValue("action") + + switch action { + case "save", "": // support both explicit and legacy save form + handleSaveSettings(w, r) + return - if err := validateConfig(newConfig); err != nil { + case "backup": + err := backupDatabase(config.DatabasePath) pageData := buildPageData("Settings", struct { - Config Config - Error string - }{config, err.Error()}) + Config Config + Error string + Success string + }{ + Config: config, + Error: errorString(err), + Success: successString(err, "Database backup created successfully!"), + }) renderTemplate(w, "settings.html", pageData) return - } - - needsRestart := (newConfig.DatabasePath != config.DatabasePath || - newConfig.ServerPort != config.ServerPort) - config = newConfig - if err := saveConfig(); err != nil { + case "vacuum": + err := vacuumDatabase(config.DatabasePath) pageData := buildPageData("Settings", struct { - Config Config - Error string - }{config, "Failed to save configuration: " + err.Error()}) + Config Config + Error string + Success string + }{ + Config: config, + Error: errorString(err), + Success: successString(err, "Database vacuum completed successfully!"), + }) renderTemplate(w, "settings.html", pageData) return } - var message string - if needsRestart { - message = "Settings saved successfully! Please restart the server for database/port changes to take effect." - } else { - message = "Settings saved successfully!" - } - + default: pageData := buildPageData("Settings", struct { Config Config Error string Success string - }{config, "", message}) + }{config, "", ""}) + renderTemplate(w, "settings.html", pageData) + } +} + +func handleSaveSettings(w http.ResponseWriter, r *http.Request) { + newConfig := Config{ + DatabasePath: strings.TrimSpace(r.FormValue("database_path")), + UploadDir: strings.TrimSpace(r.FormValue("upload_dir")), + ServerPort: strings.TrimSpace(r.FormValue("server_port")), + InstanceName: strings.TrimSpace(r.FormValue("instance_name")), + GallerySize: strings.TrimSpace(r.FormValue("gallery_size")), + ItemsPerPage: strings.TrimSpace(r.FormValue("items_per_page")), + } + + if err := validateConfig(newConfig); err != nil { + pageData := buildPageData("Settings", struct { + Config Config + Error string + }{config, err.Error()}) + renderTemplate(w, "settings.html", pageData) + return + } + + needsRestart := (newConfig.DatabasePath != config.DatabasePath || + newConfig.ServerPort != config.ServerPort) + + config = newConfig + if err := saveConfig(); err != nil { + pageData := buildPageData("Settings", struct { + Config Config + Error string + }{config, "Failed to save configuration: " + err.Error()}) renderTemplate(w, "settings.html", pageData) return } + var message string + if needsRestart { + message = "Settings saved successfully! Please restart the server for database/port changes to take effect." + } else { + message = "Settings saved successfully!" + } + pageData := buildPageData("Settings", struct { Config Config Error string Success string - }{config, "", ""}) + }{config, "", message}) renderTemplate(w, "settings.html", pageData) } +func errorString(err error) string { + if err != nil { + return err.Error() + } + return "" +} + +func successString(err error, msg string) string { + if err == nil { + return msg + } + return "" +} + +func backupDatabase(dbPath string) error { + if dbPath == "" { + return fmt.Errorf("database path not configured") + } + + timestamp := time.Now().Format("20060102_150405") + backupPath := fmt.Sprintf("%s_backup_%s.db", strings.TrimSuffix(dbPath, filepath.Ext(dbPath)), timestamp) + + input, err := os.Open(dbPath) + if err != nil { + return fmt.Errorf("failed to open database: %w", err) + } + defer input.Close() + + output, err := os.Create(backupPath) + if err != nil { + return fmt.Errorf("failed to create backup file: %w", err) + } + defer output.Close() + + if _, err := io.Copy(output, input); err != nil { + return fmt.Errorf("failed to copy database: %w", err) + } + + return nil +} + +func vacuumDatabase(dbPath string) error { + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return fmt.Errorf("failed to open database: %w", err) + } + defer db.Close() + + _, err = db.Exec("VACUUM;") + if err != nil { + return fmt.Errorf("VACUUM failed: %w", err) + } + + return nil +} + func ytdlpHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Redirect(w, r, "/upload", http.StatusSeeOther) @@ -1634,4 +1730,4 @@ func generateThumbnail(videoPath, uploadDir, filename string) error { } return nil -} -\ No newline at end of file +} diff --git a/templates/settings.html b/templates/settings.html @@ -82,4 +82,24 @@ <p>Settings are stored in <code>config.json</code> in the application directory.</p> </div> -{{template "_footer"}} -\ No newline at end of file +<hr style="margin: 40px 0;"> + +<h2>Database Maintenance</h2> + +<form method="post" style="margin-bottom: 20px;"> + <input type="hidden" name="action" value="backup"> + <button type="submit" style="background-color: #28a745; color: white; padding: 10px 20px; border: none; border-radius: 4px; font-size: 16px;"> + Backup Database + </button> + <small style="color: #666; margin-left: 10px;">Creates a timestamped backup of the database file</small> +</form> + +<form method="post"> + <input type="hidden" name="action" value="vacuum"> + <button type="submit" style="background-color: #6f42c1; color: white; padding: 10px 20px; border: none; border-radius: 4px; font-size: 16px;"> + Vacuum Database + </button> + <small style="color: #666; margin-left: 10px;">Reclaims unused space and optimizes database performance</small> +</form> + +{{template "_footer"}}