Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] 通过 WebUI 编辑配置文件 #513

Closed
wants to merge 8 commits into from
Closed

Conversation

Ghost-chu
Copy link
Collaborator

@Ghost-chu Ghost-chu commented Sep 21, 2024

本 PR 由 ChatGPT 4o 提供技术支持(前端)。

Snipaste_2024-09-22_02-08-13

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a web API for managing configuration files, including endpoints for listing, retrieving, and updating YAML configurations.
    • Added a new tabbed interface for viewing and editing configuration files using a Codemirror editor.
    • Implemented a configuration reload feature that allows for dynamic updates without restarting the application.
    • Enhanced module management with new capabilities for web configuration.
    • Added new settings routes in the application for improved navigation and configuration management.
  • Bug Fixes

    • Enhanced error handling for configuration save operations.
  • Localization

    • Expanded English and Chinese localization support for settings and configuration management messages.
  • Dependencies

    • Integrated CodeMirror support for JSON and YAML editing.

@Ghost-chu Ghost-chu requested review from a team as code owners September 21, 2024 18:15
Copy link
Contributor

coderabbitai bot commented Sep 21, 2024

Walkthrough

The changes introduce enhancements to configuration management within the application, focusing on the ability to reload configurations dynamically. A new public method, reloadConfig, has been added to facilitate this process. Additionally, the PeerBanHelperServer now registers a WebConfigController to manage web configuration functionalities. Localization support has been expanded with new entries for both English and Chinese, enhancing user interface elements related to settings. A new route for configuration management in the web UI has also been established.

Changes

File Path Change Summary
src/main/java/com/ghostchu/peerbanhelper/... - Added reloadConfig method for configuration management.
- Improved formatting in conditional statements and method definitions.
- Updated registerModules to include registration of WebConfigController.
webui/package.json - Added new dependencies for CodeMirror support (JSON, YAML) and type definitions.
webui/src/locale/en-US.ts - Added localization entries for settings and configuration management.
webui/src/locale/zh-CN.ts - Added Chinese localization entries for settings and configuration management.
webui/src/router/index.ts - Added new routes for settings and configuration management, requiring user login.

Possibly related PRs

  • Webui/torrent #531: The changes in PBHPeerController.java and PBHTorrentController.java involve modifications to access control and the introduction of an ActivationManager, which may relate to the configuration reloading functionality in Main.java as both involve managing application state and permissions.

Poem

🐇 In the meadow where configs play,
A rabbit hops, brightening the day.
With new routes and settings in sight,
Reloading joy, everything feels right!
CodeMirror dances, colors so bright,
In this code, we leap with delight! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

cloudflare-workers-and-pages bot commented Sep 21, 2024

Deploying pbh-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: 54835f2
Status:🚫  Build failed.

View logs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Outside diff range and nitpick comments (9)
webui/src/components/codeEditor.vue (1)

28-31: Consider removing the commented-out code.

The event handlers for change and input events have their implementations commented out. If this code is not needed, consider removing it to keep the codebase clean.

Apply this diff to remove the commented-out code:

-const onChange = (val: string, cm: Editor) => {
-  // console.log(val);
-  // console.log(cm.getValue());
-};
+const onChange = (val: string, cm: Editor) => {};

-const onInput = (val: string) => {
-  // console.log(val);
-};
+const onInput = (val: string) => {};

Also applies to: 33-35

webui/package.json (1)

19-19: Consider using a package registry.

The addition of the @codemirror/lang-yaml dependency from the GitHub repository is appropriate for providing YAML language support in CodeMirror, which aligns with the PR objective of editing configuration files through a WebUI.

However, consider using a package registry like npm instead of directly sourcing from the GitHub repository for better stability and versioning. You can specify a version constraint similar to the other dependencies.

src/main/java/com/ghostchu/peerbanhelper/Main.java (1)

395-400: LGTM!

The new reloadConfig method introduces a clean and well-structured functionality for configuration management. The method follows the Single Responsibility Principle and uses the reloadManager to reload the configurations, which is a good practice.

Consider documenting the return type and the expected behavior of the method using Javadoc comments. This will help other developers understand the purpose and usage of the method.

src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/WebConfigController.java (2)

53-53: Adjust permission level for retrieving configuration content

The retrieveConfigContent endpoint currently requires Role.USER_WRITE. Since it's a GET request and only reads data, consider changing the required role to Role.USER_READ.

Apply this diff to adjust the role:

.get("/api/config/{configId}", this::retrieveConfigContent, Role.USER_WRITE)
+       .get("/api/config/{configId}", this::retrieveConfigContent, Role.USER_READ)

26-142: Add unit tests for the new API endpoints

Including unit tests for the newly added endpoints will help ensure they function as expected and make future refactoring safer.

Consider adding tests to cover:

  • Successful retrieval and modification of configurations.
  • Handling of invalid configId values.
  • Error scenarios like invalid YAML content or I/O failures.
webui/src/views/config/index.vue (4)

27-28: Remove unnecessary custom type definition for the translation function

Defining a custom type TFunction for t is unnecessary since vue-i18n provides proper typings. This can simplify your code and reduce redundancy.

