181 lines
4.1 KiB
Go
181 lines
4.1 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"os"
|
||
|
|
"sync"
|
||
|
|
)
|
||
|
|
|
||
|
|
// SettingsHandler manages system configuration
|
||
|
|
type SettingsHandler struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
settings map[string]interface{}
|
||
|
|
path string
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewSettingsHandler() *SettingsHandler {
|
||
|
|
h := &SettingsHandler{
|
||
|
|
settings: make(map[string]interface{}),
|
||
|
|
path: "/app/config/system.json",
|
||
|
|
}
|
||
|
|
h.load()
|
||
|
|
return h
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *SettingsHandler) load() {
|
||
|
|
data, err := os.ReadFile(h.path)
|
||
|
|
if err != nil {
|
||
|
|
// Use defaults
|
||
|
|
h.settings = h.defaultSettings()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
json.Unmarshal(data, &h.settings)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *SettingsHandler) save() error {
|
||
|
|
data, err := json.MarshalIndent(h.settings, "", " ")
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return os.WriteFile(h.path, data, 0644)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *SettingsHandler) defaultSettings() map[string]interface{} {
|
||
|
|
return map[string]interface{}{
|
||
|
|
"appearance": map[string]interface{}{
|
||
|
|
"theme": "light",
|
||
|
|
"density": "comfortable",
|
||
|
|
"sidebar_width": 240,
|
||
|
|
"animations": true,
|
||
|
|
},
|
||
|
|
"dashboard": map[string]interface{}{
|
||
|
|
"greeting_enabled": true,
|
||
|
|
"kpi_refresh": 300,
|
||
|
|
"activity_max": 10,
|
||
|
|
},
|
||
|
|
"notifications": map[string]interface{}{
|
||
|
|
"in_app": true,
|
||
|
|
"email": false,
|
||
|
|
"slack": false,
|
||
|
|
},
|
||
|
|
"advanced": map[string]interface{}{
|
||
|
|
"api_rate_limit": 1000,
|
||
|
|
"cache_ttl": 300,
|
||
|
|
"log_level": "info",
|
||
|
|
"export_max_rows": 10000,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetSettings returns all settings
|
||
|
|
func (h *SettingsHandler) GetSettings(w http.ResponseWriter, r *http.Request) {
|
||
|
|
h.mu.RLock()
|
||
|
|
defer h.mu.RUnlock()
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
json.NewEncoder(w).Encode(h.settings)
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateSettings updates settings
|
||
|
|
func (h *SettingsHandler) UpdateSettings(w http.ResponseWriter, r *http.Request) {
|
||
|
|
var updates map[string]interface{}
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
|
||
|
|
writeError(w, http.StatusBadRequest, "invalid request body")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
h.mu.Lock()
|
||
|
|
defer h.mu.Unlock()
|
||
|
|
|
||
|
|
// Merge updates
|
||
|
|
for key, value := range updates {
|
||
|
|
h.settings[key] = value
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.save(); err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to save settings")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
json.NewEncoder(w).Encode(h.settings)
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetModuleConfig returns module configuration
|
||
|
|
func (h *SettingsHandler) GetModuleConfig(w http.ResponseWriter, r *http.Request) {
|
||
|
|
module := r.URL.Query().Get("module")
|
||
|
|
if module == "" {
|
||
|
|
writeError(w, http.StatusBadRequest, "module required")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
h.mu.RLock()
|
||
|
|
defer h.mu.RUnlock()
|
||
|
|
|
||
|
|
modules, ok := h.settings["modules"].(map[string]interface{})
|
||
|
|
if !ok {
|
||
|
|
writeError(w, http.StatusNotFound, "modules not configured")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
config, ok := modules[module]
|
||
|
|
if !ok {
|
||
|
|
writeError(w, http.StatusNotFound, "module not found")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
json.NewEncoder(w).Encode(config)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ToggleModule enables/disables a module
|
||
|
|
func (h *SettingsHandler) ToggleModule(w http.ResponseWriter, r *http.Request) {
|
||
|
|
module := r.URL.Query().Get("module")
|
||
|
|
if module == "" {
|
||
|
|
writeError(w, http.StatusBadRequest, "module required")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
h.mu.Lock()
|
||
|
|
defer h.mu.Unlock()
|
||
|
|
|
||
|
|
modules, ok := h.settings["modules"].(map[string]interface{})
|
||
|
|
if !ok {
|
||
|
|
modules = make(map[string]interface{})
|
||
|
|
h.settings["modules"] = modules
|
||
|
|
}
|
||
|
|
|
||
|
|
config, ok := modules[module].(map[string]interface{})
|
||
|
|
if !ok {
|
||
|
|
config = map[string]interface{}{"enabled": false}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Toggle enabled state
|
||
|
|
if enabled, ok := config["enabled"].(bool); ok {
|
||
|
|
config["enabled"] = !enabled
|
||
|
|
} else {
|
||
|
|
config["enabled"] = true
|
||
|
|
}
|
||
|
|
modules[module] = config
|
||
|
|
|
||
|
|
if err := h.save(); err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to save")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
json.NewEncoder(w).Encode(config)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *SettingsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||
|
|
switch r.Method {
|
||
|
|
case http.MethodGet:
|
||
|
|
h.GetSettings(w, r)
|
||
|
|
case http.MethodPut:
|
||
|
|
h.UpdateSettings(w, r)
|
||
|
|
default:
|
||
|
|
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
|
||
|
|
}
|
||
|
|
}
|