diff --git a/src/config/config.go b/src/config/config.go index 283427ea..0849a235 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -286,10 +286,9 @@ func (k *KeyBytes) UnmarshalJSON(b []byte) error { // ConfigInfo contains information about the configuration file type ConfigInfo struct { - Path string `json:"path"` - Format string `json:"format"` - Data interface{} `json:"data"` - Writable bool `json:"writable"` + Path string `json:"path"` + Format string `json:"format"` + Data interface{} `json:"data"` } // Global variables to track the current configuration state @@ -338,20 +337,6 @@ func validateConfigPath(path string) (string, error) { } } - // Basic sanity check on file extension for config files - ext := strings.ToLower(filepath.Ext(absPath)) - allowedExts := []string{".json", ".hjson", ".conf", ".config", ".yml", ".yaml", ""} - validExt := false - for _, allowed := range allowedExts { - if ext == allowed { - validExt = true - break - } - } - if !validExt { - return "", fmt.Errorf("invalid file extension: %s", ext) - } - // Additional check: ensure the path doesn't escape intended directories if strings.Count(absPath, "/") > 10 { return "", fmt.Errorf("path too deep: potential security risk") @@ -381,7 +366,6 @@ func GetCurrentConfig() (*ConfigInfo, error) { var configPath string var configData *NodeConfig var format string = "hjson" - var writable bool = false // Use current config if available, otherwise try to read from default location if currentConfigPath != "" && currentConfigData != nil { @@ -402,72 +386,33 @@ func GetCurrentConfig() (*ConfigInfo, error) { if err != nil { return nil, fmt.Errorf("invalid default config path: %v", err) } - configPath = validatedDefaultPath - // Try to read existing config file - if _, err := os.Stat(configPath); err == nil { // Path already validated above - data, err := os.ReadFile(configPath) // Path already validated above - if err == nil { - cfg := GenerateConfig() - if err := hjson.Unmarshal(data, cfg); err == nil { - configData = cfg - // Detect format - var jsonTest interface{} - if json.Unmarshal(data, &jsonTest) == nil { - format = "json" - } - } else { - return nil, fmt.Errorf("failed to parse config file: %v", err) - } - } - } else { - // No config file exists, use default - configData = GenerateConfig() - } + configPath = validatedDefaultPath + configData = GenerateConfig() } - // Detect format from file if path is known - if configPath != "" { - // Config path is already validated at this point - if _, err := os.Stat(configPath); err == nil { // Path already validated above - data, err := os.ReadFile(configPath) // Path already validated above - if err == nil { + // Try to read existing config file + if _, err := os.Stat(configPath); err == nil { // Path already validated above + data, err := os.ReadFile(configPath) // Path already validated above + if err == nil { + cfg := GenerateConfig() + if err := hjson.Unmarshal(data, cfg); err == nil { + configData = cfg + // Detect format var jsonTest interface{} if json.Unmarshal(data, &jsonTest) == nil { format = "json" } - } - } - } - - // Check if writable - if configPath != "" { - // Config path is already validated at this point - if _, err := os.Stat(configPath); err == nil { // Path already validated above - // File exists, check if writable - if file, err := os.OpenFile(configPath, os.O_WRONLY, 0); err == nil { // Path already validated above - writable = true - file.Close() - } - } else { - // File doesn't exist, check if directory is writable - dir := filepath.Clean(filepath.Dir(configPath)) - if stat, err := os.Stat(dir); err == nil && stat.IsDir() { - testFile := filepath.Join(dir, ".yggdrasil_write_test") - if file, err := os.Create(testFile); err == nil { - file.Close() - os.Remove(testFile) - writable = true - } + } else { + return nil, fmt.Errorf("failed to parse config file: %v", err) } } } return &ConfigInfo{ - Path: configPath, - Format: format, - Data: configData, - Writable: writable, + Path: configPath, + Format: format, + Data: configData, }, nil } @@ -516,6 +461,7 @@ func SaveConfig(configData interface{}, configPath, format string) error { } } } + if targetFormat == "" { targetFormat = "hjson" } diff --git a/src/webui/server.go b/src/webui/server.go index 6457ec10..7a5d1a7a 100644 --- a/src/webui/server.go +++ b/src/webui/server.go @@ -388,7 +388,6 @@ type ConfigResponse struct { ConfigPath string `json:"config_path"` ConfigFormat string `json:"config_format"` ConfigJSON string `json:"config_json"` - IsWritable bool `json:"is_writable"` } type ConfigSetRequest struct { @@ -432,7 +431,6 @@ func (w *WebUIServer) getConfigHandler(rw http.ResponseWriter, r *http.Request) ConfigPath: configInfo.Path, ConfigFormat: configInfo.Format, ConfigJSON: string(configBytes), - IsWritable: configInfo.Writable, } rw.Header().Set("Content-Type", "application/json") diff --git a/src/webui/static/config.js b/src/webui/static/config.js index 9183037d..9540ceff 100644 --- a/src/webui/static/config.js +++ b/src/webui/static/config.js @@ -30,8 +30,7 @@ async function loadConfiguration() { currentConfigJSON = data.config_json; configMeta = { path: data.config_path, - format: data.config_format, - isWritable: data.is_writable + format: data.config_format }; renderConfigEditor(); @@ -54,9 +53,6 @@ function renderConfigEditor() {
${configMeta.path} ${configMeta.format.toUpperCase()} - - ${configMeta.isWritable ? '✏️ Редактируемый' : '🔒 Только чтение'} -
@@ -76,14 +72,12 @@ function renderConfigEditor() {
Проверить
- ${configMeta.isWritable ? ` -
- Сохранить -
-
- Сохранить и перезапустить -
- ` : ''} +
+ Сохранить +
+
+ Сохранить и перезапустить +
@@ -93,7 +87,6 @@ function renderConfigEditor() { id="config-json-textarea" class="json-editor" spellcheck="false" - ${configMeta.isWritable ? '' : 'readonly'} placeholder="Загрузка конфигурации..." oninput="onConfigChange()" onscroll="syncLineNumbers()" diff --git a/src/webui/static/style.css b/src/webui/static/style.css index 001d6bd1..5db33daa 100644 --- a/src/webui/static/style.css +++ b/src/webui/static/style.css @@ -1542,9 +1542,6 @@ button[onclick="copyNodeKey()"]:hover { } .config-header { - display: flex; - justify-content: space-between; - align-items: flex-start; margin-bottom: 30px; padding: 20px; background: var(--bg-info-card); @@ -1564,13 +1561,6 @@ button[onclick="copyNodeKey()"]:hover { font-size: 1.5em; } -.config-meta { - display: flex; - gap: 15px; - flex-wrap: wrap; - align-items: center; -} - .config-path { font-family: 'Courier New', monospace; background: var(--bg-nav-item); @@ -1602,25 +1592,6 @@ button[onclick="copyNodeKey()"]:hover { color: #7b1fa2; } -.config-status { - padding: 4px 8px; - border-radius: 4px; - font-size: 0.8em; - font-weight: bold; -} - -.config-status.writable { - background: var(--bg-success); - color: var(--text-success); -} - -.config-status.readonly { - background: var(--bg-warning); - color: var(--text-warning); -} - - - /* Configuration Groups */ .config-groups { display: flex;