Apply this diff to eliminate the custom type:

- import { useI18n } from 'vue-i18n' // 无需导入 UseI18n
- type TFunction = (key: string, values?: Record<string, unknown>) => string
+ import { useI18n } from 'vue-i18n'

120-121: Adjust the returned properties from setup()

Since you're using $t in the template, there's no need to return t from the setup() function. Removing it can clean up your code.

Apply this diff to modify the return statement:

return {
-   t,
    configYamlList,
    yamlContents,
    activeTab,
    cmOptions,
    loadTabContent,
    saveConfig,
    // Other properties...
}

36-38: Specify types using Ref for better type safety

When using ref with TypeScript, it's clearer and safer to specify the types using Ref<T> from vue. This improves code readability and type checking.

Apply this diff to adjust type annotations:

- const configYamlList = ref<string[]>([]) // Holds list of YAML config files
- const yamlContents = ref<Record<string, string>>({}) // Holds content of each YAML file
- const activeTab = ref<string>('') // Current active tab
+ const configYamlList = ref<Array<string>>([]) // Holds list of YAML config files
+ const yamlContents = ref<Record<string, string>>({}) // Holds content of each YAML file
+ const activeTab = ref('') as Ref<string> // Current active tab

Ensure you import Ref from vue if you use it.


129-130: Remove unnecessary console logs in production code

The console.log statements in onReady and onFocus might clutter the console and are generally used for debugging purposes. Consider removing them before deploying.

Apply this diff to remove console logs:

onReady: (configId: string) => (cm: Editor) => {
- console.log(`Codemirror instance ready for ${configId}`)
},

onFocus(cm: Editor, event: FocusEvent) {
- console.log('Codemirror focused', cm, event)
}
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 6c79ab4 and e69bd70.

Files ignored due to path filters (1)
  • webui/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing (16)
  • src/main/java/com/ghostchu/peerbanhelper/Main.java (5 hunks)
  • src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java (1 hunks)
  • src/main/java/com/ghostchu/peerbanhelper/btn/BtnConfig.java (3 hunks)
  • src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/IPBlackRuleList.java (2 hunks)
  • src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/WebConfigController.java (1 hunks)
  • webui/package.json (2 hunks)
  • webui/src/api/model/config.ts (1 hunks)
  • webui/src/components/codeEditor.vue (1 hunks)
  • webui/src/locale/en-US.ts (3 hunks)
  • webui/src/locale/zh-CN.ts (4 hunks)
  • webui/src/main.ts (2 hunks)
  • webui/src/router/index.ts (1 hunks)
  • webui/src/service/config.ts (1 hunks)
  • webui/src/views/config/index.vue (1 hunks)
  • webui/src/views/config/locale/en-US.ts (1 hunks)
  • webui/src/views/config/locale/zh-CN.ts (1 hunks)
Additional comments not posted (31)
webui/src/views/config/locale/zh-CN.ts (1)

1-6: LGTM!

The localized messages for the configuration page in Chinese are clear, concise, and cover the essential scenarios. The use of placeholders for dynamic values is a good practice. This file will improve the user experience for Chinese-speaking users.

webui/src/views/config/locale/en-US.ts (1)

1-7: LGTM!

The localization file follows best practices and provides comprehensive coverage of user-facing messages for the configuration page. The messages are clear, informative, and use placeholders for dynamic values where appropriate.

The addition of this file enables effective localization support for the WebUI configuration feature.

webui/src/api/model/config.ts (2)

1-18: LGTM!

The ConfigSaveResult interface provides a clear and well-documented structure for the configuration save result. The properties are aptly named and their purposes are clearly explained in the comments.


20-29: LGTM!

The Result enum provides a clear set of possible outcomes for the configuration save operation. The enum values are well-defined and cover the necessary scenarios.

webui/src/main.ts (2)

11-11: LGTM!

The import statement for the InstallCodeMirror function from the codemirror-editor-vue3 package is syntactically correct.


26-26: CodeMirror editor integration looks good!

The InstallCodeMirror function is correctly used to register the CodeMirror editor as a plugin in the Vue app. This integration will enable powerful code editing features within the application.

The placement of the plugin registration after other essential plugins like Pinia, i18n, and the router ensures that the CodeMirror editor can seamlessly interact with different parts of the application.

webui/src/components/codeEditor.vue (2)

1-14: LGTM!

The template section is properly structured and uses the correct syntax for rendering the Codemirror editor. The bindings and event handlers are correctly set up.


15-57: The script section is properly structured and set up.

The script section is properly structured and uses the correct syntax for setting up the component logic. The reactive references, editor configuration, and lifecycle hooks are correctly set up.

webui/src/service/config.ts (3)

7-18: LGTM!

The function is implemented correctly and follows best practices for async/await and response handling.


20-31: LGTM!

The function is implemented correctly and follows the same best practices as getConfigYamlList for async/await and response handling. The configId parameter is correctly included in the API endpoint URL.


33-49: LGTM!

The function is implemented correctly and follows the same best practices as the previous functions for async/await and response handling. The configId parameter is correctly included in the API endpoint URL, and the content parameter is correctly sent in the PUT request body.

