Files
gobsidian/internal/handlers/settings.go
2025-08-26 07:46:01 +01:00

282 lines
9.8 KiB
Go

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": []gin.H{
{"name": "/", "url": "/"},
{"name": "Settings", "url": ""},
},
"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})
}