package handlers import ( "net/http" "path/filepath" "strconv" "strings" "github.com/gin-gonic/gin" "gobsidian/internal/utils" ) func (h *Handlers) SettingsPageHandler(c *gin.Context) { notesTree, err := utils.BuildTreeStructure(h.config.NotesDir, h.config.NotesDirHideSidepane, h.config) if err != nil { c.HTML(http.StatusInternalServerError, "error", gin.H{ "error": "Failed to build tree structure", "app_name": h.config.AppName, "message": err.Error(), "ContentTemplate": "error_content", "ScriptsTemplate": "error_scripts", "Page": "error", }) return } c.HTML(http.StatusOK, "settings", gin.H{ "app_name": h.config.AppName, "notes_tree": notesTree, "active_path": []string{}, "current_note": nil, "breadcrumbs": utils.GenerateBreadcrumbs(""), "Authenticated": isAuthenticated(c), "IsAdmin": isAdmin(c), "ContentTemplate": "settings_content", "ScriptsTemplate": "settings_scripts", "Page": "settings", }) } func (h *Handlers) GetImageStorageSettingsHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "mode": h.config.ImageStorageMode, "path": h.config.ImageStoragePath, "subfolder": h.config.ImageSubfolderName, }) } func (h *Handlers) PostImageStorageSettingsHandler(c *gin.Context) { modeStr := c.PostForm("storage_mode") path := strings.TrimSpace(c.PostForm("storage_path")) subfolder := strings.TrimSpace(c.PostForm("subfolder_name")) mode, err := strconv.Atoi(modeStr) if err != nil || mode < 1 || mode > 4 { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid storage mode"}) return } // Validate path for mode 2 if mode == 2 && path == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Storage path is required for mode 2"}) return } // Validate subfolder name for mode 4 if mode == 4 && subfolder == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Subfolder name is required for mode 4"}) return } // Save settings if err := h.config.SaveSetting("MD_NOTES_APP", "IMAGE_STORAGE_MODE", modeStr); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save storage mode"}) return } if mode == 2 { if err := h.config.SaveSetting("MD_NOTES_APP", "IMAGE_STORAGE_PATH", path); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save storage path"}) return } } if mode == 4 { if err := h.config.SaveSetting("MD_NOTES_APP", "IMAGE_SUBFOLDER_NAME", subfolder); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save subfolder name"}) return } } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Image storage settings updated successfully", "reload_required": true, }) } func (h *Handlers) GetNotesDirSettingsHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "notes_dir": h.config.NotesDir, }) } func (h *Handlers) PostNotesDirSettingsHandler(c *gin.Context) { newDir := strings.TrimSpace(c.PostForm("notes_dir")) if newDir == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Notes directory is required"}) return } // Convert to absolute path if !filepath.IsAbs(newDir) { c.JSON(http.StatusBadRequest, gin.H{"error": "Please provide an absolute path"}) return } // Ensure directory exists if err := utils.EnsureDir(newDir); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create directory: " + err.Error()}) return } // Save setting if err := h.config.SaveSetting("MD_NOTES_APP", "NOTES_DIR", newDir); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save notes directory"}) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "Notes directory updated successfully", "reload_required": true, }) } func (h *Handlers) GetFileExtensionsSettingsHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "allowed_image_extensions": strings.Join(h.config.AllowedImageExtensions, ", "), "allowed_file_extensions": strings.Join(h.config.AllowedFileExtensions, ", "), "images_hide": h.config.ImagesHide, "show_images_in_tree": h.config.ShowImagesInTree, "show_files_in_tree": h.config.ShowFilesInTree, "show_images_in_folder": h.config.ShowImagesInFolder, "show_files_in_folder": h.config.ShowFilesInFolder, }) } func (h *Handlers) PostFileExtensionsSettingsHandler(c *gin.Context) { imageExtensions := strings.TrimSpace(c.PostForm("allowed_image_extensions")) fileExtensions := strings.TrimSpace(c.PostForm("allowed_file_extensions")) imagesHide := c.PostForm("images_hide") == "true" || c.PostForm("images_hide") == "on" showImagesInTree := c.PostForm("show_images_in_tree") == "true" || c.PostForm("show_images_in_tree") == "on" showFilesInTree := c.PostForm("show_files_in_tree") == "true" || c.PostForm("show_files_in_tree") == "on" showImagesInFolder := c.PostForm("show_images_in_folder") == "true" || c.PostForm("show_images_in_folder") == "on" showFilesInFolder := c.PostForm("show_files_in_folder") == "true" || c.PostForm("show_files_in_folder") == "on" // Save settings if err := h.config.SaveSetting("MD_NOTES_APP", "ALLOWED_IMAGE_EXTENSIONS", imageExtensions); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image extensions"}) return } if err := h.config.SaveSetting("MD_NOTES_APP", "ALLOWED_FILE_EXTENSIONS", fileExtensions); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file extensions"}) return } imagesHideStr := "false" if imagesHide { imagesHideStr = "true" } if err := h.config.SaveSetting("MD_NOTES_APP", "IMAGES_HIDE", imagesHideStr); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save images hide setting"}) return } if err := h.config.SaveSetting("MD_NOTES_APP", "SHOW_IMAGES_IN_TREE", boolToStr(showImagesInTree)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save SHOW_IMAGES_IN_TREE"}) return } if err := h.config.SaveSetting("MD_NOTES_APP", "SHOW_FILES_IN_TREE", boolToStr(showFilesInTree)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save SHOW_FILES_IN_TREE"}) return } if err := h.config.SaveSetting("MD_NOTES_APP", "SHOW_IMAGES_IN_FOLDER", boolToStr(showImagesInFolder)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save SHOW_IMAGES_IN_FOLDER"}) return } if err := h.config.SaveSetting("MD_NOTES_APP", "SHOW_FILES_IN_FOLDER", boolToStr(showFilesInFolder)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save SHOW_FILES_IN_FOLDER"}) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "File extension settings updated successfully", "reload_required": true, }) } func boolToStr(b bool) string { if b { return "true" } return "false" } // --- Security (IP Ban & Thresholds) Settings --- // GetSecuritySettingsHandler returns current security-related config func (h *Handlers) GetSecuritySettingsHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "pwd_failures_threshold": h.config.PwdFailuresThreshold, "mfa_failures_threshold": h.config.MFAFailuresThreshold, "failures_window_minutes": h.config.FailuresWindowMinutes, "auto_ban_duration_hours": h.config.AutoBanDurationHours, "auto_ban_permanent": h.config.AutoBanPermanent, }) } // PostSecuritySettingsHandler validates and saves security-related config func (h *Handlers) PostSecuritySettingsHandler(c *gin.Context) { pwdStr := strings.TrimSpace(c.PostForm("pwd_failures_threshold")) mfaStr := strings.TrimSpace(c.PostForm("mfa_failures_threshold")) winStr := strings.TrimSpace(c.PostForm("failures_window_minutes")) durStr := strings.TrimSpace(c.PostForm("auto_ban_duration_hours")) permStr := strings.TrimSpace(c.PostForm("auto_ban_permanent")) // basic validation if pwdStr == "" || mfaStr == "" || winStr == "" || durStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "All numeric fields are required"}) return } if _, err := strconv.Atoi(pwdStr); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid password failures threshold"}) return } if _, err := strconv.Atoi(mfaStr); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid MFA failures threshold"}) return } if _, err := strconv.Atoi(winStr); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid failures window (minutes)"}) return } if _, err := strconv.Atoi(durStr); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid auto-ban duration (hours)"}) return } // normalize perm perm := strings.EqualFold(permStr, "true") || permStr == "1" || strings.EqualFold(permStr, "on") permStr = boolToStr(perm) // Save values if err := h.config.SaveSetting("SECURITY", "PWD_FAILURES_THRESHOLD", pwdStr); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save PWD_FAILURES_THRESHOLD"}) return } if err := h.config.SaveSetting("SECURITY", "MFA_FAILURES_THRESHOLD", mfaStr); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save MFA_FAILURES_THRESHOLD"}) return } if err := h.config.SaveSetting("SECURITY", "FAILURES_WINDOW_MINUTES", winStr); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save FAILURES_WINDOW_MINUTES"}) return } if err := h.config.SaveSetting("SECURITY", "AUTO_BAN_DURATION_HOURS", durStr); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save AUTO_BAN_DURATION_HOURS"}) return } if err := h.config.SaveSetting("SECURITY", "AUTO_BAN_PERMANENT", permStr); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save AUTO_BAN_PERMANENT"}) return } c.JSON(http.StatusOK, gin.H{"success": true}) }