src/main/java/com/ghostchu/peerbanhelper/btn/BtnConfig.java (2)

5-7: LGTM!

The imports are necessary for implementing the Reloadable interface and using its related classes. The additions are correct and relevant.


19-19: Excellent implementation of the Reloadable interface!

The BtnConfig class now correctly implements the Reloadable interface, allowing it to support dynamic reloading. The reloadModule method is implemented as required by the interface, and it appropriately returns a ReloadResult indicating that a restart is necessary for reloading this module.

Requiring a restart for reloading the BtnConfig module seems justified, as it likely contains critical configuration settings that may not be safe to reload dynamically without a restart.

The changes are well-structured and adhere to the interface contract. Great work!

Also applies to: 44-47

webui/src/locale/zh-CN.ts (3)

13-13: LGTM!

The import statement for configLocale is necessary to support localization of configuration-related features.


33-34: Looks good!

The new entries for router.settings and router.settings.config are correctly added to the exported object. The Chinese translations are appropriate for the given keys.


61-61: LGTM!

The spread operator is correctly used to include the configLocale strings in the final exported object.

webui/package.json (4)

18-18: LGTM!

The addition of the @codemirror/lang-json dependency with version ^6.0.1 is appropriate for providing JSON language support in CodeMirror, which aligns with the PR objective of editing configuration files through a WebUI.


24-24: LGTM!

The addition of the codemirror dependency with version ^5 is appropriate for integrating the core CodeMirror functionality, which aligns with the PR objective of editing configuration files through a WebUI.


25-25: LGTM!

The addition of the codemirror-editor-vue3 dependency with version ^2.8.0 is appropriate for integrating CodeMirror with Vue 3, which aligns with the PR objective of editing configuration files through a WebUI.


48-48: LGTM!

The addition of the @types/codemirror dev dependency with version ^5.60.15 is appropriate for providing TypeScript type definitions for CodeMirror, which ensures type safety during development and aligns with the PR objective of editing configuration files through a WebUI.

webui/src/locale/en-US.ts (3)

13-13: LGTM!

The import statement for configLocale follows the correct syntax and is consistent with the other locale imports in the file.


34-35: LGTM!

The new entries for 'router.settings' and 'router.settings.config' follow the correct syntax and are consistent with the other entries in the exported default object.


58-59: LGTM!

The configLocale and chartsLocale modules are being correctly merged into the exported default object using the spread operator, which is consistent with how the other locale modules are being merged.

webui/src/router/index.ts (1)

169-187: LGTM!

The new /settings route and its child route /config are properly structured and integrated into the existing router configuration. The routes have appropriate access control by requiring login, and the labels are defined for display purposes. The /config route correctly loads the configuration view component.

Overall, the addition of these routes expands the application's functionality by providing a dedicated section for configuration management.

src/main/java/com/ghostchu/peerbanhelper/Main.java (4)

16-17: LGTM!

The new imports are necessary for the reloadConfig method and look good.


107-110: LGTM!

The formatting changes improve the readability of the conditional statements.


169-173: LGTM!

The formatting changes improve the readability of the conditional statements related to the operating system name.


346-349: LGTM!

The formatting changes improve the readability of the conditional statement.

src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/IPBlackRuleList.java (2)

26-26: LGTM!

The import statement for the ReloadStatus class is valid and does not introduce any issues.


112-113: Excellent asynchronous reload implementation!

The changes to the reloadModule method look great:

  • Starting a virtual thread to execute the reloadConfig method asynchronously is a good approach to avoid blocking the main thread during the reload operation.
  • Returning a ReloadResult with a SCHEDULED status accurately communicates that the reload has been initiated but not yet completed.

The asynchronous nature of the reload aligns well with the overall design and improves responsiveness.

src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java (1)

657-657: LGTM!

The registration of the WebConfigController module with the moduleManager is a valid addition. It aligns with the PR objective of introducing web-based configuration management capabilities.

