From 382f5603a8219289e7351b703e972f0011e296b9 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Fri, 31 Jan 2025 11:00:03 +0000 Subject: [PATCH] Config rework (#2823) # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: a --- .github/release.yml | 6 - build.gradle | 3 - gradle/verification-keyring.gpg | Bin 256469 -> 260304 bytes gradle/verification-keyring.keys | 109 ++++++++ gradle/verification-metadata.xml | 145 +++++----- .../SPDF/config/ConfigInitializer.java | 247 +++++++++++------- .../software/SPDF/utils/GeneralUtils.java | 233 ++++++++++++++--- 7 files changed, 544 insertions(+), 199 deletions(-) diff --git a/.github/release.yml b/.github/release.yml index 361e7d708..8bdb83be5 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,10 +1,4 @@ changelog: - exclude: - labels: - - Documentation - - Test - - Github - categories: - title: Bug Fixes labels: diff --git a/build.gradle b/build.gradle index de4f68b35..297b1fa55 100644 --- a/build.gradle +++ b/build.gradle @@ -293,9 +293,6 @@ dependencies { implementation("io.github.pixee:java-security-toolkit:1.2.1") - // implementation "org.yaml:snakeyaml:2.2" - implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4' - // Exclude Tomcat and include Jetty implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion") implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion" diff --git a/gradle/verification-keyring.gpg b/gradle/verification-keyring.gpg index 9f8b865e69ff0240c692cc3cf14945fea2cd2a36..b3945d07dbed794efcf7fbef413c188c8cce15c7 100644 GIT binary patch delta 3905 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 delta 29 ncmV+&0OJ49@ekG053s&^vnPJoueLJ*XzPdhBm%ejBm>ve^j;6M diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys index 402dccc22..0e43f9266 100644 --- a/gradle/verification-keyring.keys +++ b/gradle/verification-keyring.keys @@ -865,6 +865,34 @@ FaSYF1lb13TNIRT1q1My =MTP+ -----END PGP PUBLIC KEY BLOCK----- +pub A6144824624A3CBA +uid Leangen (Artifacts) + +sub 95E9D9B03269AB9F +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFf5E+EBCACtP5tSlTkLEBjJuzSC+eVGpSCusCZ4Nqvqui+uJf+yN64ZOJET +DbNgJlJTEmwkUFNdGWOg1hnhpBxjBKzAGcCf8YxmW2MJQt88C+DczgYcs3Jhn0GM +0RcvxfdUHLdyVTxCM4iDGOPu8v+Rr9gsUW//rps+vQRLrBmSqIo4rnSUOhJPy1Gn +ma+hZcG95rDiQ3bCbkEYbtqW/fPes8u8MyaUxxeUpPufLrLVMqNCYrO/FeOubfo+ +JypWmuq9XTJQYXEGjXJ7t158LrHGaFtrTnt7CvrpuZGqKyjtjeUXQZ5Qu52hINKD +eCZgyb7jmNd+MpP/a8YfkX/Mmr6GBgXwojXZABEBAAG0JUxlYW5nZW4gKEFydGlm +YWN0cykgPGluZm9AbGVhbmdlbi5pbz65AQ0EV/kT4QEIAMLQGyvUrPO/NGPRmNvE +VfvIk2bpkn7BAPS9aEycLShW8Jvf8I0S8vF/O6tIPeNUz+N6ZANpM+plCUHhJoCg +96fb2jpt/DFa6NNchnXAcIe8q32blBVStVM0z9A4JPcHOWxVrn4SNQMQUdIbt6ml +P0O2P3f5YERpVjY813Awv997dJUJ0UpjpgnpBwOOVvcWaccYmP2eSuFk5NgmjHS+ +47V1FjTel/lBBXRfk/36GaU8POqlac/lKFFq2Wri2S72eWFV2pzhDEaeEUo5Pe1Y +anHVbpbXO4JVz4YduwXse183r2B+8b5L6Et9xImzmqMPLKvnnEHB6nBl4K0ggIow +QRcAEQEAAYkBHwQYAQIACQUCV/kT4QIbDAAKCRCmFEgkYko8uvpEB/9thgmJAN9K +LALzQHPCh0rsX6MfOGvnomaSBDkp6BNQGLqIrggeU80Gpg5lFwOEfIFYgP24nHoj +PnfqUZ3YaoJm27p49D5m0RpC6fN9OCM+i6kdEL2AfjeBFG3D/1JW97bs9D4Rcdgq +AIt125Abqxt/hHNFXqHklB+s82MgT4R3RlAhajOQCoKv8edvffftzdGp2x0DW+Ho +kyxnEyRMk/6nad5QiUyWEwbxi7gc1xh7IkuCC+fvWsSPIxM53ov9PSIcavKPZ496 +x0aKAXs2IZB0h0UBVChgHpv9smnm/ylvSJf8xMAkazYF35a6wapsDWORTAvxdvyd +d1zmjysrhIJd +=FiL9 +-----END PGP PUBLIC KEY BLOCK----- + pub AECDB81D38EA9C89 uid Robin Stocker (GitHub Actions) @@ -2063,6 +2091,42 @@ Dku+hCB3xh5oGGk7FagBQj0vvu1g7b6H =q86m -----END PGP PUBLIC KEY BLOCK----- +pub EBA8E97B15086E24 +uid deltazulu + +sub 0BCD135DED52B043 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGNBGBjjp8BDADIsTk3ZUN13dtbqz6LJvRI4QYOEQlpQmYkXW5cAd9kuQLwU7AM +ikDxvzUGVIRfnB02hVgNiqizaLKWzdg85+eYpsFvXnSc9pUSAFolgEnkTuxiwuvc +JOx6kfYjCOmyAJpdjbArikSurC5knsjrCxHlvU4lRoT4YANXseU5EH+DCvRnHSWR +S4Ibkz9z/PuVC1SnnzrmY2gUvnT1F3Ndr2VsP6PVoJyBaKOGyvJh1wjnBHODKv5o +NFAfowrZ6sDuZOjdmrWoA44+u+62Hk0RqCZL/R77LeL32fkXF3PrXzw64wMzHRzR +ckCoUsxziLYIj7+cJdY2jzzjz7C+S/SYN3VWFZP0fPbGxMGoAfWG0Hg0eaT9PnAU +GDFlWEYJW07JQBnbFHQAiCRRv2PWfJKTh8n6BvOq1RaH/j1HTUCa+HB/m3tCgcBa +27r2W2MPjk3iB5JCTFl3PcF4x6ZqMF8iUCVasCnYptbTxQBeTL7ZY/WM3SfoRMRZ +I+ECxp9W+lOVi4cAEQEAAbQgZGVsdGF6dWx1IDxkZWx0YXp1bHVAZ29vZ2xlLmNv +bT65AY0EYGOOnwEMALasfY3MGi6RSX725SXYqqKaQDCqYbRaWKnSsFqNm7xC58Ib +HDgV8vG9VGATTLNj9WfJdPYS9dRTvQ4epVYfsBgtBkB0auNL/aAZ0+yNqtt+nh5b +TTeWricjAbljmtHHzVK1UQZV1ZBYJt0+oH3Zd143qKoKyRjSWLrt87h+wF54IFIJ +p6H+nlqmi2Z/vdR95Qrsh9BuYWMoHZcKcWgn3cOOdcmTvDzCBgSBw7lzV5qcyo8N +0fgpw17xrAqtFF0yGVmlpE+4GjRe34pDUzZstcS0lkj4uaEKalzr60gidv/UDLuu +xT5MkrK+xTpxlrODmsUkSZjm7xB1XXJ7qz/0foNzjbwksywE5Y3DFRh7YOH4jPIs +jgg3m3AlxUEwSG+PAod6lEj0JFyQOybqsLybB1U4j+66jZbxLLUHmHNUSV9ukXaI +zvVdTxxg/mJv0fCkD6+r++bAv0t3YK0t0VZmsKHSyMJp5uROS+mFxXCtUQcs6y5l +PFmLPwcvFVWdc6thEQARAQABiQG8BBgBCgAmFiEEVgU87boX4GXef7l766jpexUI +biQFAmBjjp8CGwwFCQPCZwAACgkQ66jpexUIbiSNywv/V5hK4Z3BnkvbJX8vhGAE +9xv0j3FOfQ1/k7DN2Z2ZjiYBJnDi6NpJLfPLOZBZII7ALyLWYJFm8EQSvwdQiaGq +RIGAaXvvB0NF3HkE7K+Qcifeqd6nb0do6bwbylaOTicxE2Jkx6yX2Lo/xz+MFNNi +/qHJ3wssCj+HVpmZYUEmgXdMav+cSsTjE576mw1LE/mRwAs72JX6lFMX9KTYD98W ++YZ8JBMqlNp7o17U7im62VhV9wF4wsX2FwargdsS11lYFay8uOemaby/ldMRlZlo +xI5Qi3anytqM9IGziB6uD8hJFEelk3nV8dfwvKC3zvBJiVvKrnLXSjXu82GerN7a +rY9Y5I644pxHfZXDpEmOYfWbvd7xzeWFL6HfY5UvFBxSBmBz6KYFNYUWoBl/oZP4 +HEsh53Tu/7Z6OZYhnu2H7S/g89fccGZ5CSf7S1CT9jxbv7gIHTy5NbwiXC4b84DI +3B5ygD/qBV+GV98KiBDXFqq3I+dm+YjuK7lklzNKGLdu +=ceyA +-----END PGP PUBLIC KEY BLOCK----- + pub ECEAC3B11AD0E0A0 uid Priyanka @@ -3657,6 +3721,51 @@ WZQ/Q06arxcc0Nq2GaDkF3ts =hBX9 -----END PGP PUBLIC KEY BLOCK----- +pub 3132F65CE170BB42 +uid SpongePowered (Code Signing) + +sub 9861EF2306DFBAC3 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +0A== +=Zm9Y +-----END PGP PUBLIC KEY BLOCK----- + pub 3595395EB3D8E1BA uid Ralph Goers (CODE SIGNING KEY) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 7f9826141..1537665af 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -26,13 +26,17 @@ + + + + @@ -42,6 +46,7 @@ + @@ -50,13 +55,17 @@ + - + + + + @@ -65,7 +74,9 @@ + + @@ -79,7 +90,13 @@ - + + + + + + + @@ -89,7 +106,9 @@ + + @@ -99,6 +118,7 @@ + @@ -111,9 +131,13 @@ + + + + @@ -124,14 +148,18 @@ + + + + @@ -161,6 +189,7 @@ + @@ -830,12 +859,12 @@ - - - + + + - - + + @@ -1282,12 +1311,12 @@ - - - + + + - - + + @@ -1323,7 +1352,7 @@ - + @@ -1346,7 +1375,7 @@ - + @@ -1869,7 +1898,7 @@ - + @@ -2091,11 +2120,6 @@ - - - - - @@ -2261,12 +2285,12 @@ - - - + + + - - + + @@ -2277,41 +2301,41 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - - + + + - - + + - - - + + + - - + + @@ -2525,14 +2549,6 @@ - - - - - - - - @@ -3416,7 +3432,7 @@ - + @@ -3437,7 +3453,7 @@ - + @@ -3590,18 +3606,13 @@ + + + - - - - - - - - @@ -3805,7 +3816,7 @@ - + diff --git a/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java b/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java index b8c101af7..092b4fbf4 100644 --- a/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java +++ b/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java @@ -9,135 +9,200 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.Arrays; -import java.util.List; - -import org.simpleyaml.configuration.comments.CommentType; -import org.simpleyaml.configuration.file.YamlFile; -import org.simpleyaml.configuration.implementation.SimpleYamlImplementation; -import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions; +import java.util.*; import lombok.extern.slf4j.Slf4j; +/** + * A naive, line-based approach to merging "settings.yml" with "settings.yml.template" while + * preserving exact whitespace, blank lines, and inline comments -- but we only rewrite the file if + * the merged content actually differs. + */ @Slf4j public class ConfigInitializer { public void ensureConfigExists() throws IOException, URISyntaxException { - // Define the path to the external config directory + // 1) If settings file doesn't exist, create from template Path destPath = Paths.get(InstallationPathConfig.getSettingsPath()); - - // Check if the file already exists if (Files.notExists(destPath)) { - // Ensure the destination directory exists Files.createDirectories(destPath.getParent()); - - // Copy the resource from classpath to the external directory try (InputStream in = getClass().getClassLoader().getResourceAsStream("settings.yml.template")) { - if (in != null) { - Files.copy(in, destPath); - } else { + if (in == null) { throw new FileNotFoundException( "Resource file not found: settings.yml.template"); } + Files.copy(in, destPath); } log.info("Created settings file from template"); } else { - - // Define the path to the config settings file + // 2) Merge existing file with the template Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath()); - // Load the template resource - URL settingsTemplateResource = - getClass().getClassLoader().getResource("settings.yml.template"); - if (settingsTemplateResource == null) { + URL templateResource = getClass().getClassLoader().getResource("settings.yml.template"); + if (templateResource == null) { throw new IOException("Resource not found: settings.yml.template"); } - // Create a temporary file to copy the resource content + // Copy template to a temp location so we can read lines Path tempTemplatePath = Files.createTempFile("settings.yml", ".template"); - - try (InputStream in = settingsTemplateResource.openStream()) { + try (InputStream in = templateResource.openStream()) { Files.copy(in, tempTemplatePath, StandardCopyOption.REPLACE_EXISTING); } - final YamlFile settingsTemplateFile = new YamlFile(tempTemplatePath.toFile()); - DumperOptions yamlOptionsSettingsTemplateFile = - ((SimpleYamlImplementation) settingsTemplateFile.getImplementation()) - .getDumperOptions(); - yamlOptionsSettingsTemplateFile.setSplitLines(false); - settingsTemplateFile.loadWithComments(); + // 2a) Read lines from both files + List templateLines = Files.readAllLines(tempTemplatePath); + List mainLines = Files.readAllLines(settingsPath); - final YamlFile settingsFile = new YamlFile(settingsPath.toFile()); - DumperOptions yamlOptionsSettingsFile = - ((SimpleYamlImplementation) settingsFile.getImplementation()) - .getDumperOptions(); - yamlOptionsSettingsFile.setSplitLines(false); - settingsFile.loadWithComments(); + // 2b) Merge lines + List mergedLines = mergeYamlLinesWithTemplate(templateLines, mainLines); - // Load headers and comments - String header = settingsTemplateFile.getHeader(); - - // Create a new file for temporary settings - final YamlFile tempSettingFile = new YamlFile(settingsPath.toFile()); - DumperOptions yamlOptionsTempSettingFile = - ((SimpleYamlImplementation) tempSettingFile.getImplementation()) - .getDumperOptions(); - yamlOptionsTempSettingFile.setSplitLines(false); - tempSettingFile.createNewFile(true); - tempSettingFile.setHeader(header); - - // Get all keys from the template - List keys = - Arrays.asList(settingsTemplateFile.getKeys(true).toArray(new String[0])); - - for (String key : keys) { - if (!key.contains(".")) { - // Add blank lines and comments to specific sections - tempSettingFile - .path(key) - .comment(settingsTemplateFile.getComment(key)) - .blankLine(); - continue; - } - // Copy settings from the template to the settings.yml file - changeConfigItemFromCommentToKeyValue( - settingsTemplateFile, settingsFile, tempSettingFile, key); + // 2c) Only write if there's an actual difference + if (!mergedLines.equals(mainLines)) { + Files.write(settingsPath, mergedLines); + log.info("Settings file updated based on template changes."); + } else { + log.info("No changes detected; settings file left as-is."); } - // Save the settings.yml file - tempSettingFile.save(); + Files.deleteIfExists(tempTemplatePath); } - // Create custom settings file if it doesn't exist + // 3) Ensure custom settings file exists Path customSettingsPath = Paths.get(InstallationPathConfig.getCustomSettingsPath()); if (!Files.exists(customSettingsPath)) { Files.createFile(customSettingsPath); } } - private void changeConfigItemFromCommentToKeyValue( - final YamlFile settingsTemplateFile, - final YamlFile settingsFile, - final YamlFile tempSettingFile, - String path) { - if (settingsFile.get(path) == null && settingsTemplateFile.get(path) != null) { - // If the key is only in the template, add it to the temporary settings with comments - tempSettingFile - .path(path) - .set(settingsTemplateFile.get(path)) - .comment(settingsTemplateFile.getComment(path, CommentType.BLOCK)) - .commentSide(settingsTemplateFile.getComment(path, CommentType.SIDE)); - } else if (settingsFile.get(path) != null && settingsTemplateFile.get(path) != null) { - // If the key is in both, update the temporary settings with the main settings' value - // and comments - tempSettingFile - .path(path) - .set(settingsFile.get(path)) - .comment(settingsTemplateFile.getComment(path, CommentType.BLOCK)) - .commentSide(settingsTemplateFile.getComment(path, CommentType.SIDE)); - } else { - // Log if the key is not found in both YAML files - log.info("Key not found in both YAML files: " + path); + /** + * Merge logic that: - Reads the template lines block-by-block (where a "block" = a key and all + * the lines that belong to it), - If the main file has that key, we keep the main file's block + * (preserving whitespace + inline comments). - Otherwise, we insert the template's block. - We + * also remove keys from main that no longer exist in the template. + * + * @param templateLines lines from settings.yml.template + * @param mainLines lines from the existing settings.yml + * @return merged lines + */ + private List mergeYamlLinesWithTemplate( + List templateLines, List mainLines) { + + // 1) Parse template lines into an ordered map: path -> Block + LinkedHashMap templateBlocks = parseYamlBlocks(templateLines); + + // 2) Parse main lines into a map: path -> Block + LinkedHashMap mainBlocks = parseYamlBlocks(mainLines); + + // 3) Build the final list by iterating template blocks in order + List merged = new ArrayList<>(); + for (Map.Entry entry : templateBlocks.entrySet()) { + String path = entry.getKey(); + Block templateBlock = entry.getValue(); + + if (mainBlocks.containsKey(path)) { + // If main has the same block, prefer main's lines + merged.addAll(mainBlocks.get(path).lines); + } else { + // Otherwise, add the template block + merged.addAll(templateBlock.lines); + } } + + return merged; + } + + /** + * Parse a list of lines into a map of "path -> Block" where "Block" is all lines that belong to + * that key (including subsequent indented lines). Very naive approach that may not work with + * advanced YAML. + */ + private LinkedHashMap parseYamlBlocks(List lines) { + LinkedHashMap blocks = new LinkedHashMap<>(); + + Block currentBlock = null; + String currentPath = null; + + for (String line : lines) { + if (isLikelyKeyLine(line)) { + // Found a new "key: ..." line + if (currentBlock != null && currentPath != null) { + blocks.put(currentPath, currentBlock); + } + currentBlock = new Block(); + currentBlock.lines.add(line); + currentPath = computePathForLine(line); + } else { + // Continuation of current block (comments, blank lines, sub-lines) + if (currentBlock == null) { + // If file starts with comments/blank lines, treat as "header block" with path + // "" + currentBlock = new Block(); + currentPath = ""; + } + currentBlock.lines.add(line); + } + } + + if (currentBlock != null && currentPath != null) { + blocks.put(currentPath, currentBlock); + } + + return blocks; + } + + /** + * Checks if the line is likely "key:" or "key: value", ignoring comments/blank. Skips lines + * starting with "-" or "#". + */ + private boolean isLikelyKeyLine(String line) { + String trimmed = line.trim(); + if (trimmed.isEmpty() || trimmed.startsWith("#") || trimmed.startsWith("-")) { + return false; + } + int colonIdx = trimmed.indexOf(':'); + return (colonIdx > 0); // someKey: + } + + // For a line like "security: ", returns "security" or "security.enableLogin" + // by looking at indentation. Very naive. + private static final Deque pathStack = new ArrayDeque<>(); + private static int currentIndentLevel = 0; + + private String computePathForLine(String line) { + // count leading spaces + int leadingSpaces = 0; + for (char c : line.toCharArray()) { + if (c == ' ') leadingSpaces++; + else break; + } + // assume 2 spaces = 1 indent + int indentLevel = leadingSpaces / 2; + + String trimmed = line.trim(); + int colonIdx = trimmed.indexOf(':'); + String keyName = trimmed.substring(0, colonIdx).trim(); + + // pop stack until we match the new indent level + while (currentIndentLevel >= indentLevel && !pathStack.isEmpty()) { + pathStack.pop(); + currentIndentLevel--; + } + + // push the new key + pathStack.push(keyName); + currentIndentLevel = indentLevel; + + // build path by reversing the stack + String[] arr = pathStack.toArray(new String[0]); + List reversed = Arrays.asList(arr); + Collections.reverse(reversed); + return String.join(".", reversed); + } + + /** + * Simple holder for the lines that comprise a "block" (i.e. a key and its subsequent lines). + */ + private static class Block { + List lines = new ArrayList<>(); } } diff --git a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java index 4a359aff2..fb4cfb9e7 100644 --- a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java @@ -9,15 +9,17 @@ import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; import java.util.Enumeration; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; -import org.simpleyaml.configuration.file.YamlFile; -import org.simpleyaml.configuration.file.YamlFileWrapper; -import org.simpleyaml.configuration.implementation.SimpleYamlImplementation; -import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions; import org.springframework.web.multipart.MultipartFile; import com.fathzer.soft.javaluator.DoubleEvaluator; @@ -346,41 +348,208 @@ public class GeneralUtils { public static void saveKeyToConfig(String id, String key, boolean autoGenerated) throws IOException { - Path path = - Paths.get( - InstallationPathConfig - .getSettingsPath()); // Target the configs/settings.yml - - final YamlFile settingsYml = new YamlFile(path.toFile()); - DumperOptions yamlOptionssettingsYml = - ((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions(); - yamlOptionssettingsYml.setSplitLines(false); - - settingsYml.loadWithComments(); - - YamlFileWrapper writer = settingsYml.path(id).set(key); - if (autoGenerated) { - writer.comment("# Automatically Generated Settings (Do Not Edit Directly)"); - } - settingsYml.save(); + doSaveKeyToConfig(id, (key == null ? "" : key), autoGenerated); } public static void saveKeyToConfig(String id, boolean key, boolean autoGenerated) throws IOException { - Path path = Paths.get(InstallationPathConfig.getSettingsPath()); + doSaveKeyToConfig(id, String.valueOf(key), autoGenerated); + } - final YamlFile settingsYml = new YamlFile(path.toFile()); - DumperOptions yamlOptionssettingsYml = - ((SimpleYamlImplementation) settingsYml.getImplementation()).getDumperOptions(); - yamlOptionssettingsYml.setSplitLines(false); + /*------------------------------------------------------------------------* + * Internal Implementation Details * + *------------------------------------------------------------------------*/ - settingsYml.loadWithComments(); - - YamlFileWrapper writer = settingsYml.path(id).set(key); - if (autoGenerated) { - writer.comment("# Automatically Generated Settings (Do Not Edit Directly)"); + /** + * Actually performs the line-based update for the given path (e.g. "security.csrfDisabled") to + * a new string value (e.g. "true"), possibly marking it as auto-generated. + */ + private static void doSaveKeyToConfig(String fullPath, String newValue, boolean autoGenerated) + throws IOException { + // 1) Load the file (settings.yml) + Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath()); + if (!Files.exists(settingsPath)) { + log.warn("Settings file not found at {}, creating a new empty file...", settingsPath); + Files.createDirectories(settingsPath.getParent()); + Files.createFile(settingsPath); } - settingsYml.save(); + List lines = Files.readAllLines(settingsPath); + + // 2) Build a map of "nestedKeyPath -> lineIndex" by parsing indentation + // Also track each line's indentation so we can preserve it when rewriting. + Map pathToLine = parseNestedYamlKeys(lines); + + // 3) If the path is found, rewrite its line. Else, append at the bottom (no indentation). + boolean changed = false; + if (pathToLine.containsKey(fullPath)) { + // Rewrite existing line + LineInfo info = pathToLine.get(fullPath); + String oldLine = lines.get(info.lineIndex); + String newLine = + rewriteLine(oldLine, info.indentSpaces, fullPath, newValue, autoGenerated); + if (!newLine.equals(oldLine)) { + lines.set(info.lineIndex, newLine); + changed = true; + } + } else { + // Append a new line at the bottom, with zero indentation + String appended = fullPath + ": " + newValue; + if (autoGenerated) { + appended += " # Automatically Generated Settings (Do Not Edit Directly)"; + } + lines.add(appended); + changed = true; + } + + // 4) If changed, write back to file + if (changed) { + Files.write(settingsPath, lines); + log.info( + "Updated '{}' to '{}' (autoGenerated={}) in {}", + fullPath, + newValue, + autoGenerated, + settingsPath); + } else { + log.info("No changes for '{}' (already set to '{}').", fullPath, newValue); + } + } + + /** A small record-like class that holds: - lineIndex - indentSpaces */ + private static class LineInfo { + int lineIndex; + int indentSpaces; + + public LineInfo(int lineIndex, int indentSpaces) { + this.lineIndex = lineIndex; + this.indentSpaces = indentSpaces; + } + } + + /** + * Parse the YAML lines to build a map: "full.nested.key" -> (lineIndex, indentSpaces). We do a + * naive indentation-based path stacking: - 2 spaces = 1 indent level - lines that start with + * fewer or equal indentation pop the stack - lines that look like "key:" or "key: value" cause + * a push + */ + private static Map parseNestedYamlKeys(List lines) { + Map result = new HashMap<>(); + + // We'll maintain a stack of (keyName, indentLevel). + // Each line that looks like "myKey:" or "myKey: value" is a new "child" of the top of the + // stack if indent is deeper. + Deque pathStack = new ArrayDeque<>(); + Deque indentStack = new ArrayDeque<>(); + indentStack.push(-1); // sentinel + + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + String trimmed = line.trim(); + + // skip blank lines, comment lines, or list items + if (trimmed.isEmpty() || trimmed.startsWith("#") || trimmed.startsWith("-")) { + continue; + } + // check if there's a colon + int colonIdx = trimmed.indexOf(':'); + if (colonIdx <= 0) { // must have at least one char before ':' + continue; + } + // parse out key + String keyPart = trimmed.substring(0, colonIdx).trim(); + if (keyPart.isEmpty()) { + continue; + } + + // count leading spaces for indentation + int leadingSpaces = countLeadingSpaces(line); + int indentLevel = leadingSpaces / 2; // assume 2 spaces per level + + // pop from stack until we get to a shallower indentation + while (indentStack.peek() != null && indentStack.peek() >= indentLevel) { + indentStack.pop(); + pathStack.pop(); + } + + // push the new key + pathStack.push(keyPart); + indentStack.push(indentLevel); + + // build the full path + String[] arr = pathStack.toArray(new String[0]); + List reversed = Arrays.asList(arr); + Collections.reverse(reversed); + String fullPath = String.join(".", reversed); + + // store line info + result.put(fullPath, new LineInfo(i, leadingSpaces)); + } + + return result; + } + + /** + * Rewrite a single line to set a new value, preserving indentation and (optionally) the + * existing or auto-generated inline comment. + * + *

For example, oldLine might be: " csrfDisabled: false # set to 'true' to disable CSRF + * protection" newValue = "true" autoGenerated = false + * + *

We'll produce something like: " csrfDisabled: true # set to 'true' to disable CSRF + * protection" + */ + private static String rewriteLine( + String oldLine, int indentSpaces, String path, String newValue, boolean autoGenerated) { + // We'll keep the exact leading indentation (indentSpaces). + // Then "key: newValue". We'll try to preserve any existing inline comment unless + // autoGenerated is true. + + // 1) Extract leading spaces from the old line (just in case they differ from indentSpaces). + int actualLeadingSpaces = countLeadingSpaces(oldLine); + String leading = oldLine.substring(0, actualLeadingSpaces); + + // 2) Remove leading spaces from the rest + String trimmed = oldLine.substring(actualLeadingSpaces); + + // 3) Check for existing comment + int hashIndex = trimmed.indexOf('#'); + String lineWithoutComment = + (hashIndex >= 0) ? trimmed.substring(0, hashIndex).trim() : trimmed.trim(); + String oldComment = (hashIndex >= 0) ? trimmed.substring(hashIndex).trim() : ""; + + // 4) Rebuild "key: newValue" + // The "key" here is everything before ':' in lineWithoutComment + int colonIdx = lineWithoutComment.indexOf(':'); + String existingKey = + (colonIdx >= 0) + ? lineWithoutComment.substring(0, colonIdx).trim() + : path; // fallback if line is malformed + + StringBuilder sb = new StringBuilder(); + sb.append(leading); // restore original leading spaces + + // "key: newValue" + sb.append(existingKey).append(": ").append(newValue); + + // 5) If autoGenerated, add/replace comment + if (autoGenerated) { + sb.append(" # Automatically Generated Settings (Do Not Edit Directly)"); + } else { + // preserve the old comment if it exists + if (!oldComment.isEmpty()) { + sb.append(" ").append(oldComment); + } + } + return sb.toString(); + } + + private static int countLeadingSpaces(String line) { + int count = 0; + for (char c : line.toCharArray()) { + if (c == ' ') count++; + else break; + } + return count; } public static String generateMachineFingerprint() {