272 lines
7.4 KiB
Go
272 lines
7.4 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
type Config struct {
|
|
// SERVER equivalent settings
|
|
Host string
|
|
Port int
|
|
SecretKey string
|
|
Debug bool
|
|
MaxContentLength int64 // in bytes
|
|
|
|
// MD Notes App settings
|
|
AppName string
|
|
NotesDir string
|
|
NotesDirHideSidepane []string
|
|
NotesDirSkip []string
|
|
ImagesHide bool
|
|
ImageStorageMode int
|
|
ImageStoragePath string
|
|
ImageSubfolderName string
|
|
AllowedImageExtensions []string
|
|
AllowedFileExtensions []string
|
|
|
|
// Visibility settings
|
|
ShowImagesInTree bool
|
|
ShowFilesInTree bool
|
|
ShowImagesInFolder bool
|
|
ShowFilesInFolder bool
|
|
}
|
|
|
|
var defaultConfig = map[string]map[string]string{
|
|
"SERVER": {
|
|
"HOST": "0.0.0.0",
|
|
"PORT": "3000",
|
|
"SECRET_KEY": "change-this-secret-key",
|
|
"DEBUG": "false",
|
|
"MAX_CONTENT_LENGTH": "16", // in MB
|
|
},
|
|
"MD_NOTES_APP": {
|
|
"APP_NAME": "Gobsidian",
|
|
"NOTES_DIR": "notes",
|
|
"NOTES_DIR_HIDE_SIDEPANE": "attached, images",
|
|
"NOTES_DIR_SKIP": "secret, private",
|
|
"IMAGES_HIDE": "false",
|
|
"IMAGE_STORAGE_MODE": "1",
|
|
"IMAGE_STORAGE_PATH": "images",
|
|
"IMAGE_SUBFOLDER_NAME": "attached",
|
|
"ALLOWED_IMAGE_EXTENSIONS": "jpg, jpeg, png, webp, gif",
|
|
"ALLOWED_FILE_EXTENSIONS": "txt, pdf, html, json, yaml, yml, conf, csv, cmd, bat, sh",
|
|
// Visibility defaults
|
|
"SHOW_IMAGES_IN_TREE": "false",
|
|
"SHOW_FILES_IN_TREE": "true",
|
|
"SHOW_IMAGES_IN_FOLDER": "true",
|
|
"SHOW_FILES_IN_FOLDER": "true",
|
|
},
|
|
}
|
|
|
|
func Load() (*Config, error) {
|
|
configPath := "settings.ini"
|
|
|
|
// Ensure config file exists
|
|
if err := ensureConfigFile(configPath); err != nil {
|
|
return nil, fmt.Errorf("failed to ensure config file: %w", err)
|
|
}
|
|
|
|
// Load configuration
|
|
cfg, err := ini.Load(configPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load config file: %w", err)
|
|
}
|
|
|
|
config := &Config{}
|
|
|
|
// Load SERVER section
|
|
SERVERSection := cfg.Section("SERVER")
|
|
config.Host = SERVERSection.Key("HOST").String()
|
|
config.Port, _ = SERVERSection.Key("PORT").Int()
|
|
config.SecretKey = SERVERSection.Key("SECRET_KEY").String()
|
|
config.Debug, _ = SERVERSection.Key("DEBUG").Bool()
|
|
maxContentMB, _ := SERVERSection.Key("MAX_CONTENT_LENGTH").Int()
|
|
config.MaxContentLength = int64(maxContentMB) * 1024 * 1024 // Convert MB to bytes
|
|
|
|
// Load MD_NOTES_APP section
|
|
notesSection := cfg.Section("MD_NOTES_APP")
|
|
config.AppName = notesSection.Key("APP_NAME").String()
|
|
config.NotesDir = notesSection.Key("NOTES_DIR").String()
|
|
|
|
// Parse comma-separated lists
|
|
config.NotesDirHideSidepane = parseCommaSeparated(notesSection.Key("NOTES_DIR_HIDE_SIDEPANE").String())
|
|
config.NotesDirSkip = parseCommaSeparated(notesSection.Key("NOTES_DIR_SKIP").String())
|
|
config.AllowedImageExtensions = parseCommaSeparated(notesSection.Key("ALLOWED_IMAGE_EXTENSIONS").String())
|
|
config.AllowedFileExtensions = parseCommaSeparated(notesSection.Key("ALLOWED_FILE_EXTENSIONS").String())
|
|
|
|
config.ImagesHide, _ = notesSection.Key("IMAGES_HIDE").Bool()
|
|
// New visibility flags (fallback to legacy IMAGES_HIDE for folder images if keys missing)
|
|
config.ShowImagesInTree, _ = notesSection.Key("SHOW_IMAGES_IN_TREE").Bool()
|
|
config.ShowFilesInTree, _ = notesSection.Key("SHOW_FILES_IN_TREE").Bool()
|
|
if key := notesSection.Key("SHOW_IMAGES_IN_FOLDER"); key.String() != "" {
|
|
config.ShowImagesInFolder, _ = key.Bool()
|
|
} else {
|
|
// fallback: if IMAGES_HIDE true => not shown
|
|
config.ShowImagesInFolder = !config.ImagesHide
|
|
}
|
|
if key := notesSection.Key("SHOW_FILES_IN_FOLDER"); key.String() != "" {
|
|
config.ShowFilesInFolder, _ = key.Bool()
|
|
} else {
|
|
config.ShowFilesInFolder = true
|
|
}
|
|
config.ImageStorageMode, _ = notesSection.Key("IMAGE_STORAGE_MODE").Int()
|
|
config.ImageStoragePath = notesSection.Key("IMAGE_STORAGE_PATH").String()
|
|
config.ImageSubfolderName = notesSection.Key("IMAGE_SUBFOLDER_NAME").String()
|
|
|
|
// Convert relative paths to absolute
|
|
if !filepath.IsAbs(config.NotesDir) {
|
|
wd, _ := os.Getwd()
|
|
config.NotesDir = filepath.Join(wd, config.NotesDir)
|
|
}
|
|
|
|
if !filepath.IsAbs(config.ImageStoragePath) && config.ImageStorageMode == 2 {
|
|
wd, _ := os.Getwd()
|
|
config.ImageStoragePath = filepath.Join(wd, config.ImageStoragePath)
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
func ensureConfigFile(configPath string) error {
|
|
// Check if file exists
|
|
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
return createDefaultConfigFile(configPath)
|
|
}
|
|
|
|
// File exists, check if it has all required settings
|
|
return updateConfigFile(configPath)
|
|
}
|
|
|
|
func createDefaultConfigFile(configPath string) error {
|
|
cfg := ini.Empty()
|
|
|
|
for sectionName, settings := range defaultConfig {
|
|
section, err := cfg.NewSection(sectionName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for key, value := range settings {
|
|
section.NewKey(key, value)
|
|
}
|
|
}
|
|
|
|
return cfg.SaveTo(configPath)
|
|
}
|
|
|
|
func updateConfigFile(configPath string) error {
|
|
cfg, err := ini.Load(configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
updated := false
|
|
|
|
for sectionName, settings := range defaultConfig {
|
|
section := cfg.Section(sectionName)
|
|
|
|
for key, defaultValue := range settings {
|
|
if !section.HasKey(key) {
|
|
section.NewKey(key, defaultValue)
|
|
updated = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if updated {
|
|
return cfg.SaveTo(configPath)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseCommaSeparated(value string) []string {
|
|
if value == "" {
|
|
return []string{}
|
|
}
|
|
|
|
parts := strings.Split(value, ",")
|
|
result := make([]string, 0, len(parts))
|
|
|
|
for _, part := range parts {
|
|
if trimmed := strings.TrimSpace(part); trimmed != "" {
|
|
result = append(result, trimmed)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (c *Config) SaveSetting(section, key, value string) error {
|
|
configPath := "settings.ini"
|
|
cfg, err := ini.Load(configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sec := cfg.Section(section)
|
|
sec.Key(key).SetValue(value)
|
|
|
|
// Update in-memory config based on section and key
|
|
switch section {
|
|
case "SERVER":
|
|
switch key {
|
|
case "HOST":
|
|
c.Host = value
|
|
case "PORT":
|
|
if port, err := strconv.Atoi(value); err == nil {
|
|
c.Port = port
|
|
}
|
|
case "SECRET_KEY":
|
|
c.SecretKey = value
|
|
case "DEBUG":
|
|
c.Debug = value == "true"
|
|
case "MAX_CONTENT_LENGTH":
|
|
if size, err := strconv.ParseInt(value, 10, 64); err == nil {
|
|
c.MaxContentLength = size * 1024 * 1024
|
|
}
|
|
}
|
|
case "MD_NOTES_APP":
|
|
switch key {
|
|
case "APP_NAME":
|
|
c.AppName = value
|
|
case "NOTES_DIR":
|
|
c.NotesDir = value
|
|
case "NOTES_DIR_HIDE_SIDEPANE":
|
|
c.NotesDirHideSidepane = parseCommaSeparated(value)
|
|
case "NOTES_DIR_SKIP":
|
|
c.NotesDirSkip = parseCommaSeparated(value)
|
|
case "IMAGES_HIDE":
|
|
c.ImagesHide = value == "true"
|
|
case "IMAGE_STORAGE_MODE":
|
|
if mode, err := strconv.Atoi(value); err == nil {
|
|
c.ImageStorageMode = mode
|
|
}
|
|
case "IMAGE_STORAGE_PATH":
|
|
c.ImageStoragePath = value
|
|
case "IMAGE_SUBFOLDER_NAME":
|
|
c.ImageSubfolderName = value
|
|
case "ALLOWED_IMAGE_EXTENSIONS":
|
|
c.AllowedImageExtensions = parseCommaSeparated(value)
|
|
case "ALLOWED_FILE_EXTENSIONS":
|
|
c.AllowedFileExtensions = parseCommaSeparated(value)
|
|
case "SHOW_IMAGES_IN_TREE":
|
|
c.ShowImagesInTree = value == "true"
|
|
case "SHOW_FILES_IN_TREE":
|
|
c.ShowFilesInTree = value == "true"
|
|
case "SHOW_IMAGES_IN_FOLDER":
|
|
c.ShowImagesInFolder = value == "true"
|
|
case "SHOW_FILES_IN_FOLDER":
|
|
c.ShowFilesInFolder = value == "true"
|
|
}
|
|
}
|
|
|
|
return cfg.SaveTo(configPath)
|
|
}
|