webui/src/components/codeEditor.vue Outdated Show resolved Hide resolved
Comment on lines +51 to +54
javalinWebContainer.javalin()
.get("/api/config", this::listConfig, Role.USER_READ)
.get("/api/config/{configId}", this::retrieveConfigContent, Role.USER_WRITE)
.put("/api/config/{configId}", this::editConfig, Role.USER_WRITE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential security risk: Exposing configuration files via API

Allowing users to retrieve and edit configuration files may expose sensitive information if the configurations contain secrets or credentials. Consider restricting access to these endpoints to administrative roles or sanitizing sensitive data before exposure.

Recommendations:

  • Restrict Access: Limit these endpoints to admin roles.
  • Sanitize Output: Exclude or mask sensitive information in the configuration data.
  • Secure Transmission: Ensure that the API uses HTTPS to protect data in transit.
  • Audit Logging: Implement logging for access and modification attempts for auditing purposes.

Implement logging for configuration access and modifications

To enhance security and traceability, add logging to record when configurations are listed, retrieved, or modified. This aids in monitoring usage and detecting unauthorized activities.

For example:

private static final Logger logger = LoggerFactory.getLogger(WebConfigController.class);

private void editConfig(Context ctx) {
    String user = ctx.sessionAttribute("userId");
    String configId = ctx.pathParam("configId");
    logger.info("User '{}' is modifying configuration '{}'", user, configId);
    // existing code...
}

private void retrieveConfigContent(Context ctx) {
    String user = ctx.sessionAttribute("userId");
    String configId = ctx.pathParam("configId");
    logger.info("User '{}' is retrieving configuration '{}'", user, configId);
    // existing code...
}

private void listConfig(Context ctx) {
    String user = ctx.sessionAttribute("userId");
    logger.info("User '{}' is listing configurations", user);
    // existing code...
}

Ensure sensitive information is not included in the logs.

Also applies to: 57-79, 81-93, 96-98

Comment on lines +57 to +79
private void editConfig(Context ctx) throws IOException {
ConfigSaveRequest configSaveRequest = ctx.bodyAsClass(ConfigSaveRequest.class);
var test = new YamlConfiguration();
try {
test.loadFromString(configSaveRequest.content());
} catch (InvalidConfigurationException e) {
ctx.json(new StdResp(false, "Invalid configuration: " + e.getMessage(), null));
return;
}
switch (ctx.pathParam("configId")) {
case "config.yml" -> {
Files.writeString(Main.getMainConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
}
case "profile.yml" -> {
Files.writeString(Main.getProfileConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
}
default -> {
throw new IllegalArgumentException("Invalid configId");
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to reduce code duplication in handling configuration files

The logic for handling configId in both editConfig and retrieveConfigContent methods is similar. Refactoring this logic into a helper method will improve maintainability.

Implement a helper method to get the configuration file path:

private Path getConfigFilePath(String configId) {
    return switch (configId) {
        case "config.yml" -> Main.getMainConfigFile().toPath();
        case "profile.yml" -> Main.getProfileConfigFile().toPath();
        default -> null;
    };
}

Refactor the methods:

private void editConfig(Context ctx) {
    ConfigSaveRequest configSaveRequest = ctx.bodyAsClass(ConfigSaveRequest.class);
    var test = new YamlConfiguration();
    try {
        test.loadFromString(configSaveRequest.content());
    } catch (InvalidConfigurationException e) {
        ctx.json(new StdResp(false, "Invalid configuration: " + e.getMessage(), null));
        return;
    }
    Path configFilePath = getConfigFilePath(ctx.pathParam("configId"));
    if (configFilePath == null) {
        ctx.status(400).json(new StdResp(false, "Invalid configId", null));
        return;
    }
    try {
        Files.writeString(configFilePath, configSaveRequest.content(), StandardCharsets.UTF_8);
        ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
    } catch (IOException e) {
        ctx.json(new StdResp(false, "Error saving configuration: " + e.getMessage(), null));
    }
}

private void retrieveConfigContent(Context ctx) {
    Path configFilePath = getConfigFilePath(ctx.pathParam("configId"));
    if (configFilePath == null) {
        ctx.status(400).json(new StdResp(false, "Invalid configId", null));
        return;
    }
    try {
        String content = Files.readString(configFilePath, StandardCharsets.UTF_8);
        ctx.json(new StdResp(true, null, content));
    } catch (IOException e) {
        ctx.json(new StdResp(false, "Error reading configuration: " + e.getMessage(), null));
    }
}

Also applies to: 81-93


Handle exceptions and return appropriate error responses

In the editConfig method, exceptions like IOException during file writing are not caught, which may result in unhandled exceptions and internal server errors. It's important to handle these exceptions to provide meaningful feedback to the client.

Apply this diff to handle IOException and return proper error responses:

private void editConfig(Context ctx) {
    ConfigSaveRequest configSaveRequest = ctx.bodyAsClass(ConfigSaveRequest.class);
    var test = new YamlConfiguration();
    try {
        test.loadFromString(configSaveRequest.content());
    } catch (InvalidConfigurationException e) {
        ctx.json(new StdResp(false, "Invalid configuration: " + e.getMessage(), null));
        return;
    }
+   try {
        switch (ctx.pathParam("configId")) {
            case "config.yml" -> {
                Files.writeString(Main.getMainConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
                ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
            }
            case "profile.yml" -> {
                Files.writeString(Main.getProfileConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
                ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
            }
            default -> {
-               throw new IllegalArgumentException("Invalid configId");
+               ctx.status(400).json(new StdResp(false, "Invalid configId", null));
                return;
            }
        }
+   } catch (IOException e) {
+       ctx.json(new StdResp(false, "Error saving configuration: " + e.getMessage(), null));
+   }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void editConfig(Context ctx) throws IOException {
ConfigSaveRequest configSaveRequest = ctx.bodyAsClass(ConfigSaveRequest.class);
var test = new YamlConfiguration();
try {
test.loadFromString(configSaveRequest.content());
} catch (InvalidConfigurationException e) {
ctx.json(new StdResp(false, "Invalid configuration: " + e.getMessage(), null));
return;
}
switch (ctx.pathParam("configId")) {
case "config.yml" -> {
Files.writeString(Main.getMainConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
}
case "profile.yml" -> {
Files.writeString(Main.getProfileConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
}
default -> {
throw new IllegalArgumentException("Invalid configId");
}
}
}
private void editConfig(Context ctx) {
ConfigSaveRequest configSaveRequest = ctx.bodyAsClass(ConfigSaveRequest.class);
var test = new YamlConfiguration();
try {
test.loadFromString(configSaveRequest.content());
} catch (InvalidConfigurationException e) {
ctx.json(new StdResp(false, "Invalid configuration: " + e.getMessage(), null));
return;
}
try {
switch (ctx.pathParam("configId")) {
case "config.yml" -> {
Files.writeString(Main.getMainConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
}
case "profile.yml" -> {
Files.writeString(Main.getProfileConfigFile().toPath(), configSaveRequest.content(), StandardCharsets.UTF_8);
ctx.json(new StdResp(true, "Configuration saved", processConfigReloading()));
}
default -> {
ctx.status(400).json(new StdResp(false, "Invalid configId", null));
return;
}
}
} catch (IOException e) {
ctx.json(new StdResp(false, "Error saving configuration: " + e.getMessage(), null));
}
}

Comment on lines +10 to +12
@change="onChange(configId)"
@ready="onReady(configId)"
@focus="onFocus"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure correct event handling in Codemirror component

The onChange, onReady, and onFocus event handlers might not be correctly bound due to the way they are defined. Ensure that they are properly set up to avoid unexpected behaviors.

Verify that the event handlers are correctly implemented and consider using .bind or arrow functions if necessary.

webui/src/views/config/index.vue Show resolved Hide resolved
webui/src/views/config/index.vue Show resolved Hide resolved
Comment on lines 57 to 65
const loadTabContent = async (key: string | number) => {
const configId = key.toString(); // 确保 configId 是 string 类型
if (!yamlContents.value[configId]) {
const response = await getConfigYamlContent(configId);
if (response.success) {
yamlContents.value[configId] = response.data;
}
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle cases where no configuration files are available

If configYamlList is empty, the component might not behave as expected. Consider adding handling for cases where no configuration files are returned.

Apply this change to handle empty lists:

if (response.success) {
  configYamlList.value = response.data
  if (configYamlList.value.length > 0) {
    activeTab.value = configYamlList.value[0]
    await loadTabContent(activeTab.value) // Load content of the first tab
+ } else {
+   Message.warning({ content: 'No configuration files available.', resetOnHover: true })
  }
}

Committable suggestion was skipped due to low confidence.

webui/src/views/config/index.vue Outdated Show resolved Hide resolved
Comment on lines +74 to +117
}

const results = response.data
let requiresRestart = false

results.forEach((result) => {
if (result.result === Result.Exception) {
Message.error({
content: t('page.config.saveFailed', {
name: result.name,
errorMsg: result.errorMsg
}),
resetOnHover: true
})
return true
}
if (result.result === Result.RequireRestart) requiresRestart = true
})

const allSuccessful = results.every(
(res) => res.result === Result.Success || res.result === Result.Outdated
)

if (allSuccessful) {
Message.success({
content: t('page.config.saveSuccess'),
resetOnHover: true
})
}

if (requiresRestart) {
Message.warning({
content: t('page.config.saveRequireRestart'),
resetOnHover: true
})
}

return true
} catch (error: unknown) {
if (error instanceof Error) {
Message.error({ content: error.message, resetOnHover: true })
}
return true
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error handling in saveConfig function

The saveConfig function currently returns true regardless of success or failure, which can make it difficult to handle errors effectively. Consider returning false or throwing an error when the save operation fails to provide clearer feedback.

Apply this diff to adjust the return values:

if (!response.success) {
  Message.error({ content: response.message, resetOnHover: true })
- return true
+ return false
}

// ...

if (error instanceof Error) {
  Message.error({ content: error.message, resetOnHover: true })
}
- return true
+ return false

Ensure that any functions calling saveConfig handle the returned value appropriately.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}
const results = response.data
let requiresRestart = false
results.forEach((result) => {
if (result.result === Result.Exception) {
Message.error({
content: t('page.config.saveFailed', {
name: result.name,
errorMsg: result.errorMsg
}),
resetOnHover: true
})
return true
}
if (result.result === Result.RequireRestart) requiresRestart = true
})
const allSuccessful = results.every(
(res) => res.result === Result.Success || res.result === Result.Outdated
)
if (allSuccessful) {
Message.success({
content: t('page.config.saveSuccess'),
resetOnHover: true
})
}
if (requiresRestart) {
Message.warning({
content: t('page.config.saveRequireRestart'),
resetOnHover: true
})
}
return true
} catch (error: unknown) {
if (error instanceof Error) {
Message.error({ content: error.message, resetOnHover: true })
}
return true
}
}
const results = response.data
let requiresRestart = false
results.forEach((result) => {
if (result.result === Result.Exception) {
Message.error({
content: t('page.config.saveFailed', {
name: result.name,
errorMsg: result.errorMsg
}),
resetOnHover: true
})
return true
}
if (result.result === Result.RequireRestart) requiresRestart = true
})
const allSuccessful = results.every(
(res) => res.result === Result.Success || res.result === Result.Outdated
)
if (allSuccessful) {
Message.success({
content: t('page.config.saveSuccess'),
resetOnHover: true
})
}
if (requiresRestart) {
Message.warning({
content: t('page.config.saveRequireRestart'),
resetOnHover: true
})
}
return true
} catch (error: unknown) {
if (error instanceof Error) {
Message.error({ content: error.message, resetOnHover: true })
}
return false
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between e69bd70 and e25018c.

Files selected for processing (4)
  • webui/src/locale/en-US.ts (3 hunks)
  • webui/src/locale/zh-CN.ts (3 hunks)
  • webui/src/main.ts (2 hunks)
  • webui/src/views/config/index.vue (1 hunks)
Files skipped from review as they are similar to previous changes (3)
  • webui/src/locale/en-US.ts
  • webui/src/locale/zh-CN.ts
  • webui/src/main.ts
Additional context used
GitHub Check: Lint
webui/src/views/config/index.vue

[warning] 2-2:
Attribute 'v-model:activeKey' must be hyphenated

webui/src/views/config/index.vue Show resolved Hide resolved
Comment on lines +56 to +64
const loadTabContent = async (key: string | number) => {
const configId = key.toString() // 确保 configId 是 string 类型
if (!yamlContents.value[configId]) {
const response = await getConfigYamlContent(configId)
if (response.success) {
yamlContents.value[configId] = response.data
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling when loading YAML content

In the loadTabContent function, if getConfigYamlContent(configId) fails, there is no error handling to inform the user of the failure.

Consider adding error handling to notify the user when the YAML content fails to load.

Apply this diff to handle errors:

if (!yamlContents.value[configId]) {
  const response = await getConfigYamlContent(configId)
  if (response.success) {
    yamlContents.value[configId] = response.data
+ } else {
+   Message.error({ content: response.message || t('page.config.loadContentFailed'), resetOnHover: true })
+   yamlContents.value[configId] = ''
  }
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const loadTabContent = async (key: string | number) => {
const configId = key.toString() // 确保 configId 是 string 类型
if (!yamlContents.value[configId]) {
const response = await getConfigYamlContent(configId)
if (response.success) {
yamlContents.value[configId] = response.data
}
}
}
const loadTabContent = async (key: string | number) => {
const configId = key.toString() // 确保 configId 是 string 类型
if (!yamlContents.value[configId]) {
const response = await getConfigYamlContent(configId)
if (response.success) {
yamlContents.value[configId] = response.data
} else {
Message.error({ content: response.message || t('page.config.loadContentFailed'), resetOnHover: true })
yamlContents.value[configId] = ''
}
}
}

Comment on lines +44 to +53
onMounted(async () => {
const response = await getConfigYamlList()
if (response.success) {
configYamlList.value = response.data
if (configYamlList.value.length > 0) {
activeTab.value = configYamlList.value[0]
await loadTabContent(activeTab.value) // Load content of the first tab
}
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling when retrieving the configuration list

In the onMounted hook, if getConfigYamlList() fails (i.e., response.success is false), there is no error handling to inform the user of the failure.

Consider adding error handling to notify the user when the configuration list fails to load.

Apply this diff to handle errors:

if (response.success) {
  configYamlList.value = response.data
  if (configYamlList.value.length > 0) {
    activeTab.value = configYamlList.value[0]
    await loadTabContent(activeTab.value) // Load content of the first tab
  }
+ } else {
+   Message.error({ content: response.message || t('page.config.loadListFailed'), resetOnHover: true })
}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onMounted(async () => {
const response = await getConfigYamlList()
if (response.success) {
configYamlList.value = response.data
if (configYamlList.value.length > 0) {
activeTab.value = configYamlList.value[0]
await loadTabContent(activeTab.value) // Load content of the first tab
}
}
})
onMounted(async () => {
const response = await getConfigYamlList()
if (response.success) {
configYamlList.value = response.data
if (configYamlList.value.length > 0) {
activeTab.value = configYamlList.value[0]
await loadTabContent(activeTab.value) // Load content of the first tab
}
} else {
Message.error({ content: response.message || t('page.config.loadListFailed'), resetOnHover: true })
}
})

@Gaojianli
Copy link
Member

webui编辑配置请通过表单的方式完成

@Gaojianli Gaojianli added the on-hold Issue/PR 暂时挂起,不要关闭/合并 label Sep 22, 2024
@Gaojianli
Copy link
Member

放弃吧,我是不会接受的

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (6)
webui/src/locale/zh-CN.ts (1)

37-38: LGTM: New localization entries for settings and configuration.

The additions for 'router.settings' and 'router.settings.config' provide necessary localization for the new WebUI configuration editing feature. The spread of configLocale ensures all configuration-related localizations are included.

For consistency with other spread operators in this file, consider moving ...configLocale to be grouped with similar spreads.

Consider applying this change for better grouping:

  ...ruleManageMentLocale,
- ...chartsLocale,
- ...configLocale
+ ...chartsLocale,
+ ...configLocale

Also applies to: 64-64

webui/package.json (2)

19-19: Consider using a specific version for @codemirror/lang-yaml

The addition of YAML language support is beneficial for editing configuration files. However, specifying the package using a GitHub URL may lead to potential instability or unexpected changes in the future.

Consider using a specific version number instead of the GitHub URL if a stable release is available. This would ensure better control over updates and maintain consistency across different environments.


24-24: Consider upgrading to CodeMirror 6 and specifying a more precise version

While adding CodeMirror is appropriate for the web-based configuration editing feature, there are two points to consider:

  1. CodeMirror 5 is an older version. Consider upgrading to CodeMirror 6 for the latest features and improvements, unless there are specific compatibility requirements for version 5.

  2. The current version specification "^5" is quite broad and could lead to unexpected behavior if there are breaking changes within the 5.x.x range.

Consider upgrading to CodeMirror 6 if possible. If you need to stick with version 5, it's recommended to specify a more precise version range, such as "^5.65.0" (replace with the minimum version you need).

webui/src/router/index.ts (1)

196-214: LGTM! Consider adding an icon for consistency.

The new settings route and its child route for configuration are well-structured and consistent with the existing routing pattern. The use of dynamic import for the component is a good practice for code splitting.

For consistency with other top-level routes, consider adding an icon to the settings route metadata. This would enhance the visual consistency in the navigation UI if icons are used.

You could add an icon like this:

import { IconSettings } from '@arco-design/web-vue/es/icon'

// In the route configuration
meta: {
  label: 'router.settings',
  needLogin: true,
  icon: () => h(IconSettings)
}
src/main/java/com/ghostchu/peerbanhelper/Main.java (2)

Line range hint 108-172: LGTM: Improved proxy settings setup.

The changes to the setupProxySettings() method are good improvements:

  1. Using var for proxySection improves readability.
  2. The early return for null proxySection is a good practice.
  3. The updated switch expression syntax is more concise.

Consider extracting the proxy configuration keys (e.g., "host", "port", "non-proxy-hosts") into constants to improve maintainability.


396-401: LGTM: New reloadConfig() method added.

The new reloadConfig() method is a good addition:

  1. It provides a way to dynamically reload configurations.
  2. It ensures proxy settings are updated after reloading.
  3. It leverages the ReloadManager for handling reloads.

Consider adding a log statement at the beginning of the method to indicate that a configuration reload has been initiated. This would be helpful for debugging and monitoring.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between cd51485 and 54835f2.

⛔ Files ignored due to path filters (1)
  • webui/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • src/main/java/com/ghostchu/peerbanhelper/Main.java (5 hunks)
  • src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java (1 hunks)
  • webui/package.json (2 hunks)
  • webui/src/locale/en-US.ts (3 hunks)
  • webui/src/locale/zh-CN.ts (3 hunks)
  • webui/src/router/index.ts (1 hunks)
🧰 Additional context used
🪛 Biome
webui/src/locale/en-US.ts

[error] 10-10: Shouldn't redeclare 'chartsLocale'. Consider to delete it or rename it.

'chartsLocale' is defined here:

(lint/suspicious/noRedeclare)

webui/src/locale/zh-CN.ts

[error] 10-10: Shouldn't redeclare 'chartsLocale'. Consider to delete it or rename it.

'chartsLocale' is defined here:

(lint/suspicious/noRedeclare)

🔇 Additional comments (13)
webui/src/locale/zh-CN.ts (2)

11-11: LGTM: New import for configuration localization.

The import of configLocale is correctly placed and follows the established pattern for locale imports. This addition aligns with the PR objective of implementing WebUI configuration editing.


Line range hint 1-64: Summary: Localization updates for WebUI configuration editing feature.

The changes to this file appropriately support the new WebUI configuration editing feature by adding necessary localizations. The additions are well-placed and consistent with the existing structure.

Please address the duplicate import issue and consider the suggestion for grouping the spread operators. Once these minor adjustments are made, the localization support for the new feature will be complete and well-integrated.

🧰 Tools
🪛 Biome

[error] 10-10: Shouldn't redeclare 'chartsLocale'. Consider to delete it or rename it.

'chartsLocale' is defined here:

(lint/suspicious/noRedeclare)

webui/package.json (4)

18-18: LGTM: Addition of @codemirror/lang-json package

The addition of the "@codemirror/lang-json" package is appropriate for implementing JSON editing functionality in the WebUI. This aligns well with the PR objective of allowing users to edit configuration files through a web interface.


25-25: LGTM: Addition of codemirror-editor-vue3 package

The addition of the "codemirror-editor-vue3" package is appropriate for integrating CodeMirror with Vue 3. This aligns well with the project structure and the goal of implementing a web-based configuration editor.


48-48: LGTM: Addition of @types/codemirror package

The addition of TypeScript type definitions for CodeMirror is a good practice and will improve development experience with better type checking and autocompletion.

Note that these type definitions are for CodeMirror 5.x. If you decide to upgrade to CodeMirror 6 as suggested in a previous comment, make sure to update these type definitions accordingly.


Line range hint 18-48: Summary: Package additions align well with PR objectives

The additions to the package.json file are consistent with the PR objective of implementing a web-based configuration editor. The inclusion of CodeMirror and its related packages (JSON and YAML language support, Vue 3 integration, and TypeScript definitions) provides a solid foundation for creating a rich text editing experience for configuration files.

While the additions are approved, consider the following recommendations:

  1. Evaluate the possibility of upgrading to CodeMirror 6 for the latest features and improvements.
  2. Use more specific version ranges where possible to ensure better stability and predictability.
  3. Ensure that the YAML language support package is using a stable, versioned release if available.

These changes demonstrate a thoughtful approach to implementing the new feature while maintaining good development practices.

webui/src/locale/en-US.ts (3)

11-11: LGTM: New import for configLocale.

The addition of configLocale import is consistent with the PR objectives and follows the existing pattern for importing view-specific locales.


38-39: LGTM: New router entries for settings and config.

The addition of 'router.settings' and 'router.settings.config' entries is consistent with the PR objectives and follows the existing naming convention for router locales. The hierarchy (settings > config) is logical and aligns with typical UI structures.


64-65: LGTM: Addition of configLocale to the export.

The inclusion of configLocale in the spread operator ensures that the new config-related locale strings are properly exported. This follows the existing pattern for other view-specific locales and is consistent with the PR objectives.

src/main/java/com/ghostchu/peerbanhelper/Main.java (3)

16-17: LGTM: New imports added correctly.

The new imports for ReloadResult and ReloadableContainer are appropriately added and will be used in the new reloadConfig() method.


347-349: LGTM: Improved formatting in decapitalize() method.

The formatting changes in the decapitalize() method improve readability without altering the functionality. The logic remains correct for its intended purpose.


Line range hint 1-401: Overall LGTM: Changes align with PR objectives and improve code quality.

The changes in this file successfully implement the functionality required for the WebUI configuration editing feature:

  1. The new reloadConfig() method enables dynamic reloading of configurations, which is essential for the WebUI editing feature.
  2. Improvements in the setupProxySettings() method enhance the overall robustness of the configuration handling.
  3. Code readability and maintainability have been improved through better formatting and use of modern Java features.

These changes align well with the PR objectives of allowing configuration editing through a WebUI interface. The implementation provides a solid foundation for the feature while maintaining good code quality.

src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java (1)

672-672: LGTM: WebConfigController successfully added

The addition of WebConfigController is consistent with the PR objective of allowing users to edit configuration files through a WebUI. Its registration is appropriately placed among other web-related controllers.

@@ -7,6 +7,8 @@ import loginLocale from '@/views/login/locale/zh-CN'
import oobeLocale from '@/views/oobe/locale/zh-CN'
import topBanPageLocale from '@/views/ranks/locale/zh-CN'
import ruleManageMentLocale from '@/views/rule-management/locale/zh-CN'
import chartsLocale from '@/views/charts/locale/zh-CN'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate import of chartsLocale.

This line duplicates the import of chartsLocale from line 3. Duplicate imports are unnecessary and can lead to confusion.

Please remove this line:

-import chartsLocale from '@/views/charts/locale/zh-CN'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import chartsLocale from '@/views/charts/locale/zh-CN'
🧰 Tools
🪛 Biome

[error] 10-10: Shouldn't redeclare 'chartsLocale'. Consider to delete it or rename it.

'chartsLocale' is defined here:

(lint/suspicious/noRedeclare)

@@ -7,6 +7,8 @@ import loginLocale from '@/views/login/locale/en-US'
import oobeLocale from '@/views/oobe/locale/en-US'
import topBanPageLocale from '@/views/ranks/locale/en-US'
import ruleManageMentLocale from '@/views/rule-management/locale/en-US'
import chartsLocale from '@/views/charts/locale/en-US'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate import of chartsLocale.

The chartsLocale is already imported on line 4. This duplicate import on line 10 is unnecessary and should be removed to avoid potential confusion or errors.

Apply this diff to remove the duplicate import:

-import chartsLocale from '@/views/charts/locale/en-US'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import chartsLocale from '@/views/charts/locale/en-US'
🧰 Tools
🪛 Biome

[error] 10-10: Shouldn't redeclare 'chartsLocale'. Consider to delete it or rename it.

'chartsLocale' is defined here:

(lint/suspicious/noRedeclare)

Copy link
Member

@Gaojianli Gaojianli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no editor

@Ghost-chu
Copy link
Collaborator Author

我要提议这个功能作为 GUI 的后备,“直接编辑”

@Gaojianli Gaojianli closed this Oct 18, 2024
@Ghost-chu Ghost-chu deleted the config-editor branch October 20, 2024 19:08
@Ghost-chu Ghost-chu restored the config-editor branch October 20, 2024 19:08
@Ghost-chu Ghost-chu reopened this Oct 20, 2024
@Ghost-chu
Copy link
Collaborator Author

Ghost-chu commented Oct 20, 2024

心不甘情不愿,就算是没有菜单,也请把路由留下,我必须挣扎一下

@Gaojianli Gaojianli closed this Oct 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
on-hold Issue/PR 暂时挂起,不要关闭/合并
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants